为什么Docker容器会立即退出

440

我使用以下命令在后台运行一个容器:

 docker run -d --name hadoop h_Service

它迅速退出。但如果我在前台运行,它就能正常工作。我使用日志进行了检查。

docker logs hadoop

没有错误。有什么想法吗?

DOCKERFILE

 FROM java_ubuntu_new
 RUN wget http://archive.cloudera.com/cdh4/one-click-install/precise/amd64/cdh4-repository_1.0_all.deb
 RUN dpkg -i cdh4-repository_1.0_all.deb
 RUN curl -s http://archive.cloudera.com/cdh4/ubuntu/precise/amd64/cdh/archive.key | apt-key add -
 RUN  apt-get update
 RUN apt-get install -y hadoop-0.20-conf-pseudo
 RUN dpkg -L hadoop-0.20-conf-pseudo
 USER hdfs
 RUN hdfs namenode -format
 USER root
 RUN apt-get install -y sudo
 ADD . /usr/local/
 RUN chmod 777 /usr/local/start-all.sh
 CMD ["/usr/local/start-all.sh"]

启动所有.sh脚本

 #!/usr/bin/env bash
 /etc/init.d/hadoop-hdfs-namenode start
 /etc/init.d/hadoop-hdfs-datanode start
 /etc/init.d/hadoop-hdfs-secondarynamenode start
 /etc/init.d/hadoop-0.20-mapreduce-tasktracker start
 sudo -u hdfs hadoop fs -chmod 777 /
 /etc/init.d/hadoop-0.20-mapreduce-jobtracker start
 /bin/bash

4
黄金法则是,您应该防止将Docker化的服务器进行守护进程化。大多数服务器软件包都有选项可以强制它们在前台运行,因为守护进程化是正常情况。 - Arnaud Meuret
8
无论你希望完成什么任务,使用"chmod 777"是不安全且错误的做法。你应该恢复合理的权限设置(在这种情况下可能是755)。 - tripleee
20个回答

386

这对我起了作用:

docker run -dit ubuntu

之后,我使用以下命令检查正在运行的进程:

docker ps -a

重新安装容器

docker attach CONTAINER_NAME

提示:要退出而不停止容器,请键入:^P^Q

Translated:

提示:要退出而不停止容器,请键入:^P^Q


36
@Tommy,来自 https://docs.docker.com/engine/reference/commandline/run/ -d, --detach 以后台模式运行命令 -i, --interactive 即使未附加,也保持 STDIN 打开 -t, --tty 分配一个伪终端"-dit" 只是缩写。 - user238638
6
@am17torres,对的,抱歉让我澄清一下我的混淆问题; d 表示分离(detached),i 表示交互式(interactive),因此 d 和 i 的组合对我来说很令人困惑。我认为 d 是将其作为后台(非交互式)进程启动。 - Tommy
3
当这些选项组合在一起时,容器将在后台进入交互模式。 - YON
3
如果我理解正确的话,-di 是最低要求,当与 -d 一起使用时,-t 选项是多余的。 - Renaud
2
如果主进程立即退出,这并没有帮助。 - Neftanic
显示剩余7条评论

209

当主进程完成时,Docker容器将退出。

在这种情况下,当您的start-all.sh脚本结束时,容器将退出。我对Hadoop了解不够,无法告诉您如何处理此情况,但您需要在前台保留一些运行内容或使用像runit或supervisord这样的进程管理器来运行进程。

我认为,如果您没有指定-d,它能正常工作是错误的;它应该具有完全相同的效果。我怀疑您使用了稍微不同的命令或使用了-it来改变一些东西。

一个简单的解决方案可能是在脚本末尾添加类似以下的内容:

while true; do sleep 1000; done

但我不喜欢这样做,因为脚本应该确实监控它启动的进程。

(我应该说我从https://github.com/sequenceiq/hadoop-docker/blob/master/bootstrap.sh中窃取了该代码)


95

我希望你能扩展或者说,改进由camposer提到的答案。

当你运行

docker run -dit ubuntu

你基本上是在交互模式下后台运行容器。

当您通过CTRL + D附加并退出容器(最常用的方法),您会停止容器,因为您刚刚杀死了您使用上述命令启动容器的主进程。

利用已经运行的容器,我只需分叉另一个bash进程,并通过运行以下命令获取伪TTY:

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

78

为什么Docker容器会立即退出?

如果您想强制镜像停留(以便调试某些内容或检查文件系统的状态),可以覆盖入口点(entry point)将其更改为shell:

docker run -it --entrypoint=/bin/bash myimagename

当我使用 --entrypoint 时,它只保持3秒,我能保持更长时间吗? - Sameera De Silva
这应该会使它保持运行状态,直到 bash 进程退出。你的镜像没有 /bin/bash,可能是因为某种原因吗? - RJFalconer
是的,一旦我的bash进程结束,它就会退出。我认为这是设计上的原因。docker exec -it MyAvalanche /bin/avalanche - Sameera De Silva

49
每当我想要一个容器在脚本执行结束后仍然保持开启状态,我会添加

&& tail -f /dev/null

在命令的结尾加上一个句点。因此应该是:

/usr/local/start-all.sh && tail -f /dev/null

3
这可能解决问题,但我想知道这个命令是否会造成内存泄漏或其他问题?与运行 while true; do sleep 1; done; 相比如何? - Flying onion

31
如果您只需要运行一个容器而不退出,请运行以下命令:
docker run -dit --name MY_CONTAINER MY_IMAGE:latest

然后

docker exec -it MY_CONTAINER /bin/bash

你将进入容器的 bash shell,并且它不应该退出。

以下内容仅适用于 ROS 模拟中的 roslaunch,这很可能不是 docker-compose 的默认参数!

注意:这段话很难理解,似乎只是关于一个自定义参数。可以跳过。

或者如果在 docker-compose 过程中发生退出,请使用

command: bash -c "MY_COMMAND --wait"

正如其他两个回答所述(尽管没有明确提到docker-compose,这就是为什么我还要再次提到“wait”技巧的原因)。

在ROS模拟中,例如:

command: bash -c "roslaunch gazebo_ros empty_world.launch --wait"

使用--wait是在docker-compose中实现所谓的ROS网络的核心,示例代码请参见ROS在docker-compose中导致"bash: line 0: cd: MYPROJECT: No such file or directory"。更多关于roslaunch的信息请访问WSL2上的Docker:Dockerfile:如何测试ROS gazebo是否可以连接到已经工作的X服务器(使用Windows上的X11显示)?

因此,这个--wait必须是像roslaunch gazebo_ros empty_world.launch这样的命令的参数。但当查看链接教程教程:使用roslaunch启动Gazebo、world文件和URDF模型时,代码中没有这样的参数,只有:

roslaunch gazebo_ros empty_world.launch paused:=true use_sim_time:=false gui:=true throttled:=false recording:=false debug:=true verbose:=true gui_required:=true

这就是我猜测它一定是在项目的给定参数的基础上自行编写的扩展功能的原因。

我在其他设置中再次尝试了--wait,但没有起作用。在 ROS 启动脚本中,它起作用,很可能只是掩盖了该问题/答案中另一种解决方法的变通方法&& tail ...)。


你能详细说明一下这里所说的'MY_COMMAND'是什么意思吗?你是指应该编写一个带有--wait选项的自己的bash脚本吗?这样的脚本会是什么样子呢? - Delon
例如,如果您运行一个Python文件:command: bash -c“python main.py --wait”。您可以将任何您想要在容器终端中运行的内容放在那里。不需要脚本,只需像在Bash中运行的其他任何内容一样可能 - questionto42
也许这是一个有用的命令:docker run -d -p 8888:8888 -it --name indexing_8 -v /mount/xyz:/mount/xyz -v /pylucene-docker:/code coady/pylucene:8 - Anjani Dhrangadhariya
我不认为这还有效,现在是2023年了。 - user1034912
@user1034912 你是指 --wait 技巧吗?我现在检查了一下它的来源。很可能是 ROS 模拟器的自编参数扩展。 - questionto42

28
将此添加到Dockerfile的末尾:
CMD tail -f /dev/null

示例Docker文件:

FROM ubuntu:16.04

# other commands

CMD tail -f /dev/null

参考资料


1
CMD tail -f /dev/null runs it through sh -c "...". Can we use the exec form instead? I.e. CMD ["tail", "-f", "/dev/null"] - Meglio
它对我起作用了。 - ChauGiang
这是一个非常棒的平台无关选项,不需要使用不同的选项来执行容器。 - brandonscript

23

一个不错的方法是在后台启动您的进程和服务,并在脚本末尾使用 wait [n ...] 命令。在 bash 中,wait 命令强制当前进程:

等待每个指定的进程并返回其终止状态。如果未给出 n,则等待所有当前活动的子进程,并返回状态为零。

我从 Sébastien Pujadas 的 elk 构建的启动脚本 中得到了这个想法。

根据原始问题,您的 start-all.sh 文件应该类似于以下内容...

 #!/usr/bin/env bash
 /etc/init.d/hadoop-hdfs-namenode start &
 /etc/init.d/hadoop-hdfs-datanode start &
 /etc/init.d/hadoop-hdfs-secondarynamenode start &
 /etc/init.d/hadoop-0.20-mapreduce-tasktracker start &
 sudo -u hdfs hadoop fs -chmod 777 /
 /etc/init.d/hadoop-0.20-mapreduce-jobtracker start &
 wait

在docker-compose中,使用--wait也可以解决同样的问题。 - questionto42
“wait” 命令与可能的 “--wait” 选项不同,后者并非大多数命令的标准选项。 - tripleee
你能澄清一下哪个命令有--wait选项吗?我正在使用docker-compose,但是我的镜像和compose文件都没有任何命令或入口点。我们更喜欢在没有入口点的情况下启动容器,然后单独启动我们的服务(不作为容器启动的一部分自动启动)。但显然,在compose中,命令或入口点是必需的。我试图理解这个--wait是什么。 - anuragz

18

您需要使用-d标志运行它,才能将其作为守护进程在后台运行。

docker run -d -it ubuntu bash


docker run -d -it ubuntu 对我来说可行。 - xiaojueguan
哥们,你救了我的一天! 我成功在 Windows 上运行了微软的 mcr.microsoft.com/windows/servercore:ltsc2019 镜像。 - Ilkin Sam Elimov
1
如果你使用-d运行程序,那么-it选项基本上会被忽略。 - tripleee

6

我的做法是在 Dockerfile 中启动一个不会立即退出的 shell:CMD [ "sh", "-c", "service ssh start; bash"],然后运行 docker run -dit image_name。这样,(ssh) 服务和容器就可以正常运行了。


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