使用supervisord管理docker容器的最佳方法

时间:2015-05-04 16:15:16

标签: python jenkins continuous-integration docker

我必须设置" dockerized" 相同服务器上的环境(集成,qa和生产)(客户端要求)。每个环境将按如下方式组成:

  • 的RabbitMQ
  • 芹菜
  • 基于python 3的应用程序名为" A" (具体分支) 环境)

通过它们,jenkins将基于CI处理部署。

每个环境使用一组容器听起来像是最好的方法。

但现在我需要,流程经理来运行和监督所有这些:

  • 3个兔子容器,
  • 3个芹菜/花卉容器,
  • 3" A"容器,
  • 1个jenkins容器。

Supervisord似乎是最好的选择,但在我的测试中,我无法正确地选择#34;重启容器。这是supervisord.conf的片段

[program:docker-rabbit]
command=/usr/bin/docker run -p 5672:5672 -p 15672:15672 tutum/rabbitmq
startsecs=20
autorestart=unexpected
exitcodes=0,1
stopsignal=KILL

所以我想知道分离每个环境的最佳方法是什么,并且能够管理和监督每个服务(容器)。

[编辑我的解决方案受到Thomas回应的启发]

每个容器都由一个看起来像

的.sh脚本运行

rabbit-integration.py

#!/bin/bash

#set -x
SERVICE="rabbitmq"
SH_S = "/path/to_shs"
export MY_ENV="integration"
. $SH_S/env_.sh
. $SH_S/utils.sh

SERVICE_ENV=$SERVICE-$MY_ENV
ID_FILE=/tmp/$SERVICE_ENV.name # pid file 

trap stop SIGHUP SIGINT SIGTERM  # trap signal for calling the stop function
run_rabbitmq   

$ SH_S / env_.sh 看起来像:

# set env variable 
...
case $MONARCH_ENV in
    $INTEGRATION)
       AMQP_PORT="5672"
       AMQP_IP="172.17.42.1"
     ...
    ;;
    $PREPRODUCTION)
       AMQP_PORT="5673"
       AMQP_IP="172.17.42.1"
       ...
        ;;
    $PRODUCTION)
        AMQP_PORT="5674"
        REDIS_IP="172.17.42.1"
        ...
esac

$ SH_S / utils.sh 看起来像:

#!/bin/bash

function random_name(){
        echo "$SERVICE_ENV-$(cat /proc/sys/kernel/random/uuid)"
}
function stop (){
        echo "stopping docker container..."
        /usr/bin/docker stop `cat $ID_FILE`
}
function run_rabbitmq (){
        # do no daemonize and use stdout
        NAME="$(random_name)"
        echo $NAME > $ID_FILE
        /usr/bin/docker run -i --name "$NAME" -p $AMQP_IP:$AMQP_PORT:5672 -p $AMQP_ADMIN_PORT:15672 -e RABBITMQ_PASS="$AMQP_PASSWORD" myimage-rabbitmq &
        PID=$!
        wait $PID
}

至少 myconfig.intergration.conf 看起来像是:

[program:rabbit-integration]
command=/path/sh_s/rabbit-integration.sh
startsecs=20
priority=90
autorestart=unexpected
exitcodes=0,1
stopsignal=TERM

如果我想使用相同的容器,启动功能看起来像:

function _run_my_container () {
    NAME="my_container"
    /usr/bin/docker start -i $NAME &
    PID=$!
    wait $PID
    rc=$?
    if [[ $rc != 0 ]]; then
       _run_my_container 
    fi
}

其中

function _run_my_container (){
    /usr/bin/docker run -p{} -v{} --name "$NAME" myimage &
    PID=$!
    wait $PID
}

4 个答案:

答案 0 :(得分:11)

主管要求其管理的流程不会根据其documentation进行守护:

  

计划在主管下运行的程序不应该守护进程   他们自己。相反,它们应该在前台运行。他们应该   不要从它们开始的终端分离。

这与Docker很不兼容,其中容器是 Docker进程本身的子进程(即因此不是Supervisor的子进程)。

为了能够将Docker与Supervisor一起使用,您可以编写与Docker一起使用的等效pidproxy program

但实际上,这两个工具并没有真正构建在一起工作,所以你应该考虑改变其中一个:

  • 考虑将主管替换为Docker Compose(设计用于Docker)
  • 考虑将Docker替换为Rocket(它没有" master"进程)

答案 1 :(得分:7)

您需要确保在主管配置中使用stopsignal = INT,然后正常执行docker run

[program:foo]
stopsignal=INT
command=docker -rm run whatever

至少这似乎适用于Docker版本1.9.1。

如果在shell脚本中运行docker表单,则在docker run命令前面有exec非常重要,这样docker run将替换shell进程,从而直接接收SIGINT来自supervisord。

答案 2 :(得分:4)

你可以让Docker不分离然后工作正常。我们通过主管以这种方式管理我们的Docker容器。 Docker组合很棒,但如果您已经使用Supervisor来管理非docker的东西,那么继续使用它来将所有管理放在一个地方是很好的。我们将我们的docker run包装在一个bash脚本中,如下所示,并让主管跟踪,并且一切正常:

#!/bin/bash¬
TO_STOP=docker ps | grep $SERVICE_NAME | awk '{ print $1 }'¬
if [$TO_STOP != '']; then¬
    docker stop $SERVICE_NAME¬
fi¬
TO_REMOVE=docker ps -a | grep $SERVICE_NAME | awk '{ print $1 }'¬
if [$TO_REMOVE != '']; then¬
    docker rm $SERVICE_NAME¬
fi¬
¬
docker run -a stdout -a stderr --name="$SERVICE_NAME" \
 --rm $DOCKER_IMAGE:$DOCKER_TAG

答案 3 :(得分:1)

我发现通过主管执行console.log( adapterInstance.id, // 1 adapterInstance.name, // "myName" adapterInstance.type, // "myType" adapterInstance.overwritten, // 0 Object.keys(adapterInstance), // ["id", "name", "type", "overwritten"] Object.entries(adapterInstance) // [["id", 1], ["name", "myName"], ["type", "myType"], ["overwritten", 0]] ) 实际上工作得很好,并采取了一些预防措施。需要避免的主要事情是允许主管将.JS发送到docker run进程,这将终止该进程,而不是容器本身。

在大多数情况下,可以按照Why Your Dockerized Application Isn’t Receiving Signals中的说明进行处理。简而言之,需要:

  1. 使用SIGKILL形式(与docker run相同)而不是shell形式(CMD ["/path/to/myapp"])。
  2. ENTRYPOINT传递到CMD /path/to/myapp
  3. 如果使用--init,请确保其最后一行调用docker run,以避免产生新的进程。
  4. 如果上述方法仍然无效,请将ENTRYPOINT添加到您的exec

此外,您需要确保您在超级用户中的STOPSIGNAL设置大于收到Dockerfile(例如,{{3} }(如果使用Gunicorn)。

以下是运行Gunicorn容器的示例配置:

stopwaitsecs