基于 healthcheck 重启一个不健康的 docker 容器
Posted
技术标签:
【中文标题】基于 healthcheck 重启一个不健康的 docker 容器【英文标题】:Restarting an unhealthy docker container based on healthcheck 【发布时间】:2021-11-08 21:45:33 【问题描述】:我正在使用Docker version 17.09.0-ce
,我看到容器被标记为不健康。是否有一个选项可以让容器重新启动,而不是让容器保持不健康状态?
【问题讨论】:
我认为当您以 docker swarm 模式启动它并将其作为服务而不是普通 docker 容器运行时会发生这种情况 出于某些特定原因,我没有使用 swarm 或任何编排工具。 然后你可以使用另一个脚本使用docker events -f event=health_status
(docs.docker.com/engine/reference/commandline/events/…) 然后根据容器的健康状况采取行动
【参考方案1】:
不健康容器的重启功能在最初的 PR (https://github.com/moby/moby/pull/22719) 中,但在讨论后被删除,并考虑在以后作为 RestartPolicy 的增强功能完成。
此时您可以使用此解决方法来自动重启不正常的容器:https://hub.docker.com/r/willfarrell/autoheal/
这是一个示例撰写文件:
version: '2'
services:
autoheal:
restart: always
image: willfarrell/autoheal
environment:
- AUTOHEAL_CONTAINER_LABEL=all
volumes:
- /var/run/docker.sock:/var/run/docker.sock
只需在此执行docker-compose up -d
【讨论】:
这不会让您无法指定自己的图像名称吗?还需要环境或体积吗?音量似乎无关紧要。您可以将此图像用作依赖项吗? @obesechicken13 音量使得 docker 套接字可用于内部正在运行的容器 存在巨大安全风险news.ycombinator.com/item?id=17983623 还有一个开放的功能请求:trigger restart from unhealthy status @InsOp 那么你有什么建议呢?如果不授予容器对 Docker 的访问权限,如何做到这一点?【参考方案2】:您可以通过设置智能 HEALTHCHECK 和正确的重启策略来自动重启不健康的容器。
Docker 重启策略应该是always
或unless-stopped
之一。
HEALTHCHECK 应该实现一个逻辑,在容器不健康时将其杀死。
在以下示例中,我使用了 curl
及其内部重试机制,并将其(在失败/服务不健康的情况下)通过管道传送到 kill
命令。
HEALTHCHECK --interval=5m --timeout=2m --start-period=45s \
CMD curl -f --retry 6 --max-time 5 --retry-delay 10 --retry-max-time 60 "http://localhost:8080/health" || bash -c 'kill -s 15 -1 && (sleep 10; kill -s 9 -1)'
这里要理解的重要一步是curl
命令中自带重试逻辑,这里的Docker重试其实是强制的但是没用。那么如果curl
HTTP 请求失败 3 次,则执行kill
。首先它向容器中的所有进程发送一个 SIGTERM,让它们优雅地停止,然后在 10 秒后它发送一个 SIGKILL 来完全杀死容器中的所有进程。需要注意的是,当一个容器的PID1死了,那么容器本身就死了,并调用了重启策略。
kill
文档:https://linux.die.net/man/1/kill
curl
文档:https://curl.haxx.se/docs/manpage.html
docker
重启文档:https://docs.docker.com/compose/compose-file/compose-file-v2/#restart
陷阱:kill
在 bash 中的行为与在 sh 中的行为不同。在 bash 中,您可以使用-1
通知所有 PID 大于 1 的进程终止。
【讨论】:
很酷,谢谢你的解释?假设容器已经重新启动,但问题无法解决......它不是在无限循环中结束吗? 是的,它进入了一个无限循环。阻止它的唯一方法是通过docker compose stop
或docker compose rm -f
。有一个超级复杂的替代方法可以解决此问题。即:在容器内挂载 Docker 套接字,在容器内的 .sh
文件中实现重试逻辑,在卷上写入计数器以使其持久化,当计数器大于您的尝试次数时,使用 docker 套接字向容器本身发送stop
消息。 :)
或者:不要依赖docker重启机制,用什么东西作为supervisord
docker healthcheck 有retries
选项
@cdalxndr healthcheck retries
的目的与 @naramsim 在 cmets 中解释的目的不同。【参考方案3】:
对于独立容器,Docker 没有原生集成来在健康检查失败时重新启动容器,尽管我们可以使用 Docker 事件和脚本来实现相同的目的。健康检查更好地与 Swarm 集成。通过将健康检查集成到 Swarm 中,当服务中的容器不健康时,Swarm 会自动关闭不健康的容器并启动一个新容器以保持服务副本计数中指定的容器计数。
【讨论】:
【参考方案4】:您可以尝试在您的 Dockerfile 中添加如下内容:
HEALTHCHECK --interval=5s --timeout=2s CMD curl --fail http://localhost || kill 1
不要忘记--restart always
选项。
kill 1
将杀死容器中 pid 1 的进程并强制容器退出。通常由 CMD 或 ENTRYPOINT 启动的进程的 pid 为 1。
不幸的是,这种方法可能不会将容器的状态更改为不健康,所以要小心。
【讨论】:
这不是在第一次失败的 curl 时杀死容器吗? @Naramsim 你说得对,第一次 curl 失败会杀死容器,但是 --restart 总是会复活。此外,正如文档所说,“健康检查将在容器启动后的间隔秒内首先运行”。因此,如果容器启动时间过长,增加间隔将防止第一次快速失败。 我用正确的方法重新启动了一个不健康的容器来回答这个问题。【参考方案5】:Docker 有几种方法可以获取有关容器运行状况的详细信息。您可以配置运行状况检查及其运行频率。此外,运行状况检查可以在容器内运行的应用程序上运行,例如 http(这将使用 curl --fail
选项。)您可以查看 health_status
事件以获取详细信息。
有关不健康容器的详细信息,检查命令会派上用场,docker inspect --format='json .State.Health' container-name
(有关详细信息,请参阅https://blog.newrelic.com/2016/08/24/docker-health-check-instruction/。)
您应该首先解决导致“不健康”标签的错误条件(无论何时运行健康检查命令并获得退出代码 1)。这可能需要也可能不需要 Docker 重新启动容器,具体取决于错误。如果您正在启动/重新启动 containers automatically,那么捕获启动错误或记录它们以及运行状况检查状态可以帮助快速解决错误。如果您对自动启动感兴趣,请查看链接。
【讨论】:
如果你在 Windows 上,你将不得不使用双引号,否则它将不起作用:docker inspect --format="json .State.Health" name-of-your-container
【参考方案6】:
根据https://codeblog.dotsandbrackets.com/docker-health-check/
创建容器并添加“restart:always”。
在使用healthcheck时,要注意以下几点:
对于独立容器,Docker 没有原生集成来在健康检查失败时重新启动容器,尽管我们可以使用 Docker 事件和脚本来实现相同的目的。健康检查更好地与 Swarm 集成。通过将健康检查集成到 Swarm 中,当服务中的容器不健康时,Swarm 会自动关闭不健康的容器并启动一个新容器以保持服务副本计数中指定的容器计数。
【讨论】:
请将您的所有答案放在回复中,而不是链接中。 我解决了这个问题。在使用健康检查时,请注意以下几点 对于独立容器,Docker 没有原生集成来在健康检查失败时重启容器,尽管我们可以使用 Docker 事件和脚本来实现。健康检查更好地与 Swarm 集成。通过将健康检查集成到 Swarm 中,当服务中的容器不健康时,Swarm 会自动关闭不健康的容器并启动一个新容器以保持服务副本计数中指定的容器计数。以上是关于基于 healthcheck 重启一个不健康的 docker 容器的主要内容,如果未能解决你的问题,请参考以下文章
容器HEALTHCHECK指令对接ASP.NET Core健康检查能力