Python 使用supervisord管理docker容器的最佳方法
我必须在相同的服务器上设置“停靠”环境(集成、qa和生产)(客户要求)。每个环境将由以下部分组成: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的一个片段
- 兔子
- 芹菜
- 花
- 基于python 3的应用程序称为“A”(每个 (环境)
- 3个兔子容器
- 3个芹菜/花卉容器
- 3个“A”集装箱
- 1詹金斯集装箱
[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替换为(没有“主”流程)
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和