Add more content

This commit is contained in:
Baohua Yang
2026-01-30 16:48:39 -08:00
parent c58f61dbed
commit fec2e506d9
39 changed files with 8202 additions and 1063 deletions

View File

@@ -1,83 +1,206 @@
# HEALTHCHECK 健康检查
格式
## 基本语法
* `HEALTHCHECK [选项] CMD <命令>`设置检查容器健康状况的命令
* `HEALTHCHECK NONE`如果基础镜像有健康检查指令使用这行可以屏蔽掉其健康检查指令
```docker
HEALTHCHECK [选项] CMD <命令>
HEALTHCHECK NONE
```
`HEALTHCHECK` 指令告诉 Docker 应该如何进行判断容器状态是否正常这是 Docker 1.12 引入的新指令
`HEALTHCHECK` 指令告诉 Docker 如何判断容器状态是否正常这是保障服务高可用的重要机制
在没有 `HEALTHCHECK` 指令前Docker 引擎只可以通过容器内主进程是否退出来判断容器是否状态异常很多情况下这没问题但是如果程序进入死锁状态或者死循环状态应用进程并不退出但是该容器已经无法提供服务了 1.12 以前Docker 不会检测到容器的这种状态从而不会重新调度导致可能会有部分容器已经无法提供服务了却还在接受用户请求
---
而自 1.12 之后Docker 提供了 `HEALTHCHECK` 指令通过该指令指定一行命令用这行命令来判断容器主进程的服务状态是否还正常从而比较真实的反应容器实际状态
## 为什么需要 HEALTHCHECK
当在一个镜像指定了 `HEALTHCHECK` 指令后用其启动容器初始状态会为 `starting` `HEALTHCHECK` 指令检查成功后变为 `healthy`如果连续一定次数失败则会变为 `unhealthy`
在没有 HEALTHCHECK 之前Docker 只能通过**进程退出码**来判断容器状态
`HEALTHCHECK` 支持下列选项
**问题场景**
- Web 服务死锁无法响应请求但进程仍在运行
- 数据库正在启动中尚未准备好接受连接
- 应用陷入死循环CPU 爆满但进程存活
* `--interval=<间隔>`两次健康检查的间隔默认为 30
* `--timeout=<时长>`健康检查命令运行超时时间如果超过这个时间本次健康检查就被视为失败默认 30
* `--retries=<次数>`当连续失败指定次数后则将容器状态视为 `unhealthy`默认 3
**引入 HEALTHCHECK **
Docker 定期执行指定的检查命令根据返回值判断容器是否"健康"
`CMD`, `ENTRYPOINT` 一样`HEALTHCHECK` 只可以出现一次如果写了多个只有最后一个生效
```
容器状态转换:
Starting ──成功──> Healthy ──失败N次──> Unhealthy
▲ │
└──────成功──────┘
```
`HEALTHCHECK [选项] CMD` 后面的命令格式和 `ENTRYPOINT` 一样分为 `shell` 格式 `exec` 格式命令的返回值决定了该次健康检查的成功与否`0`成功`1`失败`2`保留不要使用这个值
---
假设我们有个镜像是个最简单的 Web 服务我们希望增加健康检查来判断其 Web 服务是否在正常工作我们可以用 `curl` 来帮助判断 `Dockerfile` `HEALTHCHECK` 可以这么写
## 基本用法
### Web 服务检查
```docker
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s \
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -fs http://localhost/ || exit 1
```
这里我们设置了每 5 秒检查一次这里为了试验所以间隔非常短实际应该相对较长如果健康检查命令超过 3 秒没响应就视为失败并且使用 `curl -fs http://localhost/ || exit 1` 作为健康检查命令
### 命令返回值
使用 `docker build` 来构建这个镜像
- `0`: 成功 (healthy)
- `1`: 失败 (unhealthy)
- `2`: 保留值 (不使用)
```bash
$ docker build -t myweb:v1 .
### 常用选项
| 选项 | 说明 | 默认值 |
|------|------|--------|
| `--interval` | 两次检查的间隔 | 30s |
| `--timeout` | 检查命令的超时时间 | 30s |
| `--start-period` | 启动缓冲期期间失败不计入次数 | 0s |
| `--retries` | 连续失败多少次标记为 unhealthy | 3 |
---
## 屏蔽健康检查
如果基础镜像定义了 HEALTHCHECK但你不想使用它
```docker
FROM my-base-image
HEALTHCHECK NONE
```
构建好了后我们启动一个容器
---
```bash
$ docker run -d --name web -p 80:80 myweb:v1
## 常见检查脚本
### HTTP 服务
使用 `curl` `wget`
```docker
# 使用 curl
HEALTHCHECK CMD curl -f http://localhost/ || exit 1
# 使用 wget (Alpine 默认包含)
HEALTHCHECK CMD wget -q --spider http://localhost/ || exit 1
```
当运行该镜像后可以通过 `docker container ls` 看到最初的状态为 `(health: starting)`
### 数据库
```bash
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
03e28eb00bd0 myweb:v1 "nginx -g 'daemon off" 3 seconds ago Up 2 seconds (health: starting) 80/tcp, 443/tcp web
```docker
# MySQL
HEALTHCHECK CMD mysqladmin ping -h localhost || exit 1
# Redis
HEALTHCHECK CMD redis-cli ping || exit 1
```
在等待几秒钟后再次 `docker container ls`就会看到健康状态变化为了 `(healthy)`
### 自定义脚本
```bash
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
03e28eb00bd0 myweb:v1 "nginx -g 'daemon off" 18 seconds ago Up 16 seconds (healthy) 80/tcp, 443/tcp web
```docker
COPY healthcheck.sh /usr/local/bin/
HEALTHCHECK CMD ["healthcheck.sh"]
```
如果健康检查连续失败超过了重试次数状态就会变为 `(unhealthy)`
---
为了帮助排障健康检查命令的输出包括 `stdout` 以及 `stderr`都会被存储于健康状态里可以用 `docker inspect` 来查看
## Compose 中使用
可以在 `docker-compose.yml` 中覆盖或定义健康检查
```yaml
services:
web:
image: nginx
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 1m30s
timeout: 10s
retries: 3
start_period: 40s
```
带健康检查的依赖启动
```yaml
services:
web:
depends_on:
db:
condition: service_healthy # 等待 db 变健康才启动 web
db:
image: mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
```
---
## 查看健康状态
```bash
$ docker inspect --format '{{json .State.Health}}' web | python -m json.tool
# 查看容器状态(包含健康信息)
$ docker ps
CONTAINER ID STATUS
abc123 Up 1 minute (healthy)
def456 Up 2 minutes (unhealthy)
# 查看详细健康日志
$ docker inspect --format '{{json .State.Health}}' mycontainer | jq
{
"FailingStreak": 0,
"Log": [
{
"End": "2016-11-25T14:35:37.940957051Z",
"ExitCode": 0,
"Output": "<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\n body {\n width: 35em;\n margin: 0 auto;\n font-family: Tahoma, Verdana, Arial, sans-serif;\n }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n",
"Start": "2016-11-25T14:35:37.780192565Z"
}
],
"Status": "healthy"
"Status": "healthy",
"FailingStreak": 0,
"Log": [
{
"Start": "...",
"End": "...",
"ExitCode": 0,
"Output": "..."
}
]
}
```
---
## 最佳实践
### 1. 避免副作用
健康检查会被频繁执行不要在检查脚本中进行写操作或消耗大量资源的操作
### 2. 使用轻量级工具
优先使用镜像中已有的工具 `wget`避免为了健康检查安装庞大的依赖 `curl`
### 3. 设置合理的 Start Period
应用启动可能需要时间 Java 应用设置 `--start-period` 可以防止在启动阶段因检查失败而误判
```docker
# 给应用 1 分钟启动时间
HEALTHCHECK --start-period=60s CMD curl -f http://localhost/ || exit 1
```
### 4. 只检查核心依赖
健康检查应主要关注**当前服务**是否可用而不是检查其下游依赖数据库等下游依赖的检查应由应用逻辑处理
---
## 本章小结
| 要点 | 说明 |
|------|------|
| **作用** | 检测容器应用是否真实可用 |
| **命令** | `HEALTHCHECK [选项] CMD command` |
| **状态** | starting, healthy, unhealthy |
| **Compose** | 支持 `condition: service_healthy` 依赖 |
| **注意** | 避免副作用节省资源 |
## 延伸阅读
- [CMD 容器启动命令](cmd.md)启动主进程
- [Compose 模板文件](../../compose/compose_file.md)Compose 中的健康检查
- [Docker 调试](../../appendix/debug.md)容器排障