
类型:虚拟化技术
简介:基于操作系统层级的虚拟化技术,将软件与其依赖项打包为容器。
在使用 Docker Compose 编排多服务应用时,健康检查(healthcheck)是保证服务依赖关系稳定的重要机制。但在实际项目中,经常会遇到容器状态显示 unhealthy,甚至导致整个项目无法启动的问题。
docker compose ps NAME STATUS db unhealthy app unhealthy
当数据库、应用等核心服务处于 unhealthy 状态时,依赖它们的容器将无法正常启动,从而导致整个系统启动失败。
一、问题根源:健康检查依赖容器内部环境
健康检查命令是在容器内部执行的,因此必须依赖镜像中已有的工具和命令。
不同基础镜像差异较大,尤其是 Alpine 系列镜像,通常非常精简,默认不会包含 curl、bash、wget 等工具。
二、常见错误写法
错误1:字符串形式未使用 CMD-SHELL
healthcheck: test: "pg_isready -U postgres"
该写法不会正确解析 shell 语法,可能导致执行失败。
错误2:镜像中不存在 curl
healthcheck: test: ["CMD", "curl", "http://localhost:8080/health"]
如果镜像未安装 curl,会直接失败。
错误3:命令未验证
healthcheck:
test: ["CMD-SHELL", "mysql -u root -p${MYSQL_PASSWORD} -e 'SELECT 1'"]
常见问题包括环境变量未传递、客户端不存在或引号解析错误。
三、正确的 Healthcheck 配置
PostgreSQL
db:
image: postgres:16-alpine
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-postgres} -d ${POSTGRES_DB:-postgres}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
MySQL
mysql:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
Redis
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
Nginx
nginx:
image: nginx:alpine
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost/health || exit 1"]
interval: 30s
timeout: 5s
retries: 3
Node.js 应用
app:
image: myapp:latest
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
四、健康检查排查方法
查看健康检查状态:
docker inspect --format='{{json .State.Health}}' container_name | jq .
手动测试命令:
docker compose exec db pg_isready -U postgres docker compose exec redis redis-cli ping
五、健康检查通过但服务不可用
健康检查通过并不代表服务完全可用,它只代表进程存活。
常见问题包括:
PostgreSQL 返回成功但初始化未完成
MySQL 可连接但主从未同步
应用接口返回200但依赖未就绪
六、正确做法:真实业务健康检查
Node.js 示例
app.get('/health', async (req, res) => {
const checks = {}
try {
await db.query('SELECT 1')
checks.database = 'ok'
} catch (err) {
checks.database = 'error'
}
try {
await redis.ping()
checks.redis = 'ok'
} catch (err) {
checks.redis = 'error'
}
const allOk = Object.values(checks).every(v => v === 'ok')
res.status(allOk ? 200 : 503).json({
status: allOk ? 'ok' : 'degraded',
checks
})
})
Python Flask 示例
@app.route('/health')
def health():
try:
db.session.execute('SELECT 1')
redis_client.ping()
return jsonify({'status': 'ok'}), 200
except Exception as e:
return jsonify({'status': 'error'}), 503
七、总结
Docker Compose healthcheck 常见问题主要包括:
- 镜像缺少工具
- 命令写法错误
- 仅检测进程未检测业务
- 依赖服务未真正就绪
正确思路是:健康检查不仅要判断“进程存活”,还要验证“关键依赖可用”和“业务是否真正可用”。

