自 1.12 版本之后,Docker 引入了原生的健康检查实现。对于容器而言,最简单的健康检查是进程级的健康检查,即检验进程是否存活。Docker Daemon会自动监控容器中的PID 1进程,如果docker run
命令中指明了restart policy
,可以根据策略自动重启已结束的容器。在很多实际场景下,仅使用进程级健康检查机制还远远不够。比如,容器进程虽然依旧运行却由于应用死锁无法继续响应用户请求,这样的问题是无法通过进程监控发现的。
当一个容器有指定健康检查 (HEALTHCHECK) 时,它除了普通的容器状态之外,还有以下几种状态:
starting
:容器仍在启动时的初始状态healthy
:如果命令成功,则容器是健康的unhealthy
:如果单次运行时间超过指定的超时时间,则认为它不健康。如果运行状况检查失败,则将运行重试次数,如果仍然失败,将被宣布为不健康。容器启动之后,初始状态会为 starting (启动中)。Docker Engine会等待 interval 时间,开始执行健康检查命令,并周期性执行。如果单次检查返回值非0或者运行需要比指定 timeout 时间还长,则本次检查被认为失败。如果健康检查连续失败超过了 retries 重试次数,状态就会变为 unhealthy (不健康)。
注:
可以在Dockerfile中声明应用自身的健康检测配置。HEALTHCHECK
指令声明了健康检测命令,用这个命令来判断容器主进程的服务状态是否正常,从而比较真实的反应容器实际状态。
HEALTHCHECK
指令格式:
HEALTHCHECK [选项] CMD <命令>
:设置检查容器健康状况的命令HEALTHCHECK NONE
:如果基础镜像有健康检查指令,使用这行可以屏蔽掉注 :在Dockerfile中HEALTHCHECK
只可以出现一次,如果写了多个,只有最后一个生效。
使用包含HEALTHCHECK
指令的Dockerfile构建出来的镜像,在实例化Docker容器的时候,就具备了健康状态检查的功能。启动容器后会自动进行健康检查。参考:healthcheck
HEALTHCHECK 支持下列选项:
--interval=<间隔>
:两次健康检查的间隔,默认为 30 秒;--timeout=<间隔>
:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;--retries=<次数>
:当连续失败指定次数后,则将容器状态视为 unhealthy,默认 3 次。--start-period=<间隔>
: 应用的启动的初始化时间,在启动过程中的健康检查失效不会计入,默认 0 秒;参数作用解释如下:
但是,如果在启动期间健康检查成功,则认为容器已启动,所有连续失败的情况都将计算到最大重试次数。
在HEALTHCHECK [选项] CMD
后面的命令,格式和ENTRYPOINT
一样,分为 shell 格式,和 exec 格 式。命令的返回值决定了该次健康检查的成功与否:
假设有个镜像是个最简单的 Web 服务,我们希望增加健康检查来判断其 Web 服务是否在正常工作,我们可以用 curl来帮助判断,其 Dockerfile 的HEALTHCHECK
可以这么写:
FROM nginx:1.23
HEALTHCHECK --interval=5s --timeout=3s --retries=3 \
CMD curl -fs http://localhost/ || exit 1
这里设置了每 5 秒检查一次(这里为了试验所以间隔非常短,实际应该相对较长),如果健康检查命令超过 3 秒没响应,并且重试3次都没响应就视为失败,并且使用curl -fs http://localhost/ || exit 1
作为健康检查命令。
使用docker build
来构建这个镜像:
docker build -t myweb:v1 .
构建好后启动容器:
docker run -d --name web myweb:v1
当运行该镜像后,可以通过docker container ls
看到最初的状态为(health: starting)
:
docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7068d793c6e4 myweb:v1 "/docker-entrypoint.…" 3 seconds ago Up 2 seconds (health: starting) 80/tcp web
在等待几秒钟后,再次docker container ls
,就会看到健康状态变化为了(healthy)
:
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7068d793c6e4 myweb:v1 "/docker-entrypoint.…" 18 seconds ago Up 16 seconds (healthy) 80/tcp web
如果健康检查连续失败超过了重试次数,状态就会变为(unhealthy)
。
为了帮助排障,健康检查命令的输出(包括stdout
以及stderr
)都会被存储于健康状态里,可以用 docker inspect
来查看。
$ docker inspect --format '{{json .State.Health}}' web | python -m json.tool
{
"FailingStreak": 0,
"Log": [
{
"End": "2022-08-20T14:02:38.19224648+08:00",
"ExitCode": 0,
"Output": "\n\n\nWelcome to nginx! \n\n\n\nWelcome to nginx!
\nIf you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.
\n\nFor online documentation and support please refer to\n\"
http://nginx.org/\">nginx.org.
\nCommercial support is available at\n\"http://nginx.com/\">nginx.com.\n\nThank you for using nginx.
\n\n\n",
"Start": "2022-08-20T14:02:38.116041192+08:00"
},
{
"End": "2022-08-20T14:02:43.271105619+08:00",
"ExitCode": 0,
"Output": "\n\n\nWelcome to nginx! \n\n\n\nWelcome to nginx!
\nIf you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.
\n\nFor online documentation and support please refer to\n\"
http://nginx.org/\">nginx.org.
\nCommercial support is available at\n\"http://nginx.com/\">nginx.com.\n\nThank you for using nginx.
\n\n\n",
"Start": "2022-08-20T14:02:43.200932585+08:00"
}
],
"Status": "healthy"
}
另外一种方法是在docker run
命令中,直接指明healthcheck相关策略:
$ docker run -d \
--name=myweb \
--health-cmd="curl -fs http://localhost/ || exit 1" \
--health-interval=5s \
--health-retries=12 \
--health-timeout=2s \
nginx:1.23
通过执行docker run --help | grep health
命令查看相关的参数及解释如下:
--health-cmd string
:运行检查健康状况的命令--health-interval duration
:运行间隔时间(ms|s|m|h)(缺省为0s)--health-retries int
:需要报告不健康的连续失败次数--health-start-period duration
:容器在开始健康重试倒计时之前初始化的起始周期(ms|s|m|h)(默认0)--health-timeout duration
:允许一次检查运行的最大时间(ms|s|m|h)(默认为0s)--no-healthcheck
:禁用任何容器指定的HEALTHCHECK
,会使得Dockerfile构建出来的HEALTHCHECK
功能失效。如果是以supervisor来管理容器的多个服务,想通过子服务的状态来判断容器的监控状态,可以使用supervisorctl status
来做判断,比如:
$ docker run --rm -d \
--name=myweb \
--health-cmd="supervisorctl status" \
--health-interval=5s \
--health-retries=3 \
--health-timeout=2s \
nginx:v1
按照此参数的设置,如果supervisorctl status
检查子服务有一个不为正常的RUNNING
状态,那么在等待大约15秒左右,容器的健康状态就会从(healthy)
变为(unhealthy)
。
在docker-compose中,healthcheck
声明运行该检查以确定此服务的容器是否“正常”。这将覆盖由服务的 Docker 映像设置的 HEALTHCHECK Dockerfile 指令。参考:healthcheck
以通过supervisor管理子进程的容器为例:
version: '3.8'
services:
web:
image: nginx:v1
container_name: web
healthcheck:
test: ["CMD", "supervisorctl", "status"]
interval: 5s
timeout: 2s
retries: 3
start_period: 10s
注意:start_period
参数,在Version 2.3和Version 3.4中引入,如果在yml文件中的开头写的是version: ‘3’,这相当于是3.0版本,会导致启动失败,出现类似信息:services.web.healthcheck value Additional properties are not allowed ('start_period' was unexpected)
。因此需要把docker-compose的version指定2.3或者3.4之后的版本。Compose版本及Docker Engine的兼容版本对应关系参考:compatibility-matrix
执行成功后,等待数秒查询容器的状态:
$ docker-compose ps
Name Command State Ports
--------------------------------------------------------------------------------
web supervisord -c /etc/superv ... Up (healthy) 443/tcp, 80/tcp
当通过手动supervisorctl stop
停掉里面的一些子服务,导致里面的子服务状态不全为RUNNING
状态时,再查看容器的状态:
$ docker-compose ps
Name Command State Ports
----------------------------------------------------------------------------------
web supervisord -c /etc/superv ... Up (unhealthy) 443/tcp, 80/tcp
容器的健康状态也会从(healthy)
变为(unhealthy)
。
也可以通过设置disable: true
来禁用镜像设置的运行状况检查:
healthcheck:
disable: true