nerdctl 容器健康检查(Healthcheck)完整指南:Docker 兼容配置、systemd 自动调度与状态机原理
2026/9/24 15:38:28 网站建设 项目流程
  • CLI
  • 云原生

【免费下载链接】nerdctl

contaiNERD CTL - Docker-compatible CLI for containerd, with support for Compose, Rootless, eStargz, OCIcrypt, IPFS, ...

项目地址:https://gitcode.com/gh_mirrors/ne/nerdctl
点击查看免费下载

健康检查(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

满足版本要求后,即可通过以下两种途径为容器配置健康检查:

  1. 创建容器时:在nerdctl run/nerdctl create命令中直接附加健康检查标志;
  2. 构建镜像时:在 Dockerfile 中编写HEALTHCHECK指令,创建容器时自动继承。

配置选项详解

CLI 标志(nerdctl run/nerdctl create

标志含义默认值
--health-cmd用于探测健康状态的命令
--health-interval两次探测之间的时间间隔30s
--health-timeout单次探测允许的最大执行时间30s
--health-retries连续失败多少次后判定为 unhealthy3
--health-start-period容器启动后的宽限期,期间内失败不计入失败计数0s
--no-healthcheck显式禁用容器(含镜像)中的任何 HEALTHCHECKfalse

这些标志的解析位于 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 按以下优先级确定最终的健康检查配置:

  1. CLI 标志优先级最高:只要显式传入了--health-cmd--health-interval--health-timeout--health-retries--health-start-period中的任意一个,就以 CLI 值为准;
  2. 未传 CLI 标志时,继承镜像中声明的健康检查(DockerfileHEALTHCHECK);
  3. 两者都没有,则不配置任何健康检查。

该逻辑在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函数:

  1. 获取容器的 task,检查容器状态必须是Running,否则报错container is not running
  2. 从容器 labels 读取健康检查配置,解析失败或没有Test会直接报错;
  3. 填充默认值后调用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);
  • 日志:每次探测的结果(StartEndExitCodeOutput)以 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),避免异常命令造成内存膨胀。HealthHealthcheckResultHealthcheck等结构体保持与 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继承PATHNERDCTL_TOMLBUILDKIT_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, ...

项目地址:https://gitcode.com/gh_mirrors/ne/nerdctl
点击查看免费下载

相关推荐

上一篇:猫抓浏览器扩展完整指南:网页视频嗅探、M3U8解密与批量下载实战
下一篇:skill-icons 技能图标终极指南:5 分钟让 GitHub 主页与简历亮起来

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询