我看过一堆教程,它们似乎和我想做的事情一样,但由于某种原因,我的 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"]
我可以看到,当我手动运行时(即使用 -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 循环来保持它,但是阻止它退出的正确方法是什么?
如果您使用的是 Dockerfile,请尝试:
ENTRYPOINT ["tail", "-f", "/dev/null"]
(显然这仅用于开发目的,除非容器正在运行一个进程,例如 nginx ...,否则您不需要保持容器处于活动状态。)
我刚刚遇到了同样的问题,我发现如果您使用 -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
只是让您在后台运行容器。
docker logs <image>
来确保它是导致我的 docker 容器退出的错误。退出状态为 0
,最后一个输出是确认我的 lighttpd
服务器正在运行:[ ok ] Starting web server: lighttpd.
exec bash
或 exec sh
添加到 start.sh 的末尾。然后你可以使用 -t 标志
这不是你应该如何设计 Docker 容器的方式。
在设计 Docker 容器时,您应该构建它以便只运行一个进程(即您应该有一个用于 Nginx 的容器,一个用于 supervisord 或它正在运行的应用程序);此外,该进程应该在前台运行。
当进程本身退出时,容器将“退出”(在您的情况下,该进程是您的 bash 脚本)。
但是,如果您真的需要(或想要)在 Docker 容器中运行多个服务,请考虑从 "Docker Base Image" 开始,它使用 runit
作为伪初始化进程(runit
将保留在 Nginx 和 Supervisor 运行时在线),这将保持在前台,而您的其他进程执行它们的操作。
他们有大量的文档,所以你应该能够轻松地实现你想要做的事情。
--nodaemon
选项完成的。
它退出的原因是因为 shell 脚本首先作为 PID 1 运行,当它完成时,PID 1 消失了,docker 仅在 PID 1 存在时运行。
你可以使用supervisor来做任何事情,如果使用“-n”标志运行它被告知不要守护进程,所以它将保持作为第一个进程:
CMD ["/usr/bin/supervisord", "-n"]
还有你的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
然后,您可以拥有任意数量的其他进程,并且主管将在需要时处理它们的重新启动。
这样你就可以在你可能需要 nginx 和 php5-fpm 的情况下使用 supervisord 并且将它们分开没有多大意义。
If 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.
你可以像@Sa'ad 兄弟提到的那样运行普通的 cat
而不需要任何参数来简单地保持容器工作[实际上除了等待用户输入什么都不做](Jenkins' Docker 插件做同样的事情)
cat
。 jenkin 的 docker 插件就是这样做的。
动机:
有nothing wrong in running multiple processes inside of a docker container。如果有人喜欢将 docker 用作轻量级 VM - 就这样吧。其他人喜欢将他们的应用程序拆分为微服务。我想:一个容器中的 LAMP 堆栈?太好了。
答案:
坚持使用良好的基础图片,例如 phusion base image。可能还有其他人。请评论。
这只是对主管的又一次恳求。因为除了 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
上立即退出。
在容器被 docker 强行杀死之前,简单的 sleep
或 cat
将需要几秒钟。
更新
作为对 Charles Desbiens 关于在一个容器中运行多个进程的回应:
这是一个意见。 the docs 正指向这个方向。一句话:“拥有多个进程是可以的,但要从 Docker 中获得最大收益,请避免一个容器负责整个应用程序的多个方面。”当然,将复杂的服务分成多个容器显然要强大得多。但在某些情况下,走一条集装箱路线可能会有所帮助。特别是对于电器。 GitLab Docker 图像是我最喜欢的多进程容器示例。它使这个复杂系统的部署变得容易。没有办法配置错误。 GitLab 保留对其设备的所有控制权。双赢。
确保将 daemon off;
添加到 nginx.conf 或按照官方 nginx 映像使用 CMD ["nginx", "-g", "daemon off;"]
运行它
然后使用以下命令将主管作为服务和 nginx 作为前台进程运行,这将阻止容器退出
service supervisor start && nginx
在某些情况下,您的容器中需要有多个进程,因此强制容器只有一个进程是行不通的,并且会在部署中产生更多问题。
因此,您需要了解权衡并做出相应的决定。
在变量(例如 $NGNIX_PID)中捕获 ngnix 进程的 PID,并在入口点文件的末尾执行
wait $NGNIX_PID
这样,您的容器应该一直运行到 ngnix 还活着,当 ngnix 停止时,容器也会停止
自 docker engine v1.25 起,an option 被称为 init
。
Docker-compose included this command 自 version 3.7
起。
所以我当前的 CMD 在运行一个应该运行到无穷大的容器时:
CMD ["sleep", "infinity"]
然后使用以下命令运行它:
docker build
docker run --rm --init app
除了在 docker 文件中包含类似 : ENTRYPOINT ["tail", "-f", "/dev/null"]
的内容外,您还应该使用 -td
选项运行 docker 容器。这在容器在远程 m/c 上运行时特别有用。把它想象成你已经 ssh'ed 到一个具有图像的远程 m/c 并启动了容器。在这种情况下,当您退出 ssh 会话时,容器将被杀死,除非它以 -td
选项启动。运行图像的示例命令是:docker run -td <any other additional options> <image name>
这适用于 docker 版本 20.10.2
在开发过程中,有些情况下还没有服务,但您想模拟它并保持容器处于活动状态。
编写一个模拟正在运行的服务的 bash 占位符非常容易:
while true; do
sleep 100
done
随着开发的进展,你会用更严肃的东西来代替它。
如果可以的话,如何使用监督服务形式?
服务 YOUR_SERVICE 监督
一旦 supervise 成功运行,它不会退出,除非它被杀死或特别要求退出。
无需创建 supervisord.conf
CMD["sleep", "1d"]
,但您的解决方案似乎更好CMD["sleep", "infinity"]
。exec tail -f /dev/null
完成您的入口点脚本,但使用tail
作为入口点是错误的答案。