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

Eli*_*Eli 131 docker

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

service supervisor start
service nginx start
Run Code Online (Sandbox Code Playgroud)

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

CMD ["sh", "/root/credentialize_and_run.sh"]
Run Code Online (Sandbox Code Playgroud)

我可以看到,当我手动运行时(即使用-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
Run Code Online (Sandbox Code Playgroud)

是什么赋予了?为什么要退出?我知道我可以在我的bash脚本末尾添加一个while循环来保持它,但是什么是阻止它退出的正确方法?

Sof*_*ets 117

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

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

(显然这只是出于开发目的,你不应该让容器保持活着,除非它正在运行一个进程,例如.nginx ...)

  • 也可以使用`CMD["sleep", "infinity"]`。 (20认同)
  • 或“猫”,但人们可能会说这是虐待动物。xD (16认同)
  • 我正在使用`CMD [“ sleep”,“ 1d”]`,但您的解决方案似乎更好 (3认同)
  • 您可以使用“exec tail -f /dev/null”完成入口点脚本,但使用“tail”作为入口点是错误的答案。 (3认同)

arn*_*e.z 77

我遇到了同样的问题,我发现如果你使用-t-d标志运行你的容器,它会继续运行.

docker run -td <image>
Run Code Online (Sandbox Code Playgroud)

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

-d, --detach=false         Run container in background and print container ID
-t, --tty=false            Allocate a pseudo-TTY
Run Code Online (Sandbox Code Playgroud)

最重要的是-t国旗.-d只是让你在后台运行容器.

  • 我可以确认这确实与最新的docker版本一起使用.如果您想稍后附加到此会话,使用-dit也可以. (4认同)
  • 我无法重现这一点.你能举个例子吗?有什么特定的(例如:CMD)关于Dockerfile我们需要这个才能工作吗? (3认同)
  • 这不适合我.我使用命令`docker logs <image>`来确保它是一个导致我的docker容器退出的错误.退出状态为"0",最后一个输出是我的`lighttpd`server正在运行的混乱:`[ok]启动Web服务器:lighttpd.` (2认同)
  • @Long 脚本不会接受 tty,如果未安装 bash,请将 `exec bash` 或 `exec sh` 添加到 start.sh 的末尾。然后你可以使用 -t 标志 (2认同)

Tho*_*zco 44

这不是你应该如何设计你的Docker容器.

在设计Docker容器时,你应该构建它,使得只有一个进程在运行(即你应该有一个容器用于Nginx,一个用于supervisord或它正在运行的应用程序); 另外,该进程应该在前台运行.

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


但是,如果您确实需要(或想要)在Docker容器中运行多个服务,请考虑从"Docker Base Image"开始,它将runit用作伪init进程(runit将在Nginx和Supervisor运行时保持在线状态),这将保持不变当你的其他进程做他们的事情时,在前台.

他们拥有大量的文档,因此您应该能够轻松地实现您想要做的事情.

  • “很少有严肃的公司”似乎是站不住脚的。操作系统的选择似乎完全基于用例。任何给定的公司都有许多不同的环境,包括内部开发人员使用,内部员工使用,销售支持,暂存,POC以及最终生产(甚至是一个模糊的术语)。我不相信OP会如此提及他们的用例,(很遗憾,这太挑剔了),但是这种评论似乎是散布有很多见解的信息而没有理由的类型。 (5认同)
  • 你能解释一下为什么我应该只运行一项服务吗?如有必要,我可以将 nginx 添加到主管,但不确定为什么需要这样做。 (3认同)
  • @Eli简短的回答是这就是Docker的工作原理.Docker每个容器只运行一个进程(及其子进程).建议这个过程是一个实际的应用程序过程(如果它退出,Docker知道),但你确实可以使用supervisor作为该过程.请注意,您必须将supervisor配置为在前台运行(即不是daemonize),这是通过`--nodaemon`选项完成的. (3认同)
  • 想要后台运行的程序是容器化中的一个真正问题。是否在一个容器中运行多个服务的问题与此正交。所以这不是问题的答案。 (2认同)

pha*_*zei 41

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

您可以使用主管来执行所有操作,如果使用"-n"标志运行,则告知不要守护进程,因此它将作为第一个进程保留:

CMD ["/usr/bin/supervisord", "-n"]
Run Code Online (Sandbox Code Playgroud)

还有你的supervisord.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
Run Code Online (Sandbox Code Playgroud)

然后,您可以拥有任意数量的其他进程,如果需要,主管将处理它们的重新启动.

这样你就可以在需要nginx和php5-fpm的情况下使用supervisord,将它们分开是没有多大意义的.


Ser*_*nov 37

你可以运行plain cat而没有任何参数,如bro @ Sa'ad所提到的那样只是让容器工作[实际上什么都不做,只是等待用户输入](Jenkins的Docker插件做同样的事情)


its*_*ire 19

动机:

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

答案:

坚持一个良好的基础图像PHUSION基本图像。可能还有其他人。请给出意见。

这只是对主管的又一次恳求。因为 phusion 基础映像除了提供 cron 和语言环境设置等其他内容之外,还提供主管。在运行这样一个轻量级的虚拟机时你喜欢设置的东西。值得一提的是,它还提供了到容器的 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
Run Code Online (Sandbox Code Playgroud)

或者死简单:

如果基本映像不适合您...为了快速 CMD 使其保持运行,我会假设 bash 是这样的:

CMD exec /bin/bash -c "trap : TERM INT; sleep infinity & wait"
Run Code Online (Sandbox Code Playgroud)

或者这个对于busybox:

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

这是很好的,因为它会立即退出docker stop

很简单,sleep或者cat在容器被docker 强行杀死之前需要几秒钟。

更新

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

这是一个意见。并且文档指向这个方向。引用:“拥有多个进程是可以的,但要从 Docker 中获得最大收益,请避免一个容器负责整个应用程序的多个方面。” 可以肯定的是,将复杂的服务划分到多个容器中显然要强大得多。但在某些情况下,走一个集装箱路线可能是有益的。特别是对于电器。该GitLab泊坞窗图像是我最喜欢的多工艺容器的例子。它使这个复杂系统的部署变得容易。没有办法进行错误配置。GitLab 保留对其设备的所有控制权。双赢。


小智 15

从 docker engine v1.25 开始,有一个名为 的选项init
Docker-compose从 开始包含此命令version 3.7

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

CMD ["sleep", "infinity"]
Run Code Online (Sandbox Code Playgroud)

然后使用以下命令运行它:

docker build
docker run --rm --init app
Run Code Online (Sandbox Code Playgroud)

crf.:rm 文档init 文档


iTe*_*ech 12

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

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

service supervisor start && nginx

在某些情况下,您需要在容器中有多个进程,因此强制容器只有一个进程将无法工作,并且可能会在部署中产生更多问题.

因此,您需要了解权衡并做出相应的决定.


小智 5

在变量(例如$ NGNIX_PID)和入口点文件末尾捕获ngnix进程的PID

wait $NGNIX_PID 
Run Code Online (Sandbox Code Playgroud)

这样,你的容器应该运行直到ngnix存活,当ngnix停止时,容器也会停止