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

Python 使用supervisord管理docker容器的最佳方法,python,jenkins,continuous-integration,docker,Python,Jenkins,Continuous Integration,Docker,我必须在相同的服务器上设置“停靠”环境(集成、qa和生产)(客户要求)。每个环境将由以下部分组成: 兔子 芹菜 花 基于python 3的应用程序称为“A”(每个 (环境) jenkins将处理基于CI的部署 在每个环境中使用一组容器听起来是最好的方法 但现在我需要流程经理来运行和监督所有这些: 3个兔子容器 3个芹菜/花卉容器 3个“A”集装箱 1詹金斯集装箱 Supervisord似乎是最佳选择,但在测试期间,我无法“正确”重新启动容器。这里是supervisordconf的一个片段

我必须在相同的服务器上设置“停靠”环境(集成、qa和生产)(客户要求)。每个环境将由以下部分组成:

  • 兔子
  • 芹菜
  • 基于python 3的应用程序称为“A”(每个 (环境)
jenkins将处理基于CI的部署

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

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

  • 3个兔子容器
  • 3个芹菜/花卉容器
  • 3个“A”集装箱
  • 1詹金斯集装箱
Supervisord似乎是最佳选择,但在测试期间,我无法“正确”重新启动容器。这里是supervisordconf的一个片段

[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 response启发的我的解决方案]

每个容器都由一个.sh脚本运行

rabbit-integration.py

$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
#!/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
}
[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
}
$SH_S/utils.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
#!/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
}
[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
}
至少myconfig.integration.conf看起来像:

# 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
#!/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
}
[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
}
在我希望使用相同容器的情况下,启动函数如下所示:

# 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
#!/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
}
[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
}

主管要求其管理的流程不进行后台监控,具体如下:

本应在主管下运行的程序不应后台监控 他们自己。相反,它们应该在前台运行。他们应该 不从启动它们的端子上拆下

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

为了能够和主管一起使用Docker,您可以编写一个和Docker相同的代码


<>但是,这两个工具并不是真正的架构在一起工作,所以你应该考虑改变一个或另一个:

  • 考虑将主管替换为(设计用于Docker)
  • 考虑将Docker替换为(没有“主”流程)

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

[program:foo]
stopsignal=INT
command=docker -rm run whatever
至少docker版本1.9.1对我来说是这样


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

您可以让docker不分离,然后一切正常。我们通过主管以这种方式管理Docker容器。Docker compose很好,但如果您已经在使用Supervisor来管理非Docker事务,那么继续使用它将您的所有管理集中在一个地方是很好的。我们将在bash脚本中封装docker运行,如下所示,并让主管跟踪,一切正常:

#!/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

我发现,通过supervisor执行
docker run
实际上效果很好,但有一些预防措施。需要避免的主要事情是允许supervisord向
docker run
进程发送
SIGKILL
,这将终止该进程,但不会终止容器本身

在大多数情况下,可以按照中的说明进行处理。简言之,我们需要:

  • 使用
    CMD[“/path/to/myapp”]
    表单(与
    ENTRYPOINT
    相同),而不是shell表单(
    CMD/path/to/myapp
  • --init
    传递到
    docker run
  • 如果使用
    入口点
    ,请确保其最后一行调用
    exec
    ,以避免产生新进程
  • 如果上述操作仍不起作用,请在
    Dockerfile
    中添加一个
    停止信号
  • 此外,您还需要确保supervisor中的
    stopwaitsecs
    设置大于进程收到
    SIGTERM
    时正常关机所需的时间(例如,如果使用gunicorn)

    下面是运行gunicorn容器的示例配置:

    [program:gunicorn]
    command=/usr/bin/docker run --init --rm -i -p 8000:8000 gunicorn
    redirect_stderr=true
    stopwaitsecs=31
    

    与你在这个问题上的具体问题有点正交,但是你已经看过了吗?它允许您定义/管理容器组(包括容器之间的链接)。此外,它允许您在配置中指定可能处理您需要的内容。谢谢,我看到了它,但没有找到重新启动特定服务以及如何监督它的方法。我们还希望使用supervisorctl web界面与现有的监控解决方案连接。。。做一个好的视觉码头经理:谢谢,这是最短最切题的建议,对我来说很有用。我在我的shell脚本中使用exec,它可以正常工作。我使用的是docker--rm-I容器,日志很好地记录在/var/log/supervisor的supervisor日志中。。exec是这里的关键部分,因为我使用shell脚本运行docker。需要执行官,以便主管可以关闭流程。不需要停车信号灯,但这不是一个很好的答案。发送
    SIGINT
    可能会导致相关进程的“不正常”关闭。这听起来很酷,我从未意识到supervisor可以以这种方式使用,supervisor.d/app.conf文件会是什么样子?在一台主机上运行多个docker compose文件的情况下,这将如何取代docker compose?您在
    app.conf
    文件中的
    命令将只运行包含上述代码的脚本。要明确的是,这甚至不接近于完全取代
    docker compose
    ,因为在一个复杂的情况下,con和