如何在启动服务后保持Docker容器运行?

我已经看到了一堆教程,似乎做同样的事情,我试图做,但出于某种原因,我的Docker容器退出。基本上,我在Docker容器中设置了一个web服务器和几个守护进程。我通过一个名为run-all.sh的bash脚本在我的Dockerfile中运行CMD来完成最后一部分。run-all.sh看起来像这样:

service supervisor start
service nginx start

我在Dockerfile中启动它,如下所示:

CMD ["sh", "/root/credentialize_and_run.sh"]

我可以看到,当我手动运行(即使用-t /bin/bash进入映像)时,所有服务都正确启动,并且当我运行映像时,一切看起来都正确运行,但一旦它完成启动我的进程,它就退出了。我希望进程无限期地运行,据我所知,容器必须一直运行才能实现这一点。然而,当我运行docker ps -a时,我看到:

➜  docker_test  docker ps -a
CONTAINER ID        IMAGE                            COMMAND                CREATED             STATUS                      PORTS               NAMES
c7706edc4189        some_name/some_repo:blah   "sh /root/run-all.sh   8 minutes ago       Exited (0) 8 minutes ago                        grave_jones

到底发生了什么事?为什么它令人兴奋?我知道我可以在bash脚本的末尾放一个while循环来保持它,但是怎样才能使它不退出呢?

341463 次浏览

这并不是你真正应该如何设计Docker容器。

在设计Docker容器时,你应该让它只运行一个进程(也就是说,你应该为Nginx准备一个容器,为它运行的应用程序准备一个容器);此外,该进程应该在前台运行。

当进程本身退出时,容器将“退出”(在您的示例中,该进程就是您的bash脚本)。


然而,如果你的真的需要(或想要)在Docker容器中运行多个服务,可以考虑从“Docker基础映像”开始,它使用runit作为伪init进程(当Nginx和Supervisor运行时,runit将保持在线),当你的其他进程运行时,它将保持在前台。

他们有大量的文档,所以你应该能够很容易地实现你想要做的事情。

确保你在nginx.conf中添加了daemon off;,或者按照nginx的官方镜像使用CMD ["nginx", "-g", "daemon off;"]运行它

然后使用以下命令运行监控器作为服务,nginx作为前台进程,以防止容器退出

service supervisor start && nginx

在某些情况下,容器中需要有多个进程,因此强制容器只有一个进程是行不通的,而且会在部署中产生更多问题。

所以你需要了解其中的利弊,并做出相应的决定。

它退出的原因是因为shell脚本首先作为PID 1运行,当它完成时,PID 1消失了,而docker只在PID 1运行时运行。

你可以使用监控器来做所有的事情,如果运行时带有“-n”标志,它会被告知不要守护进程,所以它将保持作为第一个进程:

CMD ["/usr/bin/supervisord", "-n"]

你的supervisor .conf:

[supervisord]
nodaemon=true


[program:startup]
priority=1
command=/root/credentialize_and_run.sh
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
autorestart=false
startsecs=0


[program:nginx]
priority=10
command=nginx -g "daemon off;"
stdout_logfile=/var/log/supervisor/nginx.log
stderr_logfile=/var/log/supervisor/nginx.log
autorestart=true

然后你可以有很多其他的进程,你想要和管理将处理重新启动他们,如果需要。

这样你就可以在需要nginx和php5-fpm的情况下使用supervisor ord,而把它们分开没有太大意义。

我刚刚有同样的问题,我发现,如果你运行你的容器与-t-d标志,它会继续运行。

docker run -td <image>

下面是标志的作用(根据docker run --help):

-d, --detach=false         Run container in background and print container ID
-t, --tty=false            Allocate a pseudo-TTY

最重要的是-t标志。-d只是让你在后台运行容器。

你可以运行普通的cat,不带任何参数,就像兄弟@Sa'ad提到的那样,简单地让容器工作[实际上什么都不做,只是等待用户输入](Jenkins的Docker插件也做同样的事情)

如果您正在使用Dockerfile,请尝试:

ENTRYPOINT ["tail", "-f", "/dev/null"]

(显然这只是为了开发目的,你不需要让容器保持活动状态,除非它正在运行一个进程。nginx…)

在一个变量(例如$NGNIX_PID)中捕获ngnix进程的PID,并在入口点文件do的末尾

wait $NGNIX_PID

通过这种方式,容器应该一直运行到ngnix激活,当ngnix停止时,容器也会停止

动机:

在docker容器中运行多个进程没有错。如果你喜欢使用docker作为一个轻量级的VM,那就这样吧。其他人则喜欢将他们的应用程序拆分为微服务。我想:一个容器里的LAMP堆栈?就好了。

答案是:

坚持使用良好的基础图像,如Phusion基底像。可能还有其他的。请发表评论。

这只是另一个请求监督的请求。因为phusion base image除了提供cron和locale setup之类的东西外,还提供了监督器。当运行这样一个轻量级VM时,你想要设置的东西。它还提供了到容器的ssh连接。

phusion镜像本身将启动并继续运行,如果你发出以下基本的docker运行语句:

moin@stretchDEV:~$ docker run -d phusion/baseimage
521e8a12f6ff844fb142d0e2587ed33cdc82b70aa64cce07ed6c0226d857b367
moin@stretchDEV:~$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS
521e8a12f6ff        phusion/baseimage   "/sbin/my_init"     12 seconds ago      Up 11 seconds

或者非常简单:

如果基本图像不适合你……对于快速CMD,让它保持运行,我认为bash是这样的:

CMD exec /bin/bash -c "trap : TERM INT; sleep infinity & wait"

或者busybox是这样的:

CMD exec /bin/sh -c "trap : TERM INT; (while true; do sleep 1000; done) & wait"

这很好,因为它将在docker stop立即退出

只是简单的sleepcat在容器被docker杀了之前将花费几秒钟的时间。

更新

作为对Charles Desbiens关于在一个容器中运行多个进程的回应:

这是一种观点。和的文档指向这个方向。引用一句话:“拥有多个进程是可以的,但是为了从Docker中获得最大的利益,避免一个容器负责整个应用程序的多个方面。”当然,将复杂的服务划分为多个容器显然要强大得多。但在某些情况下,走一个集装箱路线是有益的。尤其是家电。GitLab码头工人镜像是我最喜欢的多进程容器的例子。它使这个复杂系统的部署变得容易。不可能出现错误配置。GitLab保留对其设备的所有控制权。双赢。

如果可以的话,使用监督形式的服务怎么样?

service YOUR_SERVICE监督

一旦supervise成功运行,它将不会退出,除非它是 被杀或被特别要求退出

节省了必须创建supervisord.conf

除了在docker文件中包含类似于:ENTRYPOINT ["tail", "-f", "/dev/null"]的内容外,你还应该使用-td选项运行docker容器。当容器在远程m/c上运行时,这特别有用。可以把它想象成您通过ssh连接到一个远程m/c,其中包含映像并启动了容器。在这种情况下,当你退出ssh会话时,容器将被杀死,除非它是用-td选项启动的。运行映像的示例命令是:docker run -td <any other additional options> <image name>

这适用于docker版本20.10.2

在开发过程中,有些情况下还没有服务,但您希望模拟它并保持容器处于活动状态。

编写一个bash占位符来模拟一个正在运行的服务是非常容易的:

while true; do
sleep 100
done

随着开发的进展,你可以用一些更严重的东西来代替它。

从docker引擎v1.25开始,存在名为init一个选项
Docker-compose 包括这个命令 as version 3.7.

所以当我运行一个容器时,我当前的CMD应该运行到无穷大:

CMD ["sleep", "infinity"]

然后运行它,使用:

docker build
docker run --rm --init app
< p > crf。 rm文档初始化文档