Docker Compose 健康检查实战:让服务在华为云上自愈
很多人第一次把应用搬到云服务器上,都会碰到同一个迷惑:容器明明是 Up 的,端口也通,但请求就是 502 / 504。
翻遍日志可能只找到一句 connection refused。原因在于,Docker 判断"容器还活着"的标准只有一个——主进程有没有退出。进程没死,容器就一直显示 running,哪怕里面的应用早就崩了。
想让服务真正具备"自愈"能力,第一步是把健康状态显式描述出来,也就是 healthcheck。
一、healthcheck 到底在检查什么
healthcheck 的本质很简单:在容器内部定时跑一条命令,退出码为 0 就算健康。
最典型的用法是请求自己的健康检查接口:
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install --no-cache-dir fastapi uvicorn
# 每 30 秒探一次,单次最多等 3 秒,连着失败 3 次才判定为 unhealthy
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -fsS http://localhost:8000/health || exit 1
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
这里有个容易忽略的细节:curl 不一定存在。精简镜像里更稳妥的做法是直接用 Python 自己探:
HEALTHCHECK CMD python -c "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://localhost:8000/health').status==200 else 1)"
如果服务是用 compose 编排的,就可以不改动镜像,直接写在 docker-compose.yml 里:
services:
api:
image: my-api:1.0
healthcheck:
test: ["CMD", "curl", "-fsS", "http://localhost:8000/health"]
interval: 30s
timeout: 3s
retries: 3
start_period: 40s
restart: unless-stopped
start_period 特别值得强调。应用刚启动时可能要二十多秒才能连上数据库,这段时间里如果探针一直失败,容器会被过早判定为 unhealthy,触发重启——于是陷入"启动 → 检查失败 → 重启 → 再失败"的死循环。给它留一段"宽限期",是新手最容易踩的坑。
二、让依赖服务"等"健康,而不是"等"启动
很多人在 compose 里这么写:
worker:
depends_on:
db: # 老写法
或者新写法:
worker:
depends_on:
db:
condition: service_started
这两种都只保证"db 容器启动了",并不保证"db 能连上了"。worker 很可能在数据库还没 ready 的时候就去建立连接,然后报错退出。
稳妥的写法是等健康状态:
worker:
depends_on:
db:
condition: service_healthy
这样 worker 会一直等到 db 的 healthcheck 通过(healthy)才真正启动。nginx 这类反向代理也可以用同样的方式依赖后端。
三、在华为云 Flexus 上落地的完整流程
以一台轻量应用服务器为例(Flexus 应用服务器 L 实例,系统选 Ubuntu 22.04):
1. 安装 Docker
curl -fsSL https://get.docker.com | sh
sudo systemctl enable --now docker
sudo docker compose version
2. 准备项目目录
mkdir -p ~/hello && cd ~/hello
目录保持简单:
hello/
├── docker-compose.yml
└── api/
├── Dockerfile
└── main.py
main.py 里加一个最小可用的健康接口:
from fastapi import FastAPI
app = FastAPI()
@app.get("/health")
def health():
return {"status": "ok"}
@app.get("/")
def index():
return {"hello": "huaweicloud"}
3. 写编排文件
services:
api:
build: ./api
ports:
- "8000:8000"
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]
interval: 15s
timeout: 3s
retries: 5
start_period: 30s
restart: unless-stopped
4. 放行安全组
容器里监听了 8000,并不代表外面就能访问。需要到控制台的安全组里添加入方向规则放行 TCP 8000。正式环境更推荐的做法是只放行 80 / 443,前面挂一层 Nginx,再由 Nginx 反代到 8000,这样业务端口根本不必暴露。
5. 启动并验证
cd ~/hello
sudo docker compose up -d --build
# 看健康状态
sudo docker compose ps
# NAME SERVICE STATUS PORTS
# hello-api-1 api Up 2 minutes (healthy) 0.0.0.0:8000->8000/tcp
再从外部验证一次:
curl -s http://<你的公网IP>:8000/health
# {"status":"ok"}
四、排错三板斧
一旦出现 unhealthy,按这个顺序查,基本都能定位问题。
看状态
sudo docker compose ps
输出里出现 Up 5 minutes (unhealthy),就说明 Docker 已经在替你盯着这个容器了。
看健康检查历史(能看到最近几次探测的退出码和错误输出)
sudo docker inspect --format '{{json .State.Health}}' hello-api-1 | python3 -m json.tool
看应用日志
sudo docker compose logs --tail=100 -f api
常见原因无非三类:应用自身启动失败、端口配置写错、探针命令在容器里根本不存在(alpine 这类精简镜像里没有 curl,改用 wget 或 nc 即可)。
五、一点延伸
healthcheck 解决的是"状态可见",而"自动恢复"是另一件事:
restart: unless-stopped负责进程崩了之后自动拉起;healthcheck负责让编排系统知道它到底好不好;- 两者配合,才能在
depends_on: condition: service_healthy这种编排里形成可靠的启动顺序。
顺手再给容器加上资源限制,避免某个进程把整机吃满:
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
小结
把"容器在跑"和"服务可用"区分开,是用 Docker 上云之后最值得养成的习惯。一个 healthcheck 加上 depends_on: service_healthy,成本只有几行 YAML,却能把绝大部分"偶发启动失败"和"莫名其妙的 502"提前暴露在编排层,而不是等到用户投诉才发现。
文中给出的目录结构、compose 配置与排查命令,可直接按步骤在华为云 Flexus 上复现验证;如果你的服务有数据库依赖,建议优先把 start_period 和 condition: service_healthy 这两处配齐,收益最明显。
本文为原创文章,配置示例基于华为云 Flexus(Ubuntu 22.04)与 Docker Compose 官方文档整理。
- 点赞
- 收藏
- 关注作者
评论(0)