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


155

我看过很多教程似乎都在做我想做的事情,但是由于某种原因,我的Docker容器退出了。基本上,我正在Docker容器内设置一个Web服务器和一些守护程序。我通过一个bash脚本(run-all.sh在Dockerfile中通过CMD运行)完成了最后一部分。run-all.sh看起来像这样:

service supervisor start
service nginx start

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

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

我可以看到,当我手动运行某些东西(即使用-i -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循环来保持它,但是什么是防止它退出的正确方法呢?


1
您是否将服务的端口暴露于外部(docker run的-p选项)?(当然这不会阻止他们退出)
ribamar

1
我在Dockerfile中使用ENTRYPOINT,在ENTRYPOINT中定义的脚本(我的初始化脚本)运行后,它显示在日志中,但我的容器似乎退出了。因此,我使用RUN命令代替ENTRYPOINT来运行脚本,并且该容器仍在后台运行。
ypahalajani

Answers:


49

这并不是设计Docker容器的真正方法。

在设计Docker容器时,您应该将其构建为只有一个运行进程(即,您应该为Nginx提供一个容器,为管理者或正在运行的应用提供一个容器);此外,该过程应在前台运行。

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


但是,如果您真的需要(或想要)在Docker容器中运行多个服务,请考虑从“ Docker Base Image”开始,该镜像runit用作伪初始化进程(runit在运行Nginx和Supervisor时将保持在线)在前台,而其他进程则在做自己的事情。

他们有大量的文档,因此您应该能够轻松轻松地实现您想要的工作。


1
您能解释为什么我应该只运行一项服务吗?如果需要,我可以将nginx添加到主管中,但是不确定为什么这是必要的。
伊莱

3
@Eli简短的答案是,这就是Docker的工作方式。Docker每个容器只会运行一个进程(及其子进程)。建议该过程是一个实际的应用程序过程(以便Docker知道退出该过程),但实际上您可以使用超级用户作为该过程。请注意,您必须将超级用户配置为在前台运行(即不后台运行),这是通过该--nodaemon选项完成的。
Thomas Orozco 2014年

1
@Eli 这篇Docker博客文章指出,运行多个进程(从广义上讲,将容器视为“小型VPS”)不是最佳选择。在您的情况下,评论主题可能比实际博客帖子更相关。
Thomas Orozco 2014年

1
Docker基本映像是解决许多企业问题的糟糕解决方案,因为很少有严肃的公司使用ubuntu,而更喜欢RHEL / Centos树。
软件工程师

9
“很少有严肃的公司”似乎是站不住脚的。操作系统的选择似乎完全基于用例。任何给定的公司都具有许多不同的环境,包括内部开发人员使用,内部员工使用,销售支持,暂存,POC以及最终生产(甚至是一个模糊的术语)。我不相信OP会如此提及他们的用例(对不起,这很挑剔),但是这种评论似乎是散布有很多见解的信息而没有理由的类型。
约翰·卡雷尔

154

如果您使用的是Dockerfile,请尝试:

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

(显然,这仅是出于开发目的,除非容器正在运行某个进程,例如nginx,否则您无需保持其活动状态。)


5
我正在使用,CMD["sleep", "1d"]但是您的解决方案似乎更好
George Pligoropoulos

@GeorgiosPligoropoulos,它将停留在该行中;也许在后台运行会起作用
Prashanth Sams

5
也可以使用CMD["sleep", "infinity"]
罗曼(Romain)

5
或“猫”,但人们可能会说这是对动物的虐待。xD
lawphotog

您可以使用来完成入口点脚本,exec tail -f /dev/null但是将其tail用作入口点是错误的答案。
Torsten Bronger

85

我只是遇到了同样的问题,我发现如果您使用-tand -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只允许您在后台运行容器。


3
我无法重现。您能举个例子吗?为了使它起作用,我们需要有关Dockerfile的任何特定信息(例如:CMD)吗?
Matheus Santana

2
这对我不起作用。我使用命令docker logs <image>来确保它是导致我的docker容器退出的错误。退出状态为0,最后输出为确认我的lighttpd服务器正在运行:[ ok ] Starting web server: lighttpd.
ob1

我已经有一段时间没有与Docker合作了。因此,命令行界面可能已更改,并且该命令不再起作用。
arne.z

4
我可以确认这确实适用于最新的docker版本。如果要稍后附加到此会话,也可以使用-dit。
约翰·汉密尔顿

1
@长脚本将不接受tty,添加exec bash或者exec sh如果未安装bash ,则将其保留到start.sh的末尾。然后你可以使用-t标志
123

43

它退出的原因是因为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的情况下使用超级用户,而将它们分开并没有多大意义。


它在文档中哪里说明PID 1是否结束docker容器停止运行?
8

@ 8oh8本质上就是进程名称空间的工作方式。它不是特定于Docker的,而是“所有容器的基础”。从man7.org/linux/man-pages/man7/pid_namespaces.7.htmlIf the "init" process of a PID namespace terminates, the kernel terminates all of the processes in the namespace via a SIGKILL signal. This behavior reflects the fact that the "init" process is essential for the correct operation of a PID namespace.
dannysauer

40

您可以cat像兄弟@ Sa'ad一样简单地运行,而无需任何参数,以使容器正常工作(实际上只是在等待用户输入之外什么也不做)(Jenkins的Docker插件也做同样的事情)


除了我的回答:但一定要了解使用docker-compose(未守护程序)来向您展示容器的工作流程,因此尾随启动服务的日志文件可能很方便。欢呼声
Serge Velikanov

1
cat。jenkin的docker插件可以这样做。
萨阿德

12

确保您添加daemon off;到您的nginx.conf或CMD ["nginx", "-g", "daemon off;"]按照官方nginx映像运行它

然后使用以下命令将supervisor作为服务运行,并将nginx作为前台运行,这将防止容器退出

service supervisor start && nginx

在某些情况下,您的容器中需要有多个进程,因此强制容器仅具有一个进程将不起作用,并且会在部署中造成更多问题。

因此,您需要了解折衷方案,并据此做出决定。


6

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

wait $NGNIX_PID 

这样,您的容器应该运行直到ngnix处于活动状态,当ngnix停止时,容器也将停止


6

动机:

docker容器中运行多个进程没有错。如果有人喜欢将docker用作轻量级虚拟机-那就这样吧。其他人则喜欢将其应用程序拆分为微服务。我认为:在一个容器中堆叠一个LAMP?太好了

答案:

坚持使用良好的基本图像,例如phusion基本图像。可能还有其他。请评论。

这只是主管的另一个恳求。因为除了其他一些诸如cron和locale设置之类的东西以外,phusion基本映像还为管理员提供了支持。运行如此轻量级的VM时,您喜欢进行设置。值得的是,它还提供了到容器的ssh连接。

如果发出以下基本docker run语句,则phusion映像本身将立即启动并继续运行:

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。只是简单,sleep否则cat将需要几秒钟的时间才能退出容器。


我自定义了centos7基本映像以加载PostgreSQL11。首先从/ usr / pgsql-11 / bin / pg_ctl调用开始,但是pg_ctl在服务器运行后退出。您关于使用陷阱的建议非常有效;这是我的脚本pgstartwait.sh的最后一行
Alchemistmatt

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.