Docker容器在运行"docker run -d"命令后会自动停止

562

根据我迄今为止阅读的教程,使用"docker run -d"将启动一个容器从镜像中,并且该容器将在后台运行。它看起来像这样,我们可以看到我们已经有了容器ID。

root@docker:/home/root# docker run -d centos
605e3928cdddb844526bab691af51d0c9262e0a1fc3d41de3f59be1a58e1bd1d

但如果我运行"docker ps",没有任何返回。

所以我尝试运行"docker ps -a",我可以看到容器已经退出:

root@docker:/home/root# docker ps -a
CONTAINER ID        IMAGE                 COMMAND             CREATED             STATUS                         PORTS               NAMES
605e3928cddd        centos:latest         "/bin/bash"         31 minutes ago      Exited (0) 31 minutes ago                          kickass_swartz

我有做错什么吗?如何排除这个问题?


2
"docker run hello-world" <== 运行正常,但如果我运行 "docker run -d hello-world",我仍然无法获得正在运行的容器。 - J John
9
我之前也遇到了类似的问题,但是我通过使用 docker run -it -d <image> /bin/bash 解决了它。这个命令会启动一个交互式的bash shell,并且不会关闭容器,因为bash shell进程在活动状态。 - Rtsne42
23个回答

813

Centos Dockerfile有一个默认的命令bash

这意味着,当在后台运行时(-d),shell立即退出。

2017年更新

更近版本的Docker授权在分离模式下运行容器,同时也可以在前景模式-t-i-it)下运行容器。

在这种情况下,您不需要任何额外的命令,这已经足够了:

docker run -t -d centos

bash 将在后台等待。
这最初是在 kalyani-chaudhari答案 中报告的,并在 jersey bean答案 中详细说明。

vonc@voncvb:~$ d ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
4a50fd9e9189        centos              "/bin/bash"         8 seconds ago       Up 2 seconds                            wonderful_wright

请注意,对于alpineMarinos An评论中报告:

docker run -t -d alpine/git无法保持进程运行。
必须执行:docker run --entrypoint "/bin/sh" -it alpine/git


原始答案(2015年)

this article所述:

不要使用 docker run -i -t image your-command,而是建议使用 -d,因为您可以只用一个命令运行容器,而且不需要通过按下 Ctrl + P + Q 来分离容器终端。

然而,-d 选项存在一个问题。除非命令在前台持续运行,否则容器会立即停止
Docker 要求您的命令在前台持续运行。否则,它会认为您的应用程序已停止并关闭容器。

问题在于某些应用程序无法在前台运行。我们该怎么办呢?

在这种情况下,您可以将 tail -f /dev/null 添加到您的命令中。
这样,即使您的主命令在后台运行,由于 tail 在前台持续运行,您的容器也不会停止。

因此,以下内容可行:

docker run -d centos tail -f /dev/null

或者在 Dockerfile 中:

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

运行 docker ps 命令会显示 centos 容器仍在运行。

从那里,您可以 连接或分离(或使用 docker exec 执行一些命令)。


然而,正如bviktor评论中所指出的:

覆盖入口点(entrypoint)将破坏您的Dockerfile,如果它有任何指定的内容。
systemd尤其值得关注。

跟踪/dev/null听起来也不是很令人信服:实质上,您会用完全不必要的系统调用轰击您的系统。
使用另一种解决方案看起来要好得多。

这依赖于sleep infinity,这是GNU coreutils扩展,在POSIX中没有考虑到。


1
添加了tail -f /dev/null之后,我无法重新附加到shell,虽然我知道原因,但有没有办法让它重新工作? - Guruprasad GV
6
预计会这样。不要使用 docker attach,而是使用 docker exec -it <你的容器> bash - VonC
4
如果在入口文件的末尾添加tail命令,这也能起作用。 - yara
2
我使用sshd作为我的最后一个(长时间运行的)命令。然后,您可以像在虚拟机上一样ssh到容器中(一旦设置了.ssh/authorized_keys等)。您还可以使用ansible配置容器。 - andrew pate
1
我最终执行了以下操作,以执行我的命令并添加上面列出的旧修复方法:docker run image_name /bin/bash -c "my/command && tail -f /dev/null" - Geordie
显示剩余12条评论

97
根据此回答,添加-t标志将防止容器在后台运行时退出。然后,您可以使用docker exec -i -t <image> /bin/bash进入shell提示符。
docker run -t -d <image> <command>

看起来-t选项没有很好地记录在文档中, 尽管帮助信息说它“分配一个伪终端”。


15
好的。这种方法似乎比附加 tail -f /dev/null 更加规范。 - Scarysize
谢谢!尽管可能性很小,但这在Emacs Eshell中的表现并不好。 - Peter Becich
7
在我的电脑上(Ubuntu 14.04.5),docker run -t -d --name mysql -p 3306:3306 mysql 命令无法正常工作,结果状态为 STATUS=Exited (1),时间是 4 秒前。 - Putnik
我发现除非你想要,否则这里不需要 <command>。奇怪的是,用 -i 替换 -t 也可以工作。文档确实提到使用 -t 和 -i 结合会像一个 shell 一样运行。 - jersey bean
<image> 应该是容器的名称,对吗? - CyberPlayerOne
1
@CyberPlayerOne...<image> 应该是图像名称。 "docker run" 是一个旧命令,他们为了向后兼容性而保留它。您可以在以下链接中找到更多信息:https://dev59.com/BFUK5IYBdhLWcg3w0Cq9#51247809 和 https://www.docker.com/blog/whats-new-in-docker-1-13/ - babybob

89

背景

Docker容器运行一个进程(“命令”或“入口点”),使其保持活动状态。只要命令继续运行,容器就会继续运行。

在您的情况下,命令(默认情况下为/bin/bash)立即退出(当bash未连接到终端且没有任何要运行的内容时,它就会这样做)。

通常,当您以守护程序模式(使用-d)运行容器时,容器正在运行某种守护程序进程(如httpd)。在这种情况下,只要httpd守护程序正在运行,容器就会保持活动状态。

您似乎想要在容器内部不运行守护程序进程的情况下保持容器活动。这有点奇怪(因为在您与其交互之前,容器不会执行任何有用的操作,例如使用docker exec),但在某些情况下可能有意义。

(您是想在容器内部获取bash提示符吗?那很简单!docker run -it centos:latest

解决方案

在守护程序模式下使容器无限期保持活动状态的简单方法是将sleep infinity作为容器的命令运行。这不依赖于在守护程序模式下分配TTY之类的奇怪操作,但确实依赖于将sleep用作主要命令。

$ docker run -d centos:latest sleep infinity
$ docker ps
CONTAINER ID  IMAGE         COMMAND          CREATED       STATUS       PORTS NAMES
d651c7a9e0ad  centos:latest "sleep infinity" 2 seconds ago Up 2 seconds       nervous_visvesvaraya

替代方案

正如cjsimon所指出的那样,-t选项会分配一个“伪终端”。这会欺骗bash认为它连接到一个交互式TTY(即使您没有传递-i也是如此),从而使其无限期地运行下去。无论如何,这也应该能解决问题:

$ docker run -t -d centos:latest

不确定-t是否会产生其他奇怪的交互作用;如果确实有,请在下面留言。


5
请注意,此方法无法在Alpine上运行,因为BusyBox的sleep命令不支持infinity参数。 - John Kugelman
这帮助我解决了一个Amazon Linux镜像的问题,谢谢。 - influent

24

嗨,这个问题是因为如果容器中没有正在运行的应用程序,Docker 容器会停止。

-d 

选项只是以守护程序模式运行容器。

因此,让您的容器持续运行的技巧是,在Docker中指向一个shell文件,该文件将使您的应用程序保持运行状态。 您可以尝试使用start.sh文件。

Eg: docker run -d centos sh /yourlocation/start.sh

这个 start.sh 应该指向一个永久的应用程序。

如果你不想运行任何应用程序,可以安装 monit,它将保持你的 Docker 容器运行。 请告诉我们这两种情况是否对您保持容器运行有帮助。

祝一切顺利!


19

你可以使用以下任一方法实现你想要的目标:

docker run -t -d <image-name>

或者

docker run -i -d <image-name>

或者

docker run -it -d <image-name>

其他答案建议的命令参数(即tail -f /dev/null)是完全可选的,并且不需要这个参数就可以让您的容器在后台运行。

此外,请注意Docker文档建议将-i和-t选项组合使用,将使其行为类似于shell。

参见:

https://docs.docker.com/engine/reference/run/#foreground


15

我有这段代码片段从我的Docker文件中的ENTRYPOINT运行:

while true
do
    echo "Press [CTRL+C] to stop.."
    sleep 1
done

以以下方式运行构建好的 Docker 镜像:

docker run -td <image name>

登录到容器 shell:

docker exec -it <container id> /bin/bash

1
你创建了一个无限循环的解决方案吗? :D - Salman Saleem

12

请按以下方式执行命令:

docker run -t -d <image-name>

如果你想指定端口,则使用以下命令:

docker run -t -d -p <port-no> <image-name>

使用以下命令验证正在运行的容器:

docker ps

1
我实际上最喜欢这个答案,因为最受欢迎的答案建议您需要一个命令(即tail -f /dev/null)。该命令是完全可选的。关键在于使用-t。我还发现-i可以代替-t,或者您也可以同时使用-it组合(正如文档建议它将作为shell运行)。 - jersey bean
使用“-t -d”与“-i -d”相比,有什么优势吗?两者都可以保持容器运行。 - wisbucky

11

如果Docker容器内的任务完成后就退出了,但如果你希望即使没有任务或已经完成任务时仍然保持容器运行状态,可以执行docker run -di image命令。执行docker container ls命令后,您将看到它正在运行。


7

Docker要求您的命令在前台持续运行。否则,它会认为您的应用程序已停止并关闭容器。

因此,如果您的Docker入口脚本是像下面这样的后台进程:

/usr/local/bin/confd -interval=30 -backend etcd -node $CONFIG_CENTER &

'&' 的作用是在没有其他前台进程触发时,使容器停止和退出。 因此,解决方法就是删除 '&' 或在其后运行另一个前台 CMD,例如:

tail -f server.log

可能需要先使用 touch 命令触碰一下那个文件,以防找不到日志文件。 - Ismail
通常我跟踪的日志文件是当前服务器日志或重定向输出,这些应该在进程启动后就已经存在了。 - Peiming Hu

6
如果您在 Dockerfile 的末尾使用 CMD,您可以在末尾添加以下代码。仅当您的 docker 建立在 Ubuntu 或任何能够使用 bash 的操作系统上时才会生效。
&& /bin/bash

简而言之,您的Dockerfile的结尾将看起来像这样。
...

CMD ls && ... && /bin/bash

如果您在运行Docker镜像后自动执行某些操作,当任务完成时Bash终端将处于活动状态。因此,您可以输入shell命令。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接