docker容器的health健康状态检查

Docker 原生健康检查能力

自 1.12 版本之后,Docker 引入了原生的健康检查实现。对于容器而言,最简单的健康检查是进程级的健康检查,即检验进程是否存活。Docker Daemon会自动监控容器中的PID 1进程,如果docker run命令中指明了restart policy,可以根据策略自动重启已结束的容器。在很多实际场景下,仅使用进程级健康检查机制还远远不够。比如,容器进程虽然依旧运行却由于应用死锁无法继续响应用户请求,这样的问题是无法通过进程监控发现的。

当一个容器有指定健康检查 (HEALTHCHECK) 时,它除了普通的容器状态之外,还有以下几种状态:

  • starting:容器仍在启动时的初始状态
  • healthy:如果命令成功,则容器是健康的
  • unhealthy:如果单次运行时间超过指定的超时时间,则认为它不健康。如果运行状况检查失败,则将运行重试次数,如果仍然失败,将被宣布为不健康。

容器启动之后,初始状态会为 starting (启动中)。Docker Engine会等待 interval 时间,开始执行健康检查命令,并周期性执行。如果单次检查返回值非0或者运行需要比指定 timeout 时间还长,则本次检查被认为失败。如果健康检查连续失败超过了 retries 重试次数,状态就会变为 unhealthy (不健康)。

  1. 一旦有一次健康检查成功,Docker会将容器置回 healthy (健康)状态
  2. 当容器的健康状态发生变化时,Docker Engine会发出一个 health_status 事件。通过检查容器监控状态有以下两种方式:

1. Dockerfile 方式

可以在Dockerfile中声明应用自身的健康检测配置。HEALTHCHECK指令声明了健康检测命令,用这个命令来判断容器主进程的服务状态是否正常,从而比较真实的反应容器实际状态。

HEALTHCHECK指令格式:

  • HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令
  • HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉

:在Dockerfile中HEALTHCHECK只可以出现一次,如果写了多个,只有最后一个生效。

使用包含HEALTHCHECK指令的Dockerfile构建出来的镜像,在实例化Docker容器的时候,就具备了健康状态检查的功能。启动容器后会自动进行健康检查。参考:healthcheck

HEALTHCHECK 支持下列选项:

  • --interval=<间隔>:两次健康检查的间隔,默认为 30 秒;
  • --timeout=<间隔>:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;
  • --retries=<次数>:当连续失败指定次数后,则将容器状态视为 unhealthy,默认 3 次。
  • --start-period=<间隔>: 应用的启动的初始化时间,在启动过程中的健康检查失效不会计入,默认 0 秒;

参数作用解释如下:

  • 运行状态检查首先会在容器启动后的 interval 秒内运行,然后在前一次检查完成后的 interval 秒内再次运行。
  • 如果一次状态检查花费的时间超过 timeout 秒,则认为这次检查失败。
  • 容器的运行状态检查连续失败 retries 次才会被视为不健康。
  • start period 为需要时间启动的容器提供初始化时间。在此期间的探测失败将不计入最大重试次数。

但是,如果在启动期间健康检查成功,则认为容器已启动,所有连续失败的情况都将计算到最大重试次数。

HEALTHCHECK [选项] CMD后面的命令,格式和ENTRYPOINT一样,分为 shell 格式,和 exec 格 式。命令的返回值决定了该次健康检查的成功与否:

  • 0:成功;
  • 1:失败;
  • 2:保留值,不要使用

假设有个镜像是个最简单的 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\n

Welcome to nginx!

\n

If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.

\n\n

For 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\n

Thank 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\n

Welcome to nginx!

\n

If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.

\n\n

For 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\n

Thank you for using nginx.

\n\n\n"
, "Start": "2022-08-20T14:02:43.200932585+08:00" } ], "Status": "healthy" }

2. docker run 方式

另外一种方法是在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)

3. docker-compose 方式

在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

你可能感兴趣的:(operations,docker,容器,运维)