Dozzle 内置健康检查:dozzle healthcheck子命令的启用、原理与退出码详解
【免费下载链接】dozzleRealtime log viewer for containers. Supports Docker, Swarm and K8s.项目地址: https://gitcode.com/GitHub_Trending/do/dozzle
Dozzle 自带dozzle healthcheck子命令,用于为容器日志查看器本身提供 Docker 原生健康检查能力。本文以官方 Healthcheck 指南 为主线,结合仓库源码(internal/support/cli/health_command.go、internal/web/healthcheck.go等)讲解如何在 docker-compose 中启用、服务端与 Agent 两种模式各自检查什么、退出码含义,以及--addr/--base自定义端口与路径的处理逻辑。读完本文,你可以直接为自己的 Dozzle 部署配置一套可落地的健康检查,并理解其底层实现原理。
为什么要为 Dozzle 启用健康检查
Dozzle 是运行在 Docker、Swarm、K8s 等容器环境中的实时日志查看器。当容器编排平台(如 Swarm 的deploy.healthcheck、K8s 的 livenessProbe、或第三方监控工具)需要判断 Dozzle 是否存活时,就需要一个可靠的探活手段。
Dozzle 在镜像中内置了dozzle healthcheck子命令。值得注意的是,该命令默认并未写入镜像——原因在官方文档中明确说明:它会给容器增加少量 CPU 开销(docs/guide/healthcheck.md原文:"It is not wired into the image by default because it adds a small amount of CPU overhead")。因此需要用户在 compose 文件中显式启用。
从源码看,/dozzle正是二进制入口。Dockerfile 中ENTRYPOINT ["/dozzle"](Dockerfile),而 CLI 子命令在 internal/support/cli/args.go 中注册:
Healthcheck *HealthcheckCmd `arg:"subcommand:healthcheck" help:"checks if the server is running"`即容器启动参数/dozzle healthcheck会解析到HealthcheckCmd并执行健康检查逻辑。
在 docker-compose 中启用 healthcheck
官方文档给出的完整 compose 配置如下(原样继承,可直接复制使用):
services: dozzle: image: amir20/dozzle:latest volumes: - /var/run/docker.sock:/var/run/docker.sock ports: - 8080:8080 healthcheck: test: ["CMD", "/dozzle", "healthcheck"] interval: 3s timeout: 30s retries: 5 start_period: 30s各字段含义(Docker Compose 标准语义):
| 字段 | 值 | 说明 |
|---|---|---|
test | ["CMD", "/dozzle", "healthcheck"] | 在容器内执行/dozzle healthcheck命令,以其退出码判定健康状态 |
interval | 3s | 两次健康检查之间的间隔 |
timeout | 30s | 单次检查允许的最大执行时间,超时视为失败 |
retries | 5 | 连续失败达到该次数后标记容器为 unhealthy |
start_period | 30s | 容器启动后的宽限期,期间失败不计入 retries,便于 Dozzle 完成启动 |
提示:若你的 Dozzle 通过环境变量自定义了监听地址或基础路径(见下文
--addr/--base),此 healthcheck 仍无需额外配置即可正常工作。
服务端模式下检查什么:/healthcheck端点
当以服务端模式运行时(即常规的 Dozzle 主进程,未配置 agent),dozzle healthcheck会向自身发送一个 HTTPGET请求到/healthcheck端点。该端点路由在 internal/web/routes.go 中注册:
r.Get("/healthcheck", h.healthcheck)端点实现位于 internal/web/healthcheck.go,其判定逻辑与官方文档描述完全一致:
- 获取所有本地Docker 客户端(
h.hostService.LocalClients()); - 若本地客户端为空(
len(clients) == 0):- 只要存在已知的远程 agent 主机(
len(h.hostService.Hosts()) > 0),返回200 OK; - 否则返回
500 Internal Server Error;
- 只要存在已知的远程 agent 主机(
- 若存在本地客户端,则并发对每个客户端执行
client.Ping(ctx),每个客户端的 ping 超时上限为 3 秒(context.WithTimeout(r.Context(), 3*time.Second)); - 只要至少一个本地客户端 ping 成功,就返回
200 OK;全部失败则返回500。
对应官方文档的结论可以严格对上:
200 OK—— 至少一个本地 Docker 客户端正常响应,或本地客户端为空但已知至少一个远程 agent 主机;500 Internal Server Error—— 所有本地客户端均失败且没有任何已知的 agent 主机。
从实现细节看,这个端点有两点值得注意:
- 并发探活:多个本地客户端通过
sync.WaitGroup+atomic.Bool并行 ping,而不是串行等待,因此多 Docker 客户端场景下总耗时不会线性累加; - 宽容策略:只要有一个客户端存活即判定健康,避免单客户端抖动导致整个 Dozzle 被标记为 unhealthy。
远程 Agent 被有意排除在服务端检查之外
官方文档特别强调:远程 agent故意不参与服务端的健康检查——一个不可达的 agent 不应让主 Dozzle 进程被判为不健康。这与上面第 2 步的逻辑互为印证:即使配置了 agent 主机,服务端也不向它们发起任何 ping,仅在本机无 Docker 客户端时把它们作为"降级通过"的依据。
每个 agent 可以暴露自己的健康检查,参见 Agent 健康检查配置。
Agent 模式:RPC 健康检查
除了服务端模式,dozzle healthcheck还会自动识别"当前进程是否是 agent"并切换检查方式。入口实现位于 internal/support/cli/health_command.go:
const agentAddrFile = "/tmp/dozzle-agent.addr" if data, err := os.ReadFile(agentAddrFile); err == nil { // 读到 agent 地址文件 → 走 RPC 检查 ... return healthcheck.RPCRequest(ctx, agentAddress, certs) } else { // 否则 → 走 HTTP 检查 return healthcheck.HttpRequest(args.Addr, args.Base) }逻辑为:若容器内存在 agent 地址文件/tmp/dozzle-agent.addr,则说明当前进程运行在 agent 模式,此时会对该地址发起 gRPC 请求;否则执行服务端 HTTP 检查。地址规范化方面,若 agent 地址的主机部分为空、::或0.0.0.0,会被替换为127.0.0.1,以确保从本机可达。
RPC 检查实现在 internal/healthcheck/rpc.go:通过agent.NewClient(addr, certs)建立客户端,然后调用client.ListContainers(ctx, ...)验证 agent 是否正常响应。注意它需要 TLS 证书(ReadCertificates(embeddedCerts, args.CertPath, args.KeyPath)),并受 CLI 的--timeout(环境变量DOZZLE_TIMEOUT,默认10s,见 internal/support/cli/args.go)约束。
退出码与输出
dozzle healthcheck的退出码规则(官方文档):
0—— 健康,对应 HTTP 200;- 非零 —— 不健康,包括网络错误或非 200 响应。
HTTP 请求的底层实现在 internal/healthcheck/http.go:
func HttpRequest(addr string, base string) error { if strings.HasPrefix(addr, ":") { addr = "localhost" + addr } if base == "/" { base = "" } url := fmt.Sprintf("%s%s/healthcheck", addr, base) if !strings.HasPrefix(url, "http") { url = "http://" + url } log.Info().Str("url", url).Msg("performing healthcheck") resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode == 200 { return nil } return fmt.Errorf("healthcheck failed with status code %d", resp.StatusCode) }- 请求失败(连接错误等)直接返回
err,进程以非零退出; - 响应非 200 时,错误信息中包含失败的状态码,并通过 zerolog 写入 stdout,与文档中"失败 URL 和状态写入 stdout"的描述对应;
- 成功(200)返回
nil,进程以 0 退出。
该行为有对应的单元测试覆盖:internal/healthcheck/http_test.go 构造了始终返回 200 和始终返回 500 的两个测试服务器,分别断言HttpRequest返回nil和错误,验证了"200 视为健康、500 视为失败"的判定。
兼容--addr与--base自定义端口和基础路径
官方文档明确:该命令遵守--addr和--base,因此在自定义端口或基础路径下无需额外配置。源码印证了这一点:
HttpRequest接收的args.Addr、args.Base直接参与 URL 构造;- 当
addr以:开头(如默认值:8080)时,自动补全为localhost:8080; - 当
base为/(默认值)时按空串处理,避免产生//healthcheck之类的畸形路径; - 若 URL 不以
http开头,自动补http://前缀。
对应参数定义见 internal/support/cli/args.go:
Addr string `arg:"env:DOZZLE_ADDR" default:":8080" help:"sets host:port to bind for server..."` Base string `arg:"env:DOZZLE_BASE" default:"/" help:"sets the base for http router."`因此无论是默认的:8080、/,还是通过DOZZLE_ADDR/DOZZLE_BASE修改过的监听地址与基础路径(例如在反向代理后挂在/dozzle路径下),healthcheck 命令都能正确构造出指向自身的健康检查 URL,无需在 compose 中额外传参。
已知限制:不要与--health-cmd一起使用
官方文档给出了一条重要警告:
Warning:由于 Docker 自身的一个 bug,
healthcheck命令无法与--health-cmd标志配合使用。请使用如上所示的docker-compose.yml中的healthcheck块(即docker-compose的healthcheck.test指令),而不要通过dozzle --health-cmd ...的方式传递。相关细节可参阅 Docker CLI 的 issue docker/cli#3719。
换句话说:健康检查命令必须通过 compose 的healthcheck块声明(如本文第一节的配置),而不是通过 Dozzle 的--health-cmd参数注入。
小结:一条命令、两种模式、三类判定
总结dozzle healthcheck的完整行为:
| 运行模式 | 检查方式 | 判定依据 |
|---|---|---|
服务端(无/tmp/dozzle-agent.addr) | HTTP GET 自身/healthcheck | 至少一个本地 Docker 客户端 ping 成功;或本地无客户端但有 agent 主机 |
| Agent(存在地址文件) | gRPCListContainers探活 | agent 能否正常响应容器列表请求 |
| 失败场景 | —— | 全部本地客户端失败且无 agent 主机 → HTTP 500 → 进程非零退出 |
启用健康检查的成本只是微量的 CPU 开销,换来的却是编排平台对 Dozzle 存活状态的准确感知。按照本文第一节的 compose 配置复制到你的部署文件中,即可立即获得这项能力。
【免费下载链接】dozzleRealtime log viewer for containers. Supports Docker, Swarm and K8s.项目地址: https://gitcode.com/GitHub_Trending/do/dozzle
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考