完整实战指南:跨主机容器日志聚合、TLS 加密与安全加固)
Dozzle 代理模式Agent完整实战指南跨主机容器日志聚合、TLS 加密与安全加固【免费下载链接】dozzleRealtime log viewer for containers. Supports Docker, Swarm and K8s.项目地址: https://gitcode.com/GitHub_Trending/do/dozzle导读Dozzle 的代理模式Agent Mode允许你在远程 Docker 主机上部署一个轻量的 Dozzle 代理再由本地的主实例通过 TLS 加密连接聚合查看所有代理主机的容器日志。本文基于 docs/zh/guide/agent.md 展开结合仓库源码深入讲解代理的创建、连接、主机分组、健康检查、过滤器与自定义证书等完整配置链路。读完本文你将能够在一台机器上集中监控分布于多台 Docker 主机上的全部容器并正确加固代理的访问边界。代理模式仅适用于Docker。如果你在使用 Docker Swarm则不需要代理——Dozzle 会自动发现自身并组建集群详见 Swarm 模式。一、代理模式的架构与原理代理模式把查看容器日志这件事拆成了两层代理Agent部署在远程 Docker 主机上持有该主机的/var/run/docker.sock主实例Hub/Server运行在你的本地机器上只与代理通信不直接接触远程的 Docker 守护进程。从源码结构看代理与主实例之间通过gRPC TLSmTLS 双向认证通信internal/agent/server.go 中的NewServer创建 gRPC 服务端时使用了tls.RequireAndVerifyClientCert即强制要求客户端出示证书两端必须持有同一套证书对才能建立连接服务端提供了完整的容器操作 RPC 接口StreamLogs、LogsBetweenDates、StreamRawBytes、StreamEvents、StreamStats、ListContainers、HostInfo、ContainerAction、ContainerExec、ContainerAttach、UpdateContainer、CheckImageUpdate等覆盖了日志流、历史日志、统计指标、容器动作与终端接入等全部能力客户端一侧在 internal/agent/client.go 中配置了 gRPC 传输层默认开启 gzip 压缩grpc.UseCompressor(gzip.Name)、单条消息上限 10 MBMaxCallRecvMsgSize并通过 keepalive 参数每 30 秒探测、10 秒超时实现断线自动重连。换句话说代理把读取 Docker socket这件事留在了远程主机本地完成通过网络传输的只是加密后的日志与指令数据。安全边界理解内置证书的信任模型文档开篇给出了一个必须重视的安全警告Dozzle 自带的证书在每一份镜像副本里都是同一个它只能加密连接并不能证明对端是谁。任何能访问7007端口的人都可以用自己的 Dozzle 连上你的代理读取该主机上的全部日志并在其容器里执行命令。代理并不检查DOZZLE_ENABLE_SHELL或DOZZLE_ENABLE_ACTIONS这两个开关只决定界面上显示什么。这一点在源码中得到印证默认证书以 embed 形式编译进二进制internal/support/cli/certs.go 中的ReadCertificates会先尝试加载用户提供的自定义证书DOZZLE_CERT/DOZZLE_KEY若不存在则回退到内嵌的shared_cert.pem/shared_key.pem。由于镜像公开任何人都能提取出这份内嵌证书因此它只提供加密不提供身份认证。由此得出两条硬性实践把7007端口留在私有网络里不要暴露到公网如果代理可能被不可控的网络访问到务必生成你自己的证书对让代理只接受持有同一套凭据的主实例。二、创建代理agent子命令2.1 基本部署代理就是运行在agent子命令下的 Dozzle 进程只需挂载 Docker socket 并映射7007端口docker run -v /var/run/docker.sock:/var/run/docker.sock -p 7007:7007 amir20/dozzle:latest agent# docker-compose.yml services: dozzle-agent: image: amir20/dozzle:latest command: agent volumes: - /var/run/docker.sock:/var/run/docker.sock:ro ports: - 7007:7007代理启动后会监听7007端口默认监听地址定义在 internal/support/cli/args.go 的AgentCmd中可通过--agent-addr或环境变量DOZZLE_AGENT_ADDR修改并只显示它所在主机上的容器。[!TIP] 使用 Docker 网络时不必暴露 7007 端口 同一 Docker 网络中的其他容器可以直接访问代理。这是最安全的运行方式——网络之外的任何东西都无法触达它。2.2 socket 代理Docker Socket Proxy用户请注意如果你之前用 socket proxy 保护 Docker socket请注意不能在代理之上再加一层 socket proxy。Dozzle 代理是用来替代socket proxy 的。关于如何用 socket proxy 代替代理请参阅 远程主机。2.3 代理启动的底层过程从 internal/support/cli/agent_command.go 可以看到代理启动的关键流程创建本地 Docker 客户端docker.NewLocalClient并通过container.NewHostIDResolver(args.HostID)解析主机 ID读取证书自定义优先回退内嵌共享证书监听 TCP 端口并把监听地址写入/tmp/dozzle-agent.addr供健康检查使用创建共享的ClientService单一ContainerStore供 gRPC 服务与通知系统共同使用加载通知配置与云配置./data/notifications.yml、./data/cloud.yml启动 gRPC 服务。三、连接代理主实例配置在主实例一侧通过--remote-agent参数或DOZZLE_REMOTE_AGENT环境变量指定代理地址docker run -p 8080:8080 amir20/dozzle:latest --remote-agent agent:7007# docker-compose.yml services: dozzle: image: amir20/dozzle:latest environment: - DOZZLE_REMOTE_AGENTagent:7007 ports: - 8080:8080 # Dozzle UI port注意连接代理时不必挂载本地的 Docker socket界面上只会显示各个代理上的容器如果你想同时看到本机容器按 快速开始 中的示例挂载docker.sock即可可以用逗号分隔提供多个代理DOZZLE_REMOTE_AGENTagent1:7007,agent2:7007。多个--remote-agent参数也是支持的args.go 中RemoteAgent []string使用separate标记可重复传入。连接后的协议细节连接建立后主实例会把代理当作一个普通的container.Host接入Type: agent见 internal/agent/client.go 的Host方法日志流、统计流、容器事件流都通过 gRPC 的双向流通道传输。得益于客户端的 keepalive 配置代理短暂不可用时主实例会自动重连无需手动干预。附带能力agent-test子命令仓库还提供了一个方便的连通性测试子命令agent-test见 internal/support/cli/agent_test_command.go可以快速验证主实例能否与代理握手并获取主机信息dozzle agent-test agent:7007成功时会打印代理的版本、名称与主机 ID适合部署后做一次冒烟测试。四、主机分组endpoint|name|group语法当管理分布在不同环境中的大量代理时可以为每个代理指定分组名称。分组在侧边栏中显示为可折叠区块每个分组都有合并全部按钮用于查看该分组内所有主机的合并日志。连接字符串格式为endpoint|name|group三个部分都是可选的endpoint必填格式结果agent:7007不覆盖名称无分组agent:7007\|web-1覆盖名称无分组agent:7007\|web-1\|Production覆盖名称 分组agent:7007\|\|Production默认主机名 分组docker run -p 8080:8080 amir20/dozzle:latest \ --remote-agent agent1:7007|web-1|Production \ --remote-agent agent2:7007|web-2|Production \ --remote-agent agent3:7007|dev-1|Development# docker-compose.yml services: dozzle: image: amir20/dozzle:latest environment: - DOZZLE_REMOTE_AGENTagent1:7007|web-1|Production,agent2:7007|web-2|Production,agent3:7007|dev-1|Development ports: - 8080:8080侧边栏会显示为▾ Production web-1 web-2 ▾ Development dev-1 ungrouped-host ← agents without a group appear below点击分组名称旁边的合并图标会打开一个从该分组所有主机汇总的日志视图合并视图也可以直接通过/host-group/group-name访问。没有分组的代理行为和以前完全一样显示在分组区块的下方。在源码层面endpoint|name|group的解析实现在 internal/agent/client.go 的ParseEndpoint中按|分割超过三段或地址为空时报错第二段作为名称覆盖第三段作为分组。解析出的名称覆盖会体现在Host()返回的主机名上分组则写入Host.Group由前端 SideMenu.vue 按组渲染折叠区块与合并入口。五、自定义证书让代理只信任你的主实例5.1 为什么要自定义证书Dozzle 自带一份自签名证书两端互相出示的是同一份且每一端只信任这一份。它被编译进二进制每个 Dozzle 安装里都完全相同任何人都能从公开镜像里提取。因此默认证书提供的是加密而非身份认证——代理分不清你的主实例和别人的主实例。对于默认配置启动的代理唯一把陌生人挡在外面的就是他们访问不到7007端口。[!WARNING] 什么时候需要自己的证书 只要7007端口能被你无法控制的东西访问到包括把它发布在有公网 IP 的主机上就请生成你自己的证书对让每个部署都拥有一份别人没有的凭据。5.2 用generate-certs生成证书对运行generate-certs生成一份独有的证书对docker run --rm -v $PWD:/out amir20/dozzle:latest generate-certs --cert-out /out/dozzle_cert.pem --key-out /out/dozzle_key.pem从 internal/support/cli/generate_certs_command.go 可以看到命令基于 Ed25519 生成密钥生成的证书有效期5 年同时具备 ServerAuth、ClientAuth 与 CA 签名能力因为两端既要互相出示证书又要互相作为 CA 验证对端默认输出文件名为dozzle_cert.pem/dozzle_key.pem文件已存在时会报错可用--force覆盖。生成后命令还会打印复制到主实例与每个代理的后续指引。5.3 部署证书到主实例与代理把两个文件都复制到主实例和每个代理上。Dozzle 默认在/dozzle_cert.pem和/dozzle_key.pem查找挂载到这两个路径即可# docker-compose.yml使用 Docker secrets 提供证书 services: agent: image: amir20/dozzle:latest command: agent volumes: - /var/run/docker.sock:/var/run/docker.sock secrets: - source: cert target: /dozzle_cert.pem - source: key target: /dozzle_key.pem ports: - 7007:7007 secrets: cert: file: ./cert.pem key: file: ./key.pem也可以用环境变量DOZZLE_CERT/DOZZLE_KEY指定自定义路径services: agent: image: amir20/dozzle:latest command: agent environment: - DOZZLE_CERT/certs/my-cert.pem - DOZZLE_KEY/certs/my-key.pem volumes: - /var/run/docker.sock:/var/run/docker.sock - ./certs:/certs ports: - 7007:7007或者使用命令行参数--cert/--keydocker run -v /var/run/docker.sock:/var/run/docker.sock -v ./certs:/certs -p 7007:7007 amir20/dozzle:latest agent --cert /certs/my-cert.pem --key /certs/my-key.pem# docker-compose.yml services: agent: image: amir20/dozzle:latest command: agent --cert /certs/my-cert.pem --key /certs/my-key.pem volumes: - /var/run/docker.sock:/var/run/docker.sock - ./certs:/certs ports: - 7007:7007[!TIP] 推荐使用 Docker secrets 提供证书 可以用docker secret create命令创建也可以像上面的例子那样在docker-compose.yml中定义。连接该代理的 Dozzle 实例必须使用同一套证书。5.4 安全提示像对待密码一样对待私钥拿到它的人都能连上你的代理代理会拒绝出示其他证书的主实例所以主实例和它的代理必须拿到同一份证书对并且一起重启替换证书后主代理两端需要同步更新如果更想用 openssl 而不是generate-certs生成$ openssl genpkey -algorithm Ed25519 -out key.pem $ openssl req -new -key key.pem -out request.csr -subj /CUS/STCalifornia/LSan Francisco/OMy Company $ openssl x509 -req -in request.csr -signkey key.pem -out cert.pem -days 365六、常见问题重复主机 ID如果看到日志An agent with an existing ID was found. Removing the duplicate host.说明有两台主机使用了相同的 Server ID。Dozzle 通过 Docker API 收集主机信息每个代理都需要一个在重启后保持不变的唯一主机 ID以便正确识别。目前代理使用 Docker 的system ID或node ID来标识主机主机 ID 的派生与覆盖逻辑可参见 internal/container/host_id.go 与 internal/support/cli/args.go 中的--host-id/DOZZLE_HOST_ID参数。如果运行在 Swarm 环境中则使用node ID如果发现并非所有主机都可见可能是因为存在使用相同主机 ID 的重复主机。解决方法删除系统上的/var/lib/docker/engine-id并重启 Docker以消除重复主机 ID 引起的冲突。更多排查建议请参阅 FAQ。七、高级配置7.1 配置健康检查可以为代理配置健康检查与主实例的健康检查类似。在代理模式下健康检查检测的是代理与 Docker 的连接——如果 Docker 不可达代理会被标记为不健康并且不会显示在界面上。使用healthcheck子命令配置# docker-compose.yml services: dozzle-agent: image: amir20/dozzle:latest command: agent healthcheck: test: [CMD, /dozzle, healthcheck] interval: 5s retries: 5 start_period: 5s start_interval: 5s volumes: - /var/run/docker.sock:/var/run/docker.sock:ro ports: - 7007:7007实现上internal/support/cli/health_command.go 的HealthcheckCmd会先读取代理启动时写入的/tmp/dozzle-agent.addr如果存在说明当前进程是代理模式就通过 internal/healthcheck/rpc.go 发起一次 RPC 请求验证代理与 Docker 的连通性否则退化为对主实例的 HTTP 健康检查。7.2 修改代理名称与 Dozzle 实例一样可通过DOZZLE_HOSTNAME环境变量修改代理名称docker run -v /var/run/docker.sock:/var/run/docker.sock -p 7007:7007 amir20/dozzle:latest agent --hostname my-special-name# docker-compose.yml services: dozzle-agent: image: amir20/dozzle:latest command: agent environment: - DOZZLE_HOSTNAMEmy-special-name volumes: - /var/run/docker.sock:/var/run/docker.sock:ro ports: - 7007:7007这会把代理名称改为my-special-name连接该代理时界面上会显示这个名称。需要注意它与第三节的name覆盖连接字符串第二段的优先级当主实例在连接字符串中显式指定了名称时会覆盖代理自身的 hostname见 internal/agent/client.go 的Host方法。7.3 配置过滤器可以为代理配置过滤器限制它能访问的容器。这些过滤器会直接传给 Docker从而限制 Dozzle 能看到的内容# docker-compose.yml services: dozzle-agent: image: amir20/dozzle:latest command: agent environment: - DOZZLE_FILTERlabelcolor volumes: - /var/run/docker.sock:/var/run/docker.sock:ro这会让该代理只显示带有color标签的容器。注意这些过滤器会和界面上的过滤器叠加进一步缩小容器范围。想了解各类过滤器的完整语法请阅读 过滤器文档。在源码中DOZZLE_FILTER经 internal/support/cli/args.go 解析为keyvalue形式的 map支持多个也支持--filter参数并作为 Docker 客户端查询条件传入ListContainers等调用。八、代理 vs 远程连接如何选型代理和远程连接DOZZLE_REMOTE_HOST很相似但代理有一些优势。出于性能和安全方面的考虑通常更推荐使用代理。对比如下特性代理远程连接性能负载分散表现更好界面端表现更差安全性加密传输可自带证书不加密或使用 Docker TLS易用性开箱即用需要暴露 Docker socket权限对 Docker 的完全访问可以通过 socket proxy 控制重连自动重连需要重启界面健康检查内置健康检查没有健康检查过滤器支持过滤器不支持过滤器如果你确实打算使用远程连接请务必用 Docker TLS 或反向代理来保护连接相关配置同样参见 远程主机。总结代理模式是 Dozzle 跨主机监控的核心方案以agent子命令在远程 Docker 主机上启动加密的 gRPC 服务主实例通过DOZZLE_REMOTE_AGENT接入配合endpoint|name|group语法实现大规模主机的分组管理。部署时请始终牢记安全边界——内置证书只能加密不能认证7007端口必须留在私有网络必要时用generate-certs生成独有证书对在此基础上再叠加健康检查、名称覆盖与 Docker 侧过滤器即可构建一套安全、可维护的多主机日志聚合平台。【免费下载链接】dozzleRealtime log viewer for containers. Supports Docker, Swarm and K8s.项目地址: https://gitcode.com/GitHub_Trending/do/dozzle创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考