ChatGPT解决这个技术问题 Extra ChatGPT

启动服务后如何保持 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"]

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

您是否将服务的端口暴露在外部(docker run 的 -p 选项)? (当然这不会阻止他们退出)
我在我的 Dockerfile 中使用 ENTRYPOINT,在 ENTRYPOINT(我的初始化脚本)中定义的脚本运行后,它出现在日志中,但我的容器似乎已经退出。因此,我使用 RUN 命令而不是 ENTRYPOINT 来运行脚本,并且容器仍在后台运行。

S
Soviut

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

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

(显然这仅用于开发目的,除非容器正在运行一个进程,例如 nginx ...,否则您不需要保持容器处于活动状态。)


我使用的是 CMD["sleep", "1d"],但您的解决方案似乎更好
@GeorgiosPligoropoulos 这将停留在该行;也许在后台运行会起作用
也可以使用 CMD["sleep", "infinity"]
或“猫”,但人们可能会说这是虐待动物。 xD
您可以使用 exec tail -f /dev/null 完成您的入口点脚本,但使用 tail 作为入口点是错误的答案。
a
arne.z

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


我无法重现这个。你能举个例子吗?为了使它工作,我们需要关于 Dockerfile 的任何特定内容(例如:CMD)吗?
这对我不起作用。我使用命令 docker logs <image> 来确保它是导致我的 docker 容器退出的错误。退出状态为 0,最后一个输出是确认我的 lighttpd 服务器正在运行:[ ok ] Starting web server: lighttpd.
我已经有一段时间没有使用 Docker 了。因此,命令行界面可能已更改,并且此命令不再起作用。
我可以确认这确实适用于最新的 docker 版本。如果您想稍后附加到此会话,使用 -dit 也可以。
@Long 脚本不接受 tty,如果未安装 bash,请将 exec bashexec sh 添加到 start.sh 的末尾。然后你可以使用 -t 标志
K
Kostas

这不是你应该如何设计 Docker 容器的方式。

在设计 Docker 容器时,您应该构建它以便只运行一个进程(即您应该有一个用于 Nginx 的容器,一个用于 supervisord 或它正在运行的应用程序);此外,该进程应该在前台运行。

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

但是,如果您真的需要(或想要)在 Docker 容器中运行多个服务,请考虑从 "Docker Base Image" 开始,它使用 runit 作为伪初始化进程(runit 将保留在 Nginx 和 Supervisor 运行时在线),这将保持在前台,而您的其他进程执行它们的操作。

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


你能解释一下为什么我应该只运行一项服务吗?如有必要,我可以将 nginx 添加到主管,但不确定为什么需要这样做。
@Eli 简短的回答是,这就是 Docker 的工作方式。 Docker 每个容器只会运行一个进程(及其子进程)。建议这个进程是一个实际的应用程序进程(这样如果它退出,Docker 知道),但你确实可以使用 supervisor 作为那个进程。请注意,您必须将主管配置为在前台运行(即不是守护进程),这是通过 --nodaemon 选项完成的。
@Eli This Docker blog post 证明了运行多个进程(并且,从广义上讲,将容器视为“小型 VPS”)是次优的。在您的情况下,评论线程可能比实际的博客文章更相关。
Docker 基础镜像对于许多企业问题来说是一个糟糕的解决方案,因为很少有严肃的公司使用 ubuntu,而是更喜欢 RHEL/Centos 树。
“很少有严肃的公司”似乎站不住脚。操作系统的选择似乎完全基于用例。任何给定的公司都有许多不同的环境,包括内部开发人员使用、内部员工使用、销售支持、登台、POC,最后是生产(甚至这是一个模糊的术语)。我不相信 OP 提到了他们的用例,(对不起,挑剔),但这种评论似乎是传播高度自以为是的信息的类型,没有争论为什么。
p
phazei

它退出的原因是因为 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 并且将它们分开没有多大意义。


如果 PID 1 结束 docker 容器停止运行,它在文档中的哪个位置说?
@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.
@phazei 谢谢你。这么多年过去了,这个答案仍然有意义,并且比我拼凑的脚本好得多。
S
Serge Velikan

你可以像@Sa'ad 兄弟提到的那样运行普通的 cat 而不需要任何参数来简单地保持容器工作[实际上除了等待用户输入什么都不做](Jenkins' Docker 插件做同样的事情)


除了我的回答:但请理解 docker-compose (非守护程序)用于向您展示容器的工作流程,因此跟踪已启动服务的日志文件可能会很方便。干杯
cat。 jenkin 的 docker 插件就是这样做的。
i
itsafire

动机:

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 强行杀死之前,简单的 sleepcat 将需要几秒钟。

更新

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

这是一个意见。 the docs 正指向这个方向。一句话:“拥有多个进程是可以的,但要从 Docker 中获得最大收益,请避免一个容器负责整个应用程序的多个方面。”当然,将复杂的服务分成多个容器显然要强大得多。但在某些情况下,走一条集装箱路线可能会有所帮助。特别是对于电器。 GitLab Docker 图像是我最喜欢的多进程容器示例。它使这个复杂系统的部署变得容易。没有办法配置错误。 GitLab 保留对其设备的所有控制权。双赢。


我自定义了 centos7 基础映像以加载 PostgreSQL 11。您可以通过调用 /usr/pgsql-11/bin/pg_ctl 来启动它,但是一旦服务器运行,pg_ctl 就会退出。您使用陷阱的建议效果很好;这是我脚本 pgstartwait.sh 的最后一行
说在一个容器中运行多个进程并没有错,然后用这句话链接到开头说这不是最好的主意的文档,这有点奇怪……
@CharlesDesbiens 感谢您的意见。请参阅我的更新回复。
i
iTech

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

然后使用以下命令将主管作为服务和 nginx 作为前台进程运行,这将阻止容器退出

service supervisor start && nginx

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

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


u
user2825611

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

wait $NGNIX_PID 

这样,您的容器应该一直运行到 ngnix 还活着,当 ngnix 停止时,容器也会停止


u
user8210351

自 docker engine v1.25 起,an option 被称为 init
Docker-compose included this commandversion 3.7 起。

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

CMD ["sleep", "infinity"]

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

docker build
docker run --rm --init app

crf.: rm docsinit docs


B
Binita Bharati

除了在 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


A
Audrius Meškauskas

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

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

while true; do
  sleep 100
done

随着开发的进展,你会用更严肃的东西来代替它。


I
Iñigo

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

服务 YOUR_SERVICE 监督

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

无需创建 supervisord.conf