如何使用不同的命令启动已停止的Docker容器?

441
我想使用不同的命令启动已停止的Docker容器,因为默认命令崩溃了-这意味着我无法启动容器然后使用“docker exec”。

基本上我想启动一个shell以便检查容器的内容。
幸运的是,我使用了-it选项创建容器!
13个回答

613

查找已停止的容器ID

docker ps -a

提交已停止的容器:

此命令将修改后的容器状态保存为名为 user/test_image 的新镜像:

docker commit $CONTAINER_ID user/test_image

使用不同的入口点启动/运行:

docker run -ti --entrypoint=sh user/test_image

入口点参数说明:

https://docs.docker.com/engine/reference/run/#/entrypoint-default-command-to-execute-at-runtime

注意:

上面的步骤只是使用相同的文件系统状态启动了一个停止的容器,这对于快速调查非常有用;但是环境变量、网络配置、附加卷和其他内容不会被继承。您应该明确指定所有这些参数。

从这里借鉴了启动停止容器的步骤:(最后一条评论)https://github.com/docker/docker/issues/18078


2
不,图像是只读的。它将修改后的容器状态保存为新的镜像test_image。 - Dmitriusan
27
这个缺少了关于环境变量、卷、UID等的几乎所有配置信息。它与已停止的容器唯一相同的是文件系统(对于某些情况可能足够)。 - Florian Klein
5
如果我能以某种方式获取相同的环境、网络配置和已挂载的卷,那就太好了。是否有可能将“inspect”输出转换为用于后续运行的配置呢? - Otheus
2
@Webman,是的,但对于在停止容器之前挂载的卷来说并不是这样。下次启动容器时,您将需要显式地附加相同的卷。 - Dmitriusan
2
@EmreTapcı,我认为这样做违背了Docker的理念。容器旨在成为一次性的运行和丢弃实体,与虚拟机相反。您可以尝试遵循aaa90210的答案,但这将是一种hack方法。 - Dmitriusan
显示剩余10条评论

187

编辑此文件(对应您已停止的容器):

vi /var/lib/docker/containers/923...4f6/config.json

将“路径”参数更改为指向您的新命令,例如/bin/bash。您还可以设置“参数”参数以将参数传递给命令。

重新启动Docker服务(请注意,这将停止所有正在运行的容器,除非您首先启用live-restore):

service docker restart
列出您的容器并确保命令已更改:

docker ps -a

启动容器并进入它,现在您应该在您的 shell 中!

docker start -ai mad_brattain

在 Fedora 22 上使用 Docker 1.7.1 工作。

注意:如果您的 shell 不是交互式的(例如,您没有使用 -it 选项创建原始容器),则可以将命令更改为 "/bin/sleep 600" 或 "/bin/tail -f /dev/null",以便有足够的时间执行 "docker exec -it CONTID /bin/bash",作为获取 shell 的另一种方式。

注意2:新版本的 Docker 具有 config.v2.json,在那里您需要更改 Entrypoint 或 Cmd (感谢 user60561)。


94
我的眼睛。我的眼睛。我希望在Docker中能够有一个功能请求来正确地处理这个问题。 - gertvdijk
10
这是这个问题唯一真正准确的答案:其他所有的提议都运行了一个“几乎相同”的容器,但它们忽略了卷、环境变量、用户标识等因素。 - Florian Klein
6
在我的情况下,/usr/bin/sleep 不可用。我尝试了 ..."Path":"tail","Args":["-f","/dev/null"]... 并且成功了。 - nevrome
11
较新的Docker版本具有config.v2.json,您将需要更改EntrypointCmd中的任一项。 - flaviut
6
重新启动容器后配置文件总是回到之前的版本。 - Mr Jedi
显示剩余16条评论

31

在入口点脚本的顶部添加一个检查

虽然Docker真的需要实现这个作为新特性,但在一些情况下,如果你的Entrypoint在成功或失败后终止,这可能会使调试变得困难。下面是另一种解决办法。

如果您还没有Entrypoint脚本,请创建一个运行您容器所需命令的脚本。然后,在此文件的顶部,将以下行添加到entrypoint.sh中:

# Run once, hold otherwise
if [ -f "already_ran" ]; then
    echo "Already ran the Entrypoint once. Holding indefinitely for debugging."
    cat
fi
touch already_ran

# Do your main things down here

为了确保cat保持连接,您可能需要提供一个TTY。我正在使用我的入口脚本运行容器,如下所示:
docker run -t --entrypoint entrypoint.sh image_name

这将导致脚本运行一次,创建一个文件,表示它已经在容器的虚拟文件系统中运行过。然后,您可以重新启动容器以进行调试:
docker start container_name

当您重新启动容器时,将找到already_ran文件,导致Entrypoint脚本使用cat停顿(它只是永远等待永远不会到来的输入,但保持容器活动)。然后,您可以执行调试bash会话:
docker exec -i container_name bash

在容器运行时,您也可以删除already_ran并手动执行entrypoint.sh脚本以重新运行它,如果您需要以这种方式进行调试。


8
此外,您可以将入口点更改为运行/bin/sh而不是cat-- 这样您只需重启就可以进入。您的解决方案非常棒! - Danny Dulai
cat and /bin/sh didn't halt execution for me, I ended up looping / sleeping indefinitely. while : do sleep 3600 done - Jeff Ward

9

使用docker-compose run命令并指定cont_id_or_name参数,进入容器的/bin/bash终端。(为了方便,可以将环境变量和卷挂载在docker-compose.yml文件中)

或者使用docker run命令并手动指定所有参数。


6

这可能不完全符合你的要求,但是如果你只想检查文件,你可以在已停止的容器上使用 docker export 命令。

mkdir $TARGET_DIR
docker export $CONTAINER_ID | tar -x -C $TARGET_DIR

5
我采用了@Dmitriusan的答案并将其制作成了一个别名:
alias docker-run-prev-container='prev_container_id="$(docker ps -aq | head -n1)" && docker commit "$prev_container_id" "prev_container/$prev_container_id" && docker run -it --entrypoint=bash "prev_container/$prev_container_id"'
将这个别名添加到您的~/.bashrc别名文件中,您就会有一个新的巧妙的别名docker-run-prev-container,它会将您放入上一个容器的 shell 中。
对于调试失败的docker build非常有帮助。

4

似乎Docker无法在容器启动后更改入口点,但您可以设置自定义入口点,并在下次重新启动容器时更改入口点的代码。

例如,您可以像这样运行一个容器:

docker run --name c --entrypoint "/boot" -v "./boot":/boot $image

这是引导程序的入口点:
#!/bin/bash
command_a

当您需要使用不同的命令重新启动 C 时,只需更改启动脚本:

#!/bin/bash
command_b

并且重新启动:

docker restart c

3
我找到了一个简单的命令。
docker start -a [container_name]

这将解决问题。
或者。
docker start [container_name]

那么

docker exec -it [container_name] bash

19
如果容器在启动时立即崩溃,那么这种方法就无法奏效。 - aaa90210
1
如果按照以下方式运行,它可以工作:docker start [container_name] && docker exec -it [container_name] bash - nggit
1
@nggit 如果容器立即出现错误并退出,这仍然无法解决问题。docker exec 无法保持容器的运行。 - dboshardy
@dboshardy 对我来说这很有效,只要存在“bash”或“sh”。 所以我们可以通过在shell中运行所述的原始CMD / ENTRYPOINT,来调试为什么原始CMD / ENTRYPOINT会立即退出。 只要容器中存在并且可以执行,“bash”或“sh”就可以被另一个程序替换。 根据问题“运行不同的命令”,即bash,sh等。 - nggit
1
我只是想,在“立即退出”之前,可以使用“&&”稍微覆盖PID 1的一点时间。 - nggit
显示剩余2条评论

2

我的问题:

  • 我使用docker run <IMAGE_NAME>命令启动了一个容器
  • 然后我向这个容器中添加了一些文件
  • 之后我关闭了这个容器,并尝试使用与上述相同的命令再次启动它
  • 但是当我检查新文件时,它们不见了
  • 当我运行docker ps -a时,我可以看到两个容器。
  • 这意味着每次我运行docker run <IMAGE_NAME>命令时,都会创建一个新的镜像

解决方案:

  • 运行docker ps来获取您的容器的ID
  • 运行docker container start <CONTAINER_ID>来启动现有的容器
  • 然后您可以从离开的地方继续。例如:docker exec -it <CONTAINER_ID> /bin/bash
  • 然后您可以决定将其创建为新的镜像

5
这并没有回答问题。提问者想知道如何使用不同的参数重新启动容器,而不是使用“docker run <containerID>”中使用的那些参数。 - CodeBlooded

2

看起来大多数人在修改配置文件时遇到了这个问题,这也是我所做的。我试图为一个具有Vue SPA入口点的PHP / Apache服务器绕过CORS。无论如何,如果您知道你修改的文件,一个简单的解决方案对我有效:

  1. 从镜像中复制您修改过的文件:

    docker cp bt-php:/etc/apache2/apache2.conf .

  2. 在本地修复它

  3. 将其复制回去

    docker cp apache2.conf bt-php:/etc/apache2/apache2.conf

  4. 重新启动容器

  5. *额外加分 - 由于该文件正在被修改,请将其添加到Compose或Build脚本中,以便在正确时它将被烤入镜像!


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