Fork me on GitHub

版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | https://vearne.cc

起因:

本文受到参考资料1的启发
我们线上的服务有不少都是部署在docker中,部署涉及的机器多大几十台,
服务发布时,要求前一个版本的容器必须优雅的退出。
docker容器中的进程是一个任务消费者。不断得从任务队列中取任务,然后进行执行(执行时间较长)

假定docker容器的name为test_v1
docker容器中的进程名为atm
也就是说不能简单的

docker rm -vf test_v1
docker run -d --name test_v1 test:v1

解决方案

1)方案1

首先,我想到的办法是在docker容器外部使用

ps -ef| grep atm |grep -v grep |awk '{print $2}'|xargs kill -15

容器中的进程捕获 SIGTERM 信号,优雅的退出,发现所有容器中的进程都退出后再执行发布逻辑

缺点:
这个方案肯定是没有问题的,但是如果进程同名,很有可能会导致误杀,而且从docker容器外部,传信号给容器内部的进程,感觉有点奇怪

2)方案2

幸运的看到了参考资料1

root@xxxx:~/test/docker/test_dk$ docker stop --help

Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]

Stop a running container by sending SIGTERM and then SIGKILL after a
grace period

  --help=false       Print usage
  -t, --time=10      Seconds to wait for stop before killing it

在docker stop命令执行的时候,会先向容器中PID为1的进程发送系统信号SIGTERM,然后等待容器中的应用程序终止执行,如果等待时间达到设定的超时时间,或者默认的10秒,会继续发送SIGKILL的系统信号强行kill掉进程。在容器中的应用程序,可以选择忽略和不处理SIGTERM信号,不过一旦达到超时时间,程序就会被系统强行kill掉,因为SIGKILL信号是直接发往系统内核的,应用程序没有机会去处理它。在使用docker stop命令的时候,我们唯一能控制的是超时时间,比如设置为20秒超时:

docker stop --time=20 container_name

既然docker 提供了stop命令,那完全可以利用stop命令来实现整个服务的优雅退出
整个demo
https://github.com/vearne/graceful_docker

1. 构建测试docker image
docker pull ubuntu:14.04
sh build.sh
2. 程序入口文件
#!/bin/sh
# 实际运行的工作程序
# my test program
nohup python /data/atm.py &

prog_exit()
{
    ps -ef| grep atm |grep -v grep |awk '{print $2}'|xargs kill -15

}
# 注册中断处理函数
trap "prog_exit" 15

flag=1
while [ $flag -ne 0 ];do
    sleep 3;
    flag=`ps -ef| grep atm |grep -v grep | wc -l`
done;
3. 启动容器
sh deploy.sh

4. 测试是否优雅退出

root@xxx:~/test/docker/test_dk$ time docker stop -t 1000 test_v1
test_v1

real    0m15.567s
user    0m0.012s
sys 0m0.013s

test_v1正常停止

后记

这里的shell脚本也可以使用supervisor来代替
supervisor收到SIGTERM信号 后
也会向它管理的进程发送SIGTERM信号,我们可以借此来达到优雅退出的目的

参考资料:

  1. 优雅的终止docker容器

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注