Docker Swarm健康检查失败直接终止容器(退出码0)求助
首先,从你提供的容器状态信息来看,核心矛盾点在于:健康检查失败后容器直接以ExitCode 0退出,而非停留在unhealthy状态。ExitCode 0意味着容器是正常退出的,而非被Docker/Swarm强制终止,所以我们需要先定位应用自身退出的原因,再调整配置确保健康检查逻辑正确。
第一步:排查容器退出的真正原因
容器以ExitCode 0退出,大概率是你的Node.js应用主动调用了process.exit(0),或者启动脚本执行完毕后自然退出。你需要查看应用的运行日志来确认:
docker logs 73fce379163c # 替换为你的容器ID
重点关注日志末尾是否有应用退出的提示,比如未捕获的异常(虽然异常通常会导致非0退出码,但也不排除自定义处理)、或者启动脚本的执行逻辑问题。
第二步:修正Dockerfile的指令顺序(最佳实践)
你的Dockerfile存在指令顺序错误,这虽然不是当前问题的直接原因,但会影响构建效率和镜像正确性:
# 正确的Dockerfile顺序 FROM node:current WORKDIR /usr/src/app # 先复制package文件,利用Docker缓存减少重复构建 COPY package*.json ./ RUN npm install # 再复制其余代码,这样代码变更不会触发npm install的重新执行 COPY . . EXPOSE 8080 # 健康检查可以定义在这里,或者在stack文件中(stack配置优先级更高) HEALTHCHECK --interval=10s --timeout=3s CMD curl --fail http://localhost:8080/health || exit 1 CMD [ "npm", "start" ]
原Dockerfile中COPY . .放在CMD之后,虽然构建时不会报错,但会导致每次代码变更都重新执行npm install,浪费资源。
第三步:调整健康检查配置
你尝试的test: ["CMD", "curl -f http://localhost:8080/health || exit 1"]写法是错误的——数组格式的CMD要求每个命令参数单独作为数组元素,无法直接解析shell逻辑。正确的写法是使用CMD-SHELL来执行shell命令:
version: "3" services: webapp: image: testapp:broken ports: - 8081:8080 healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"] interval: 10s timeout: 2s retries: 3 deploy: replicas: 1 update_config: parallelism: 2 delay: 10s restart_policy: condition: on-failure
不过其实curl -f本身在HTTP状态码≥400时就会返回非0退出码,所以|| exit 1是多余的,直接写curl -f http://localhost:8080/health即可。
第四步:确保应用不会因404请求退出
如果你的Node.js应用(比如Express)没有定义/health路由,收到请求只会返回404,不会主动退出。如果应用确实因此退出,你需要检查代码中的错误处理逻辑:
- 是否有全局错误捕获逻辑错误地调用了
process.exit(0)? - 是否有第三方中间件在处理404时触发了退出?
第五步:调整Swarm重启策略(可选)
如果希望容器在健康检查失败后重启,可以修改restart_policy为:
restart_policy: condition: any delay: 5s max_attempts: 3
不过这是解决容器退出后的重启问题,核心还是要先解决容器为什么会退出的问题。
内容的提问来源于stack exchange,提问作者Bogdan Daniel

