nginx 容器的 Docker 健康检查

wmo*_*ell 6 nginx docker docker-compose

我有一个使用nginx来自 Docker Hub的官方docker 容器的项目,通过 Docker Compose 启动。我在 Docker Compose 中为我的每个容器配置了健康检查,最近这个nginx容器的健康检查表现得很奇怪;在启动时docker-compose up -d,我的所有容器都启动并开始运行健康检查,但nginx容器看起来从未运行过健康检查。如果我docker exec进入容器,我可以手动运行脚本就好了,如果我重新启动容器,健康检查会正常运行。

示例输出docker ps

CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS                            PORTS                                                                       NAMES
458a55ae8971        my_custom_image           "/tini -- /usr/local…"   7 minutes ago       Up 7 minutes (healthy)                                                                                        project_worker_1
5024781b1a73        redis:3.2                 "docker-entrypoint.s…"   7 minutes ago       Up 7 minutes (healthy)            127.0.0.1:6379->6379/tcp                                                    project_redis_1
bd405dde8ce7        postgres:9.6              "docker-entrypoint.s…"   7 minutes ago       Up 7 minutes (healthy)            127.0.0.1:15432->5432/tcp                                                   project_postgres_1
93e15c18d879        nginx:mainline            "nginx -g 'daemon of…"   7 minutes ago       Up 7 minutes (health: starting)   127.0.0.1:80->80/tcp, 127.0.0.1:443->443/tcp                                nginx
Run Code Online (Sandbox Code Playgroud)

示例(部分,为简洁起见)输出docker inspect nginx

    "State": {
        "Status": "running",
        "Running": true,
        "Paused": false,
        "Restarting": false,
        "OOMKilled": false,
        "Dead": false,
        "Pid": 11568,
        "ExitCode": 0,
        "Error": "",
        "StartedAt": "2018-02-13T21:04:22.904241169Z",
        "FinishedAt": "0001-01-01T00:00:00Z",
        "Health": {
            "Status": "unhealthy",
            "FailingStreak": 0,
            "Log": []
        }
    },
Run Code Online (Sandbox Code Playgroud)

docker-compose.yml定义nginx容器的部分:

nginx:
  image: nginx:mainline
  # using container_name means there will only ever be one nginx container!
  container_name: nginx
  restart: always
  networks:
    - proxynet
  volumes:
    - /etc/nginx/conf.d
    - /etc/nginx/vhost.d
    - /usr/share/nginx/html
    - tlsdata:/etc/nginx/certs:ro
    - attachdata:/usr/share/nginx/html/uploads:ro
    - staticdata:/usr/share/nginx/html/static:ro
    - ./nginx/healthcheck.sh:/bin/healthcheck.sh
  healthcheck:
    test: ['CMD', '/bin/healthcheck.sh']
    interval: 1m
    timeout: 5s
    retries: 3
  ports:
    # Make the http/https ports available on the Docker host IPv4 loopback interface
    - '127.0.0.1:80:80'
    - '127.0.0.1:443:443'
Run Code Online (Sandbox Code Playgroud)

healthcheck.sh我装在一个体积:

#!/bin/bash

service nginx status || exit 1
Run Code Online (Sandbox Code Playgroud)

看起来问题只是一个问题,当容器最初启动时,systemd 从不从状态检查中返回,同时配置的健康检查超时不会触发。其他一切正常,并且nginx正在启动并响应,但是如果每次启动时无需手动重新启动,健康检查可以正常运行会很好。

我的配置中是否缺少某些内容,或者我可以运行更好的检查?

Kam*_*ski 11

对于官方的 alpine nginx 镜像,你也可以这样做:

healthcheck:
      test: ["CMD-SHELL", "wget -O /dev/null http://localhost || exit 1"]
      timeout: 10s
Run Code Online (Sandbox Code Playgroud)

wget 是标准映像的一部分。它的作用是将你的index.html/php/whatever 下载到任何地方(/dev/null),否则它应该超时并失败。

  • @TheGodfather 我目前正在使用 nginx:1.19-alpine 最小图像,它包含 wget 以及curl。我使用 `test: ["CMD-SHELL", "curl -so /dev/null http://localhost/ || exit 1"]` 实现了我的测试,它工作正常,但是 wget 显示了我(连接被拒绝) 。 (3认同)
  • 一旦您知道 Alpine 是基于 Busybox 的,这并不奇怪,Busybox 是一个单一的二进制文件,也是以其名称命名的工具。听起来很奇怪?在 Alpine 上尝试“ls -l /usr/bin/”。;) (3认同)
  • 小澄清:官方的 `nginx:latest` 镜像不包含 wget,而 `nginx:alpine` 包含它(这很奇怪,通常人们会期望 alpine 以某种方式成为完整镜像的“子集”) (2认同)

pau*_*rio 8

我认为在这种情况下不需要自定义脚本。

尝试将您的健康检查测试更改为

test: ["CMD", "service", "nginx", "status"]
Run Code Online (Sandbox Code Playgroud)

这对我来说很好用。

尝试使用"而不是',以防万一:)

编辑

如果你真的想强制一个exit 1,万一失败,你可以使用:

test: service nginx status || exit 1
Run Code Online (Sandbox Code Playgroud)

  • 顺便说一句,这不适用于 `alpine` 版本,因为 `service` 不是命令。 (3认同)

小智 5

我尝试了相同的脚本并遇到了相同的问题。我改为改为这样healthcheck.sh运行:

#!/bin/bash

if service nginx status; then
    exit 0
else
    exit 1
fi
Run Code Online (Sandbox Code Playgroud)

在 docker 容器中运行它会成功进行健康检查。


wmo*_*ell 4

一年多后,我找到了解决方案。首先,对环境、我认为正在发生的事情以及对 Docker 引擎可能存在的错误的猜测进行额外的澄清。

我现在使用的 Compose 文件正在启动“官方”Alpine NGINX 映像的轻微修改版本,该映像用于COPY加载健康检查脚本并HEALTHCHECK显式添加到映像中。该镜像用于服务,并与运行jwilder/docker-gen 的nginx镜像配合使用,以使用 Docker 中的容器元数据生成 NGINX 配置文件。该容器作为名为 的服务运行。当容器更改时,会重新生成配置,如果有任何更改,则会将 a发送到服务。nginx-genSIGHUPnginx

我的发现如下:

  • 如果所有服务一起启动,则该nginx服务永远不会运行运行状况检查;
  • 如果nginx服务在启动后立即重新启动,则健康检查正常完成;
  • 如果nginx服务自行启动,则健康检查正常完成;
  • 如果除此之外的所有服务nginx-gen一起启动,则运行状况检查正常完成;
  • 如果所有服务一起启动,但nginx-gen修改为sleep 60在执行任何操作之前,健康检查会正常完成;

因此,信号处理、Docker 和 NGINX 之间似乎存在一些模糊的交互。如果在容器中运行第一个运行状况检查之前SIGHUP将 a 发送到容器中的 NGINX 进程,则不会运行任何运行状况检查。

我提出的最后一次迭代修改了nginx-gen容器以轮询容器的运行状况nginx。它在循环中查找具有定义标签的容器的健康状态,带有短sleep. 一旦nginx容器报告健康,nginx-gen就会继续生成配置文件。我还将通知方法更改为docker exec脚本,以显式测试并重新加载nginx容器中的配置,而不是依赖于SIGHUP.

最终结果:我可以docker-compose up -d,一切最终都会报告,healthy无需进一步干预。成功!