通过信号解决docker启动容器后Exited退出的问题

前言:

      嗯哼,前段时间公司搞Docker的那位架构师走了,我们公司用40台实体服务器做了Docker的服务组,量级不是太大,上面的容器在600个左右。 现在被遗弃的Elasticsearch和docker是兵强接手,这大神以前是百度做底层开发的,水平很nb。。。  

嗯,最近总是有网站爬取我的文章,标注下原文地址,http://xiaorui.cc   

    因为我这边有个业务是buzz爬虫相关的。 计划把几百个 proxy端和爬虫端放在docker上运行,避免以前用openstack iaas那样,浪费了虚拟os的消耗 ~     上次搞docker的时候还是在乐视,现在隔了半年再玩docker,发现很多东西都忘了…. 甚至是基本的运行的概念…. 伤不起,所以这次把遇到的docker启动后总是退出的问题描述下。   


下篇文章会写 爬虫的ip轮训技术,避免因为某个ip过度的访问某个站点,被封杀… …

这是我的docker测试环境,ubuntu 14.04,具体的docker的安装,大家可以看看我以前的文章,这里就不再复述了 !

docker images
REPOSITORY             TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
<none>                 <none>              def2e0b08cbc        About an hour ago   1.37 GB
spider                 latest              f0bec095f291        2 hours ago         614.6 MB
gitlab                 latest              bf5c375d9057        3 days ago          635.1 MB
<none>                 <none>              9cbaf023786c        3 days ago          192.8 MB
sameersbn/postgresql   latest              24a6064fa4cd        10 days ago         142.1 MB
<none>                 <none>              48648094d099        5 weeks ago         997.9 MB
<none>                 <none>              195fa62e92a8        6 weeks ago         496.4 MB
training/webapp        latest              31fa814ba25a        4 months ago        278.8 MB
..... .... ....

大家会看到,我启动了一个容器,但是查看状态是 Exited (0)

docker ps -a
CONTAINER ID        IMAGE                 COMMAND                CREATED             STATUS                      PORTS               NAMES
05ded2b898c9        test:supervisor-v1    "/bin/gorun   22 seconds ago      Exited (0) 21 seconds ago                       hungry_engelbart

如果gorun 是个python daemon写的服务端,如果run 执行,他的状态肯定是exited退出的? 为啥? docker只是能监控一个服务, 如果这个服务退出了,没有往前台扔数据的话,对于docker来说,他还是会判断你的程序是退出的。 


那么怎么解决 ~

可以在gorun里面加上一条while 1;do sleep 1;done的语句, 这样你再执行的时候,docker会锁定最后一个命令,因为最后一个命令是死循环,所以容器也就不会退出了。

那么问题来了?  

注释: docker stop命令向Docker容器进程发送SIGTERM信号。如果你想快速停止某个容器,也可以使用docker kill命令向容器进程
Usage: docker kill CONTAINER [CONTAINER…]
杀死一个正在运行的容器(发送终止信号)
Usage: docker stop [OPTIONS] CONTAINER [CONTAINER…]
关闭一个容器
-t=10: 等待停止容器需要的时间
这个容器主进程接收终止进程信号,在一段时间内,终止

接着上面的那个场景, 如果你docker stop 651ed2b898c9 停止这个容器,docker其实会运行的是给刚才的那个命令(while sleep)传递一个SIGTERM信号,他接收到后会退出。  但是和while同时执行的那几个命令就很有可能成为僵尸程序了… …因为他不是正常退出的

那么docker因为退出的问题,引起的僵尸进程问题我们该如何解决?

可以自己写个python程序,当 sudo docker run -t -i centos:centos6 /bin/gorun的时候,gorun 调用subprocess的模块调用第三方的脚本,然后取出pid进程号,紧接着做signal的信号监听。  当接受到外面进来的信号,可以是各种信号,再进行kill处理 !

shell下,也有类似的监听方式:

#!/bin/bash
#from xiaorui.cc
echo starting up

function shut_down() {
    echo shutting down
    pid=(ps -e | grep xiaorui.cc | grep -v grep| awk '{print1}')
    kill -SIGTERM pid  
}

trap "shut_down" SIGKILL SIGTERM SIGHUP SIGINT EXIT

flag=1
while [flag -ne 0 ];do
    sleep 3;
    flag=`ps -ef| grep xiaorui.cc |grep -v grep | wc -l`
done;

# xiaorui.cc

如果不想自己控制信号,那么可以用supervisord或者是monit来控制,强烈推荐使用supervisord,原因是配置相当简单,可以控制进程的数目.  另外如果你是python应用,可以使用下我写的Master Worker进程管理模块,他功能跟supervisord类似,都可以保护好真正的worker进程。

ProcessHandler进程管理模块的地址:   https://github.com/rfyiamcool/ProcessHandler

monit的是使用教程:

http://xiaorui.cc/2014/07/13/%E4%BD%BF%E7%94%A8monit%E5%AE%9E%E7%8E%B0%E8%BF%9B%E7%A8%8B%E7%9B%91%E6%8E%A7%E7%AE%A1%E7%90%86/

简单说下supervisord的用法:

编辑Dockerfile
vim Dockerfile
FROM ubuntu:14.04
MAINTAINER debugo@sina.com
RUN apt-get update
RUN apt-get install -y openssh-server supervisor
RUN mkdir -p /var/run/sshd
RUN mkdir -p /var/log/supervisor
COPY supervisord.conf /etc/supervisor/supervisord.conf
EXPOSE 22 80
CMD ["/usr/bin/supervisord"]

编辑supervisord配置文件
vim supervisord.conf

[supervisord]
nodaemon=true
[program:sshd]
command=/usr/sbin/sshd -D
autorestart=unexpected
autostart=true

Linux的Signal信号列表

kill -l
1) SIGHUP    2) SIGINT    3) SIGQUIT   4) SIGILL
5) SIGTRAP   6) SIGABRT   7) SIGBUS    8) SIGFPE
9) SIGKILL   10) SIGUSR1   11) SIGSEGV   12) SIGUSR2
13) SIGPIPE   14) SIGALRM   15) SIGTERM   17) SIGCHLD
18) SIGCONT   19) SIGSTOP   20) SIGTSTP   21) SIGTTIN
22) SIGTTOU   23) SIGURG   24) SIGXCPU   25) SIGXFSZ
26) SIGVTALRM  27) SIGPROF   28) SIGWINCH  29) SIGIO
30) SIGPWR   31) SIGSYS   34) SIGRTMIN  35) SIGRTMIN+1
36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5
40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9
44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13
52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9
56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5
60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1
64) SIGRTMAX 


2017-10-10 更新:

同事朱伟写了一个docker优雅退出的demo,用shell脚本做enterpoint,  请移步到 https://github.com/vearne/graceful_docker 



END .


大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码,感谢!
另外再次标注博客原地址  xiaorui.cc

14 Responses

  1. 上海蝎子 2015年12月3日 / 下午4:22

    -d 参数为啥不用呢,峰云大神。

  2. Docker小白 2015年11月13日 / 下午4:56

    没看明白

  3. 磊磊 2015年7月22日 / 下午12:33

    我现在用docker启动jetty 但是不知道怎么用jetty来启动一个java程序,是把java的war放进jetyy所在的容器里 是这样吗 在线等或者1078030014qq在线等您老指点

  4. 磊磊 2015年7月22日 / 下午12:31

    在吗大神求助一下 docker

  5. 沈灿 2015年1月11日 / 下午5:14

    大神你好 自从看了你的博客后 我终于找到了一个2000一月的网管工作了 感谢大神

    • 峰云 2015年1月11日 / 下午6:52

      傻逼呀,上次还4000多

      • 沈灿 2015年1月11日 / 下午9:44

        上次那个 被开了 你说的这些 都没用上

        • 峰云 2015年1月12日 / 上午10:37

          麻痹呀,没提我的名字?

          • 默默无言 2015年8月18日 / 下午12:00

            提了,给的更少了

          • 传说 2016年11月15日 / 下午2:17

            看来你是没有提我的名字

  6. 挖墙脚 2015年1月9日 / 下午10:13

    峰云来我们这里吧

磊磊进行回复 取消回复

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