docker kill signal in container process

最近做了一个 Keepalived 的 Docker 镜像,启动镜像的命令由 Shell 脚本封装了一下,因为我需要在启动镜像时自动生成 Keepalived 的配置文件。用 Shell 脚本在执行一些操作之后再启动真正的容器进程是非常普遍的做法。

我所制作的这个 Keepalived 的镜像主要用来管理 VIP 的,镜像以主机网络启动,启动之后会在主机上挂载一个 VIP,但是却发现在停止容器或停止 Docker 服务时主机上挂载的 VIP 并不能被删除。

正常情况下,停止容器里的进程和停止不在容器里面的进程所执行的操作应该是相同的。当 Keepalived 在容器外运行时,手动停止的话机器上的 VIP 被被删除掉,而在容器内部运行时停止容器 VIP 却没被删除。这个问题困扰了我很久,第二天我才想起来 Keepalived 在容器内外运行的唯一区别就是它在容器内部运行时的启动命令是一个 Shell 脚本,有可能是这个地方的原因。

然后我就查了一下 Docker 文档,当停止容器或 Docker 服务时,容器内进程确实会收到一个SIGTERM信号,但是这个信号只能发给容器里的主进程,而我做的这个 Keepalived 镜像里的主进程是一个 Shell 脚本。

接下来就比较简单了,在 Google 上搜索了一下 Shell 脚本里怎么来捕捉并处理 Kill 信号,然后修改相应脚本就可以了。最终的脚本内容如下:

#!/bin/sh

AUTO_CONFIGURE=${AUTO_CONFIGURE:-true}

function sigterm_handler() {
    local pids=$(ps -ef | awk '/ keepalived / {print $1}')
    kill -SIGTERM $pids
    wait $pids
    exit 143
}

function create_config() {
    .....
}

if [[ "$AUTO_CONFIGURE" == "true" ]]; then
    create_config
fi

trap 'kill ${!}; sigterm_handler' SIGTERM

keepalived -n -l & wait ${!}

脚本的大致逻辑是:捕捉主进程(Shell脚本)接收到的信号,然后将该信号发送给真正的业务进程,最后退出。

最后安利一下我做的这个 Keepalived 的 Docker 镜像,非常小大概只有 6M 左右并且还是 Multiarch 的,可以同时在amd64ppc64les390x等架构上运行。

镜像地址: https://hub.docker.com/r/siji/keepalived/

源码地址: https://github.com/chenzhiwei/dockerfile/tree/master/multiarch/keepalived