美文网首页Docker容器
docker --init 与多进程容器

docker --init 与多进程容器

作者: 舞动的痞老板 | 来源:发表于2019-12-10 16:09 被阅读0次

    目录

    1、多进程的启动
    2、docker --init
    3、附录


    容器运行需要一个前台进程来夯住容器,否则容器会被视为死掉。

    docker容器的主进程是Dokcerfile文件末尾ENTRYPOINT 或者CMD中定义的。一般情况下服务拆分推荐使用多个容器,每个容器一个进程。但是服务自身有时也需要fork出多个进程(比如Apache web server需要开启多个worker进程)。容器本身也是支持多进程的,但是想要获得容器带来的最大好处,就要避免一个容器对一个应用的多个维度负责。你可以使用多个容器,用自定义的网络和共享数据卷将他们联系起来。
    容器的主进程负责管理它启动的所有的主进程。但是在一些情况下主进程模式不是一个好的设计,也不能够及时处理(停止)reaping 重启子进程当容器退出的时候。如果你的进程遇到这种情况,启动容器你可以使用 --init 参数。--init标志会插入一个小的进程作为主进程,当容器退出时这个小进程会负责处理所有的进程的重启。解决这些进程启停最好的方式是设置一个上层的进程来统一处理这些进程的声明周期。比较稳定的初始进程有sysvinitupstartsystemd


    1、多进程的使用


    方式一: 用script脚本启动程序

    使用脚本启动每个service,CMD中启动一个总脚本来启动所有服务

    #!/bin/bash
    
    # Start the first process 启动第一个
    ./my_first_process -D 
    status=$?
    if [ $status -ne 0 ]; then
      echo "Failed to start my_first_process: $status"
      exit $status
    fi
    
    # Start the second process 启动第二个
    ./my_second_process -D
    status=$?
    if [ $status -ne 0 ]; then
      echo "Failed to start my_second_process: $status"
      exit $status
    fi
    
    # Naive check runs checks once a minute to see if either of the processes exited.
    # This illustrates part of the heavy lifting you need to do if you want to run
    # more than one service in a container. The container exits with an error
    # if it detects that either of the processes has exited.
    # Otherwise it loops forever, waking up every 60 seconds
    
    while sleep 60; do
      ps aux |grep my_first_process |grep -q -v grep
      PROCESS_1_STATUS=$?
      ps aux |grep my_second_process |grep -q -v grep
      PROCESS_2_STATUS=$?
      # If the greps above find anything, they exit with 0 status
      # If they are not both 0, then something is wrong
      if [ $PROCESS_1_STATUS -ne 0 -o $PROCESS_2_STATUS -ne 0 ]; then
        echo "One of the processes has already exited."
        exit 1
      fi
    done
    

    Dockerfile

    FROM ubuntu:latest
    COPY my_first_process my_first_process
    COPY my_second_process my_second_process
    COPY my_wrapper_script.sh my_wrapper_script.sh
    CMD ./my_wrapper_script.sh
    

    方式二 后台进程启动多个service

    前面几个server 丢到后台启动,最后一个作为前台进程启动

    #!/bin/bash
    
    # turn on bash's job control
    set -m
    
    # Start the primary process and put it in the background
    ./my_main_process & 后台启动
    
    # Start the helper process
    ./my_helper_process 前台启动
    
    # the my_helper_process might need to know how to wait on the
    # primary process to start before it does its work and returns
    
    
    # now we bring the primary process back into the foreground
    # and leave it there
    fg %1
    
    FROM ubuntu:latest
    COPY my_main_process my_main_process
    COPY my_helper_process my_helper_process
    COPY my_wrapper_script.sh my_wrapper_script.sh
    CMD ./my_wrapper_script.sh
    

    方式三 用进程管理工具

    supervisord,一个稍重的管理工具来多个进程,需要配置supervisord.conf对每一个进程进行设置

    FROM ubuntu:latest
    RUN apt-get update && apt-get install -y supervisor
    RUN mkdir -p /var/log/supervisor
    COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
    COPY my_first_process my_first_process
    COPY my_second_process my_second_process
    CMD ["/usr/bin/supervisord"]
    

    2、docker --init


    名词解释

    1. 信号

    这个是 Linux 最常见一个概念,一般杀死进程时都会用到 kill <pid> 。 不同的信号有不同的默认行为。用户可以注册自己的信号处理函数,来覆盖掉默认行为

    1. 僵尸进程

    尸进程是终止运行的进程,为什么它们是有害的?
    虽然应用申请的内存已经释放了,但是你依然能通过 ps 看到它。这是因为有一些内核资源没有释放。

    下面是 Linux waitpid 的 man page:
    consume a slot in the kernel process table, and if this table fills, it will not be possible to create further processes."

    容器化后的问题

    容器化后,由于单容器单进程,已经没有传统意义上的 init 进程了。应用进程直接占用了 pid 1 的进程号。从而导致以下两个问题。

    进程不能正常终止

    Linux 内核中会对 pid 1 进程发送特殊的信号量。
    一般情况下,当给一个进程发送信号时,内核会先检查是否有用户定义的处理函数,如果没有,就会回退到默认行为。例如使用 SIGTERM 直接杀死进程。然而,如果进程的 PID是 1, 内核会特殊对待它。如果没有没有注册用户处理函数,内核不会回退到默认行为,什么也不做。换句话说,如果你的进程没有处理信号的函数,给他发送 SIGTERM会一点效果也没有。
    常见的使用是 docker run my-container script. 给 docker run进程发送SIGTERM 信号会杀掉 docker run 进程,但是容器还在后台运行。

    孤儿僵尸进程不能正常回收

    当进程退出时,它会变成僵尸进程,直到它的父进程调用 wait()( 或其变种 ) 的系统调用。process table 里面会把它的标记为 defunct 状态。一般情况下,父进程应该立即调用 wait(), 以防僵尸进程时间过长。
    如果父进程在子进程之前退出,子进程会变成孤儿进程, 它的父进程会变成 PID 1。因此,init进程就要对这些进程负责,并在适当的时候调用 wait() 方法。
    但是,通常情况下,大部分进程不会处理偶然依附在自己进程上的随机子进程,所以在容器中,会出现许多僵尸进程

    解决方案

    让所有的应用能正确的处理以上的情况,不太现实。好在现在有很多解决方案,例如dumb-init [0] 。他像是一个小型 init 服务,他启动一个子进程并转发所有接收到的信号量给子进程。而且不需要修改应用代码。
    此时你的应用进程已经不是 pid 1 了,所以已经没有上面提到的问题。而且 dumb-init
    也会转发所有的信号给子进程,应用的形为和在没有 dumb-init 时是一样的。如果应用进程死掉了,dumb-init 进程也会死掉,并会清理所有其它的子进程。
    类似的解决方案有 tini,pidnnu
    docker -init 就是会调用tini


    参考资料

    [0] docker多进程
    [1]CMD、ENTRYPOINT说明
    [2]docker init 进程

    相关文章

      网友评论

        本文标题:docker --init 与多进程容器

        本文链接:https://www.haomeiwen.com/subject/hwkggctx.html