Dozzle 容器分组机制:默认分组、Swarm 服务分组与 dev.dozzle.group 自定义标签
【免费下载链接】dozzleRealtime log viewer for containers. Supports Docker, Swarm and K8s.项目地址: https://gitcode.com/GitHub_Trending/do/dozzle
Dozzle 会自动按 Stack 名称或 Swarm 服务名把容器归组,你也可以通过dev.dozzle.group标签创建自己的分组。本文以 Dozzle 官方文档的"容器分组"为主题,完整覆盖默认分组规则与自定义标签用法,并结合仓库源码深入讲解分组标签的解析优先级、Swarm 服务级标签合并机制,以及分组日志流的 API 实现链路。
一、分组的基本原理
Dozzle 的分组逻辑核心就是一条:容器上的 label 决定了它属于哪个分组。官方文档 docs/guide/container-groups.md 给出的规则有两条:
- 默认分组:在主机(host)模式下,容器默认按其 stack 名称分组;如果容器带有
com.docker.swarm.service.name标签,Dozzle 会自动进入"Swarm 模式",把所有服务名相同的容器合并到同一组; - 自定义分组:给容器打上
dev.dozzle.group=<组名>标签,所有同组名的容器会在界面中合并显示。例如组名为myapp时,所有带dev.dozzle.group=myapp标签的容器会被归到一组。
从源码结构看,分组结果最终体现在 internal/container/types.go 中Container结构体的Group字段上——这是后端与前端共享的契约,也是后面所有分组行为(列表归组、分组日志流、分组页面)的数据源头。
二、默认分组:Stack、Compose 与 Swarm 三种场景
2.1 主机模式:按 stack / compose 项目名分组
在普通 Docker 主机模式下,分组依据来自两个由 Docker 自动写入的标签:com.docker.stack.namespace(docker stack deploy部署时写入)和com.docker.compose.project(docker compose up时写入)。这一机制在 docs/guide/swarm-mode.md 中也有明确说明:
com.docker.stack.namespaceandcom.docker.compose.projectlabels are used for grouping containers. For services, Dozzle uses the service name as the group name which iscom.docker.swarm.service.name.
前端模型 assets/models/Container.ts 中的namespace计算属性完整呈现了这条标签读取链(含自定义分组在内的完整回退顺序):
get namespace() { return ( this.labels["dev.dozzle.group"] || this.labels["coolify.projectName"] || this.labels["com.docker.stack.namespace"] || this.labels["com.docker.compose.project"] ); }即:dev.dozzle.group优先,其后依次回退到coolify.projectName、com.docker.stack.namespace、com.docker.compose.project。测试用例 assets/models/Container.spec.ts 用一组断言锁定了这个回退顺序(falls back through coolify, stack, then compose)。
2.2 Swarm 模式:按服务名合并
当容器带有com.docker.swarm.service.name标签时,意味着它由某个 Swarm service 调度。此时 Dozzle 将该标签的值作为分组名,把同一 service 的所有 task 容器合并展示——这正是文档中所说的"自动启用 Swarm 模式"。前端 assets/stores/swarm.ts 通过遍历容器标签中的com.docker.swarm.service.name来构建服务列表;而分组日志流的订阅则直接按标签过滤(见第四节useServiceStream)。
三、自定义分组:dev.dozzle.group 标签
3.1 用法示例
给容器添加dev.dozzle.group标签即可创建自定义分组,标签值就是组名。以下示例来自官方文档,可直接复制使用。
使用 Docker CLI:
docker run --label dev.dozzle.group=myapp hello-world使用 Docker Compose(docker-compose.yml):
services: dozzle: image: hello-world labels: - dev.dozzle.group=myapp打上该标签后,所有dev.dozzle.group=myapp的容器会在 Dozzle 界面中合并为一个分组入口,点击后即可查看整组的聚合日志。
3.2 后端如何解析分组标签
后端在把 Docker API 返回的原始容器数据转换为Container对象时解析分组,逻辑位于 internal/docker/client.go:
group := "" if c.Labels["dev.dozzle.group"] != "" { group = c.Labels["dev.dozzle.group"] } else if c.Labels["coolify.projectName"] != "" { group = c.Labels["coolify.projectName"] }两个关键事实:
- 优先级:
dev.dozzle.group优先于coolify.projectName(后者是对 Coolify 平台的适配,作为无 dozzle 标签时的兜底); - 两条构建路径共用同一套规则:
newContainer(来自 list summary,internal/docker/client.go#L610-L654)与newContainerFromJSON(来自 inspect,internal/docker/client.go#L666-L671)都执行相同的dev.dozzle.group→coolify.projectName回退逻辑,保证增量推送与完整检查两条数据路径的分组结果一致。
这一优先级由单元测试 internal/docker/client_test.go 中的Test_newContainer_labelPriority明确锁定,其中dozzle labels take priority用例验证了当dev.dozzle.group与coolify.projectName同时存在时,取 dozzle 标签的值;docker name as final fallback用例则验证了无任何分组标签时Group为空字符串(容器不入组)。
3.3 命名与分组标签的配套
dev.dozzle.group常与同族标签dev.dozzle.name(自定义显示名)配合使用,两者在同一优先级体系中并列处理(dev.dozzle.name→coolify.serviceName→ 容器名)。相关标签的完整说明可参考 docs/guide/container-names.md 与 docs/guide/container-links.md。
四、Swarm 下的进阶:服务级 deploy.labels 的合并
这是文档未展开、但实际使用中非常关键的细节。Swarm 会把 compose 文件中deploy.labels写下的标签打在service上,而不是打在 task 容器上——直接 inspect 容器根本看不到这些标签。Dozzle 的解决方案在 internal/docker/service_labels.go:
serviceLabelCache以30 秒 TTL(serviceLabelTTL)缓存一次ServiceList调用结果,把"每个容器一次 API 调用"摊薄为"每次刷新一次";缓存刷新失败时会保留旧值,避免瞬时错误导致标签在界面上消失;mergeServiceLabels通过com.docker.swarm.service.id标签把 service 标签合并到其 task 容器上,容器自身的标签更具体、永远优先(maps.Copy(merged, serviceLabels)先铺底,maps.Copy(merged, c.Labels)再覆盖);- 合并后重新推导
Name与Group(internal/docker/service_labels.go#L97-L108),沿用与newContainer相同的优先级:dev.dozzle.group优先于coolify.projectName; - 该合并仅在 manager 节点(
Swarm.ControlAvailable为真)执行,worker 节点上的 agent 会跳过,其容器仅保留自身标签。
由此得到一个实用结论:在 Swarm 环境下,dev.dozzle.group既可以写在docker service create --label上,也可以写在 compose 的deploy.labels中,后者由 service 继承合并而来。这一点由 internal/docker/service_labels_test.go 中Test_mergeServiceLabels_redoes_name_and_group用例验证:service 上带dev.dozzle.group=cloud时,task 容器的Group会被重算为cloud。
五、分组日志流的 API 链路
分组不只是列表上的视觉归组,更重要的是"整组日志合并查看"。其实现链路贯穿前后端:
- 路由层:分组日志流端点注册在 internal/web/routes.go——
r.Get("/groups/{group}/logs/stream", h.streamGroupedLogs); - 前端订阅:assets/composable/logs/eventStreams.ts 中
useGroupedStream通过EventSource连接/api/groups/{group}/logs/stream,服务端将组内所有容器的日志按时间交错排序后以 SSE 推给浏览器; - 同类端点:stack 分组走标签过滤
/api/labels/com.docker.stack.namespace:{name}/logs/stream(useStackStream),Swarm 服务走/api/labels/com.docker.swarm.service.name:{name}/logs/stream(useServiceStream),多容器任意合并走/api/hosts/{host}/logs/mergedStream/{ids}(useMergedStream)——四种入口最终复用同一套带搜索过滤、级别过滤、断线重连的useLogStream底座; - 页面入口:分组详情页为 assets/pages/group/[name].vue,路由形如
/group/{组名},与dev.dozzle.group的标签值直接对应。
六、适用前提与小结
- 分组标签对 Docker、Swarm 与 Kubernetes 场景通用,其中 Swarm 场景依赖 service 标签合并,worker 节点上的远程 agent 容器只有自身标签可参与分组;
dev.dozzle.group的优先级高于coolify.projectName,且都高于com.docker.stack.namespace/com.docker.compose.project的自动回退(该回退链定义在前端 assets/models/Container.ts);- 一个容器只能归属一个分组(
Group为单值字符串),需要更细的合并视图时可用多容器 merged stream 或按标签过滤。
小结:Dozzle 的分组体系 = "自动标签(stack/compose/swarm service)+ 自定义dev.dozzle.group标签"。给容器打一个标签就能完成归组,而分组的解析优先级、Swarm service 标签的 30 秒缓存合并、以及/api/groups/{group}/logs/stream日志流端点,共同构成了这一机制从容器标签到合并日志界面的完整实现链路。
【免费下载链接】dozzleRealtime log viewer for containers. Supports Docker, Swarm and K8s.项目地址: https://gitcode.com/GitHub_Trending/do/dozzle
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考