《Docker 运维入门实战:日志 + 健康检查,出问题先知道去哪看》
关键词:docker compose logs、健康检查、healthcheck、日志轮转
上一篇我们把服务收敛成了一个入口:网关 Nginx 在前,web 和 api 在后,HTTPS 也接上了。但到这里还缺运维最关键的一环——服务一旦出问题,你怎么第一时间发现、又去哪里看原因?靠"感觉不对就重启"是最贵的排查方式。今天给这套 my-app 加上两样东西:日志查看和健康检查。
本文接着前面的结构讲:gateway(Nginx 网关)、web(静态网页)、api(8000 端口的 Python 接口服务),假设服务器上已经装好 Docker 和 Docker Compose。
一、先看日志:别进容器翻文件
容器里的进程往标准输出打的日志,Docker 都替你收着,不用进容器找日志文件。最常用的就几条:
docker compose logs # 看所有服务的日志
docker compose logs api # 只看 api
docker compose logs -f api # 跟踪实时输出,相当于 tail -f
docker compose logs --tail 100 web # 只看 web 最近 100 行
docker compose logs --since 10m gateway # 只看网关最近 10 分钟的
docker compose logs -t api # 每行前面带上时间戳
一个习惯要先养成:出问题先看日志,再决定动不动手。接口 500 了,先 logs api 看报错;网关转发不通,先 logs gateway 看它把请求发到哪去了、后端回了什么状态码。日志里没有报错,再去怀疑配置和网络。
还有一个容易忽略的点:前面 api 的示例代码里把请求日志关掉了(log_message 空实现)。排查阶段建议先把它打开,或者至少让异常和启动信息能打出来——一个从不说话的服务,出问题时你连它死在哪一步都不知道。
二、给日志加个上限:别让它把磁盘吃满
默认情况下容器日志文件会一直增长,跑久了可能把磁盘写满,反过来把服务拖垮。在 docker-compose.yml 里给每个服务加一行日志配置:
services:
api:
build: ./api
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
意思是单个日志文件最多 10MB,轮转保留 3 个,超过就覆盖最旧的。改完重新 docker compose up -d 生效。这一步现在做成本最低——等磁盘报警那天再做,就已经晚了。
三、健康检查:让 Compose 替你判断"活着还是能用"
容器进程在跑,不等于服务能用:接口进程可能卡住了还在监听端口,也可能启动到一半就僵住了。健康检查就是定期在容器里跑一条探活命令,告诉 Compose 这个服务到底健不健康:
services:
api:
build: ./api
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/')"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
逐项看:
test:探活命令。python:3.12-alpine镜像里没有 curl,就用自带的 urllib 发一次请求,能拿到响应才算通过——不要用"进程在不在"代替"接口通不通"。interval: 30s:每 30 秒查一次,别设太密,探活本身也是负载。timeout: 5s:一次探活超过 5 秒就判失败。retries: 3:连续失败 3 次才标记为不健康,避免一次网络抖动就误判。start_period: 10s:启动后 10 秒内即使失败也不计入,给服务留出启动时间。
web 和 gateway 都是 nginx:alpine,里面没有 curl,但有 busybox 的 wget,写法换一下:
web:
build: ./web
healthcheck:
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://localhost/"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
四、让网关等后端健康了再启动
健康检查写好后,还能顺手解决一个启动顺序问题:以前网关可能比 api 先起来,前几秒转发过去全是失败。有了健康状态,depends_on 可以改成等后端"健康"再启动:
services:
gateway:
image: nginx:alpine
depends_on:
web:
condition: service_healthy
api:
condition: service_healthy
注意:condition: service_healthy 只保证启动顺序,不保证运行中后端挂了网关会跟着处理——运行时的故障发现,还是靠日志和健康状态本身。
改完启动并验证:
docker compose up -d
docker compose ps # STATUS 一列会显示 (healthy) 还是 (unhealthy)
docker inspect my-app-api-1 --format '{{.State.Health.Status}}'
看到 (healthy),这套探活就算通了。故意把 api 的端口改错再启动一次,你会看到它长时间停在 (health: starting) 然后变成 (unhealthy)——这正是健康检查的价值:问题不用等用户报上来,状态里已经写明了。
五、两个新手最容易踩的坑
1. 健康检查命令在镜像里根本不存在
最常见的失败不是服务坏了,是探活命令本身跑不起来:alpine 镜像里写 curl、debian 镜像里写 wget,结果容器一启动就被判不健康。写之前先确认镜像里有什么:docker compose exec api sh 进去试一条,通了再写进配置。
2. 只看健康状态,不看日志
健康检查只能告诉你"不健康",不能告诉你"为什么"。状态变红之后,下一步永远是 docker compose logs 看具体报错——健康检查是报警器,日志才是案发现场。两个配合用,出问题先知道去哪看,而不是凭感觉重启。
结语
从单容器、Compose 编排、数据卷、反向代理和 HTTPS,到今天的日志与健康检查,这条线已经是一套能对外服务、出了问题也能快速定位的部署了。下一篇可以往"怎么更新不中断服务"走一步:改了代码之后,如何滚动更新这几个容器,而不用停掉整个站。
- 点赞
- 收藏
- 关注作者
评论(0)