docker 优雅关闭容器

Docker 容器停止过程

对于容器来说,init 系统不是必须的,当你通过命令 docker stop mycontainer 来停止容器时,docker CLI 会将 TERM 信号发送给 mycontainer 的 PID 为 1 的进程。

  • 如果 PID 1 是 init 进程 – 那么 PID 1 会将 TERM 信号转发给子进程,然后子进程开始关闭,最后容器终止。
  • 如果没有 init 进程 – 那么容器中的应用进程(Dockerfile 中的 ENTRYPOINTCMD 指定的应用)就是 PID 1,应用进程直接负责响应 TERM 信号。这时又分为两种情况:

应用不处理 SIGTERM – 如果应用没有监听 SIGTERM 信号,或者应用中没有实现处理 SIGTERM 信号的逻辑,应用就不会停止,容器也不会终止。

容器停止时间很长 – 运行命令 docker stop mycontainer 之后,Docker 会等待 10s,如果 10s 后容器还没有终止,Docker 就会绕过容器应用直接向内核发送 SIGKILL,内核会强行杀死应用,从而终止容器。

Dockerfile中ENTRYPOINT/CMD 的几种写法, 会影响 pid 1 进程的产生:

写法1:
“shell” format 的 ENTRYPOINT/CMD, 不带方括号:
ENTRYPOINT top -b
#PID 1 是 /bin/sh -c shell  top -b
#另外有个 pid 7 是 top -b

写法2:
“shell” format 的 ENTRYPOINT/CMD, 不带方括号, 但这次ENTRYPOINT后紧跟了一个 exec :
ENTRYPOINT exec top -b
#PID 1 是 top -b

写法3:
“exec” form 的 ENTRYPOINT/CMD, 方括号括着, 每个部分都是json字符串.
ENTRYPOINT [“top”,”-b”]
pid 1 进程就是 top -b

所以推荐使用”exec” form的命令, 而不是 “shell” 形式的命令.

init 进程调整方案

方案1: 自行确保 pid 1 是我们的java程序.
上面的 Dockerfile 可以确保 java 程序作为 pid 1进程.

方案评价: 有时候不太容易将我们的主程序调整为 pid 1 进程, 另外虽然 docker 容器推荐是单进程, 但实际情形往往不是这么理想. 本方案仅仅适合单进程容器.

方案2: 适合于 Docker 1.13 以上.
Docker 1.13以上的docker run 命令新增了 –init 参数, 加了该参数后, docker 会启用 tini 作为 init (pid 1) 进程, 该 tini 进程能够将终止信号转发给其子进程, 同时能reap 子进程, 不会出现因孤儿进程导致的线程句柄无法回收情形.

方案3(推荐): 在docker镜像中强制 tini 作为 init(pid 1) 进程, 该方案使用范围广, ENTRYPOINT 可以是任意sh脚本文件.

# Add Tini
ENV TINI_VERSION v0.18.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/local/bin/tini
RUN chmod +x /usr/local/bin/tini

ENTRYPOINT ["/usr/local/bin/tini", "--", "/docker-entrypoint.sh"]

参考文献:

https://xie.infoq.cn/article/11d413217d5186feed013122e

https://bbs.huaweicloud.com/blogs/158125

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