- CLI
- 云原生
【免费下载链接】nerdctl
contaiNERD CTL - Docker-compatible CLI for containerd, with support for Compose, Rootless, eStargz, OCIcrypt, IPFS, ...
健康检查(healthcheck)是容器化应用可观测性中至关重要的一环:通过定期在容器内执行用户定义的探测命令,将"进程活着"细化为"服务真正可用"。本项目 nerdctl 提供了与 Docker 兼容的健康检查能力,支持在nerdctl run/nerdctl create时通过 CLI 标志配置,也支持继承镜像 Dockerfile 中声明的HEALTHCHECK,并在 Linux + systemd 环境下由 systemd timer 自动定时执行、自动更新starting / healthy / unhealthy状态。读完本文,你将掌握 nerdctl 健康检查的全部配置项、优先级规则、手动触发方式、状态机迁移逻辑,以及 systemd 定时调度的底层实现细节。
版本与启用前提
健康检查功能从nerdctl >= 2.1.5开始提供(参见 docs/healthchecks.md 中的要求表格)。使用前请确认:
nerdctl version满足版本要求后,即可通过以下两种途径为容器配置健康检查:
- 创建容器时:在
nerdctl run/nerdctl create命令中直接附加健康检查标志; - 构建镜像时:在 Dockerfile 中编写
HEALTHCHECK指令,创建容器时自动继承。
配置选项详解
CLI 标志(nerdctl run/nerdctl create)
| 标志 | 含义 | 默认值 |
|---|---|---|
--health-cmd | 用于探测健康状态的命令 | 无 |
--health-interval | 两次探测之间的时间间隔 | 30s |
--health-timeout | 单次探测允许的最大执行时间 | 30s |
--health-retries | 连续失败多少次后判定为 unhealthy | 3 |
--health-start-period | 容器启动后的宽限期,期间内失败不计入失败计数 | 0s |
--no-healthcheck | 显式禁用容器(含镜像)中的任何 HEALTHCHECK | false |
这些标志的解析位于 cmd/nerdctl/container/container_create.go,解析后还会经过helpers.ValidateHealthcheckFlags做参数合法性校验,之后在 pkg/cmd/container/create.go 的withHealthcheck函数中组装成健康检查配置。
注意:
--health-start-interval选项目前不被 nerdctl 支持(与 Docker 的对应能力存在差异),使用时会报错或无效,请勿在脚本中依赖该参数。
上述默认值并非凭空设定,它们在源码中有明确定义,见 pkg/healthcheck/health.go:
DefaultProbeInterval = 30 * time.Second // 默认探测间隔 DefaultProbeTimeout = 30 * time.Second // 单次探测超时 DefaultStartPeriod = 0 * time.Second // 启动宽限期 DefaultProbeRetries = 3 // 判定 unhealthy 所需的连续失败次数当某项未配置时,Healthcheck.ApplyDefaults()(pkg/healthcheck/health.go)会自动补齐默认值。
DockerfileHEALTHCHECK
在镜像构建阶段声明健康检查(Dockerfile 语法与 Docker 一致):
HEALTHCHECK --interval=30s --timeout=3s --retries=3 CMD curl -f http://localhost/ || exit 1镜像中的健康检查配置会被写入镜像的config.Labels。nerdctl 创建容器时读取标签containerd.io/nerdctl/healthcheck(见 pkg/labels/labels.go)并解析为内部配置。相关逻辑位于 pkg/cmd/container/create.go。
配置优先级:CLI 标志 > 镜像声明
当创建容器时,nerdctl 按以下优先级确定最终的健康检查配置:
- CLI 标志优先级最高:只要显式传入了
--health-cmd、--health-interval、--health-timeout、--health-retries、--health-start-period中的任意一个,就以 CLI 值为准; - 未传 CLI 标志时,继承镜像中声明的健康检查(Dockerfile
HEALTHCHECK); - 两者都没有,则不配置任何健康检查。
该逻辑在withHealthcheck(pkg/cmd/container/create.go)中体现:先从镜像 labels 解析出基础配置,再用 CLI 选项逐个覆盖非零字段,最后如果配置仍为空结构体(reflect.DeepEqual判断),则跳过写入。注意 CLI 覆盖是"按字段合并"而非整体替换——例如镜像声明了 interval 而你只传了--health-cmd,则 interval 沿用镜像值。
禁用健康检查:--no-healthcheck
若镜像自带健康检查但当前场景不需要,可显式关闭:
nerdctl run --no-healthcheck myapp源码中--no-healthcheck会生成Test: []string{"NONE"}的特殊配置(见 pkg/cmd/container/create.go),执行时被识别为"无探测"直接跳过(见下文状态与执行机制)。
手动触发:nerdctl container healthcheck
除了自动调度,用户也可以随时手动触发一次健康检查:
nerdctl container healthcheck <container-id>该命令是执行健康检查的入口,尤其适用于外部调度器(如 cron、Kubernetes 风格的第三方探活系统)自行按节奏触发探测的场景。命令定义为healthcheck [flags] CONTAINER,接受容器 ID 或前缀匹配(见 cmd/nerdctl/container/container_health_check.go),底层调用 pkg/cmd/container/health_check.go 中的HealthCheck函数:
- 获取容器的 task,检查容器状态必须是
Running,否则报错container is not running; - 从容器 labels 读取健康检查配置,解析失败或没有
Test会直接报错; - 填充默认值后调用
healthcheck.ExecuteHealthCheck真正执行探测。
值得注意的是:如果容器已停止,手动触发还会顺带清理可能残留的 systemd timer(CleanupStaleHealthcheckTimer),避免遗留垃圾单元。
健康状态机:starting / healthy / unhealthy
容器健康状态有三种(定义于 pkg/healthcheck/health.go):
starting:容器初始化阶段(仅当配置了--health-start-period时进入);healthy:健康检查通过;unhealthy:连续失败次数达到--health-retries阈值。
状态迁移的核心逻辑位于 pkg/healthcheck/executor.go 的updateHealthStatus,分为两条工作流:
Start Period 工作流(宽限期内):
- 只要探测退出码为 0,立即将状态置为
healthy并退出宽限期; - 宽限期内的失败结果被忽略,不增加失败计数(给慢启动应用留出初始化时间)。
Health Interval 工作流(正常周期):
- 退出码为 0:状态变为
healthy,失败计数清零; - 退出码非 0:
FailingStreak++,当FailingStreak >= Retries时状态置为unhealthy。
单次探测的执行(probeHealthCheck,pkg/healthcheck/executor.go)通过 containerd 的task.Exec在容器内创建临时进程(exec id 形如health-check-<id>),探测命令继承容器的环境变量、用户与工作目录;若执行超过Timeout,会发送SIGKILL强杀并记录超时信息。命令类型支持 Docker 兼容的三种形态(pkg/healthcheck/executor.go):
NONE/ 空串:跳过执行;CMD(JSON 数组形式,如["CMD", "curl", "-f", "http://localhost/"]):直接以数组形式执行;CMD-SHELL:拼接为/bin/sh -c "<command>"交给 shell 执行——--health-cmd传入的正是这种形式(pkg/cmd/container/create.go)。
状态与日志的存储:labels + health.json
健康检查的执行结果如何被查询?核心是"标签存状态、文件存日志":
- 状态:每次探测后,
HealthState(状态 + 连续失败次数 + 是否处于宽限期)以 JSON 形式写回容器标签containerd.io/nerdctl/healthstate(见 pkg/healthcheck/log.go); - 日志:每次探测的结果(
Start、End、ExitCode、Output)以 JSON 行追加写入容器状态目录下的health.json(pkg/healthcheck/log.go),并调用file.Sync()确保落盘; - 查询:
nerdctl inspect通过ReadHealthStatusForInspect读取最近5 条日志(MaxLogEntries),每条输出超过4096 字节会被截断(MaxOutputLenForInspect),防止 inspect 输出被淹没(pkg/healthcheck/log.go)。
探测过程中的原始输出缓冲上限为 1MB(MaxOutputLen),超出部分以... [truncated]标记(见 pkg/healthcheck/log.go),避免异常命令造成内存膨胀。Health、HealthcheckResult、Healthcheck等结构体保持与 Docker 兼容的字段布局(pkg/healthcheck/health.go),方便依赖 Docker inspect 输出的工具无缝迁移。
基于 systemd 的自动健康检查
在 Linux 且具备 systemd 的环境下,nerdctl 会自动创建并管理 systemd timer 单元,按配置的间隔定时执行健康检查——不需要常驻守护进程,调度完全交给 systemd,可靠且零额外常驻开销。
启用条件
自动调度仅在以下条件全部满足时生效(见 pkg/healthcheck/healthcheck_manager_linux.go 的shouldSkipHealthCheckSystemd):
- 系统可用 systemd(
defaults.IsSystemdAvailable()为真); - 容器不是 rootless 模式运行;
- 配置文件
nerdctl.toml中未将disable_hc_systemd设置为true; - 健康检查配置有效且
Test不为空、不是NONE。
工作原理
创建/启动带健康检查的容器时,nerdctl 在 pkg/containerutil/containerutil.go 与 pkg/containerutil/containerutil.go 依次调用两个关键函数(位于 pkg/healthcheck/healthcheck_manager_linux.go):
1.CreateTimer(创建临时 timer):通过systemd-run为容器创建 transient 单元,关键参数包括:
systemd-run --unit <container-id> \ --on-unit-inactive=<interval> \ --timer-property=AccuracySec=1s \ --collect \ <nerdctl 可执行文件> <全局参数> container healthcheck <container-id>--on-unit-inactive:以--health-interval作为定时频率——注意是每次探测结束后再等一个 interval,避免长任务与调度重叠;--timer-property=AccuracySec=1s:定时精度 1 秒;--collect:即使容器停止后探测报错导致 service 单元进入 failed 状态,也会被 systemd 自动垃圾回收,无需手动systemctl reset-failed;- 通过
--setenv继承PATH、NERDCTL_TOML、BUILDKIT_HOST等环境变量,保证 systemd 服务环境中 nerdctl 命令可正常运行; - 创建前会防御性地清理上一轮残留的 timer(
CleanupStaleHealthcheckTimer),否则systemd-run会因 "Unit was already loaded" 失败。
2.StartTimer(启动 timer):通过 systemd DBus 接口(go-systemd)重启<container-id>.service单元,触发首个探测周期的调度。
容器停止/删除时,pkg/cmd/container/remove.go 调用RemoveTransientHealthCheckFiles停止并清理对应的.timer与.service单元;ForceRemoveTransientHealthCheckFiles则提供非阻塞的强制清理(带 3 秒超时,绝不阻塞容器删除流程)。此外,当手动触发nerdctl container healthcheck时若发现容器已停止,也会同步清理残留 timer(pkg/cmd/container/health_check.go)。
在 nerdctl.toml 中关闭 systemd 调度
如果不想使用 systemd 自动调度(例如改用外部调度器配合nerdctl container healthcheck),可在nerdctl.toml中配置(字段定义见 pkg/config/config.go):
disable_hc_systemd = true设置后,健康检查配置依然会写入容器,但不再创建 systemd timer,你需要自行安排触发时机。
平台差异说明
- Linux + systemd:自动创建 timer 单元,实现"零守护进程"的定时探测(本文上述机制);
- Windows、macOS、FreeBSD及其他平台:
CreateTimer/StartTimer/RemoveTransientHealthCheckFiles均为空实现(no-op),例如 pkg/healthcheck/healthcheck_manager_windows.go 中仅保留函数签名。这些平台上健康检查仍可通过手动nerdctl container healthcheck触发,但不提供自动调度。
实战示例
以下三个示例完整覆盖了常见用法(与 docs/healthchecks.md 保持一致并补充说明):
1. 基本健康检查:验证 Web 服务
nerdctl run -d --name web \ --health-cmd="curl -f http://localhost/ || exit 1" \ --health-interval=5s \ --health-retries=3 \ nginx每隔 5s 用 curl 探测 nginx 根路径,连续 3 次失败即标记为unhealthy。注意镜像内需自带curl(nginx 官方镜像包含),否则探测命令会因找不到可执行文件而持续失败。
2. 带启动宽限期的健康检查
nerdctl run -d --name app \ --health-cmd="./health-check.sh" \ --health-interval=30s \ --health-timeout=10s \ --health-retries=3 \ --health-start-period=60s \ myapp--health-start-period=60s给应用最多 60 秒初始化,期间探测失败不计入重试计数;单次探测超时 10s 会被强杀并记为一次失败;整体 30s 探测一次,连续 3 次失败判为unhealthy。这类配置非常适合启动较慢的应用(如需要加载模型、连接数据库的服务)。
3. 禁用镜像自带健康检查
nerdctl run --no-healthcheck myapp即使镜像 Dockerfile 中声明了HEALTHCHECK,也不会对容器生效。
验证与排查建议
- 创建容器后,用
nerdctl inspect <container>查看Health字段:包含当前状态、连续失败次数与最近 5 次探测日志(时间戳、退出码、输出); - 若健康状态长时间停留在
starting,检查是否配置了过长的--health-start-period,或应用在宽限期内始终未通过首次探测; - 若自动调度未生效(状态一直不变、无探测日志),依次排查:systemd 是否可用、是否 rootless 模式、
nerdctl.toml是否设置了disable_hc_systemd = true; - 排查 systemd 侧问题,可查看对应单元状态(timer 单元名与容器 ID 相同,即
<container-id>.timer/.service),确认其 ActiveState 与最近触发时间。
通过 CLI 标志、镜像继承、手动触发、systemd 定时调度这四层能力,nerdctl 提供了与 Docker 工作流几乎一致的健康检查体验,同时用 containerd 的标签与状态文件机制保证了状态可查询、日志可追溯,是生产环境容器健康治理的可靠基础。
- CLI
- 云原生
【免费下载链接】nerdctl
contaiNERD CTL - Docker-compatible CLI for containerd, with support for Compose, Rootless, eStargz, OCIcrypt, IPFS, ...
相关推荐
如何使用ANTs进行精准医学图像配准:5个实用技巧
如何使用ANTs进行精准医学图像配准:5个实用技巧 ANTs(Advanced Normalization Tools)是一款强大的开源医学图像配准工具,广泛应
计算机视觉Dozzle 内置健康检查(healthcheck)完整指南:原理、配置与 Docker Compose 实践
Dozzle 内置健康检查(healthcheck)完整指南:原理、配置与 Docker Compose 实践 Dozzle 是面向 Docker、Swarm
可观测性日志分析后端运维Docker健康检查机制:容器状态监控与自愈能力实现原理
Docker健康检查机制:容器状态监控与自愈能力实现原理 你是否曾遇到过容器明明显示"运行中",但服务却无法响应的情况?Docker健康检查(Health Ch
云原生容器运行时虚拟化容器编排
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考