【问题标题】:How to keep Docker container running after starting services?启动服务后如何保持 Docker 容器运行?
【发布时间】:2014-11-04 16:24:24
【问题描述】:

我看过一堆教程,它们似乎和我想做的事情一样,但由于某种原因,我的 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(我的 init 脚本)中定义的脚本运行后,它出现在日志中,但我的容器似乎已退出。所以,我没有使用 ENTRYPOINT,而是使用 RUN 命令来运行脚本,并且容器仍在后台运行。

标签: docker


【解决方案1】:

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

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

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

【讨论】:

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

我刚刚遇到了同样的问题,我发现如果您使用 -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 &lt;image&gt; 来确保这是导致我的docker 容器退出的错误。退出状态是0,最后的输出是确认我的lighttpdserver 正在运行:[ ok ] Starting web server: lighttpd.
  • 我已经有一段时间没有使用 Docker 了。所以有可能是命令行界面发生了变化,这个命令不再起作用了。
  • 我可以确认这确实适用于最新的 docker 版本。如果您想稍后附加到此会话,使用 -dit 也可以。
  • @Long 脚本不会接受 tty,如果没有安装 bash,请在 start.sh 末尾添加 exec bashexec sh。然后你可以使用 -t 标志
【解决方案3】:

这并不是你应该设计 Docker 容器的真正方式。

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

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


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

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

【讨论】:

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

它退出的原因是因为 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.
【解决方案5】:

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

【讨论】:

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

动机:

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立即退出

简单的sleepcat 将需要几秒钟,然后容器才会被 docker 强行杀死

更新

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

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

【讨论】:

  • 我定制了 centos7 基础镜像来加载 PostgreSQL 11。你从调用 /usr/pgsql-11/bin/pg_ctl 开始,但是一旦服务器运行,pg_ctl 就会退出。您使用陷阱的建议效果很好;这是我的脚本 pgstartwait.sh 的最后一行
  • 说在一个容器中运行多个进程没有错,然后用这句话链接到开头说这不是最好的主意的文档,这有点奇怪......
  • @CharlesDesbiens 感谢您的意见。请查看我的更新回复。
【解决方案7】:

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

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

service supervisor start &amp;&amp; nginx

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

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

【讨论】:

    【解决方案8】:

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

    wait $NGNIX_PID 
    

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

    【讨论】:

      【解决方案9】:

      除了在您的 docker 文件中添加类似 ENTRYPOINT ["tail", "-f", "/dev/null"] 的内容外,您还应该使用 -td 选项运行 docker 容器。这在容器在远程 m/c 上运行时特别有用。把它想象成你已经 ssh'ed 到一个具有图像的远程 m/c 并启动了容器。在这种情况下,当您退出 ssh 会话时,容器将被杀死,除非它以 -td 选项启动。运行图像的示例命令是:docker run -td &lt;any other additional options&gt; &lt;image name&gt;

      这适用于 docker 版本 20.10.2

      【讨论】:

        【解决方案10】:

        在开发过程中有些情况下还没有服务,但你想模拟它并保持容器存活。

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

        while true; do
          sleep 100
        done
        

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

        【讨论】:

          【解决方案11】:

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

          服务 YOUR_SERVICE 监督

          Once supervise is successfully running, it will not exit unless it is killed or specifically asked to exit.

          无需创建supervisord.conf

          【讨论】:

            猜你喜欢
            • 2020-07-17
            • 1970-01-01
            • 2014-09-27
            • 1970-01-01
            • 2020-11-17
            • 2019-11-24
            • 2018-05-20
            • 2021-09-11
            • 1970-01-01
            相关资源
            最近更新 更多