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

Posted

技术标签:

【中文标题】如何使用不同的命令启动停止的 Docker 容器?【英文标题】:How to start a stopped Docker container with a different command? 【发布时间】:2015-11-27 22:56:28 【问题描述】:

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

基本上我想启动一个 shell,以便检查容器的内容。

幸运的是,我使用 -it 选项创建了容器!

【问题讨论】:

【参考方案1】:

找到您停止的容器 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

【讨论】:

不,图像是只读的。它将修改后的容器状态保存到新图像 test_image 这几乎遗漏了所有关于 env、volumes、UID 的配置……它与停止的容器的共同点是文件系统(这对某些人来说可能就足够了) 如果我能以某种方式获得相同的环境、网络配置、附加卷,那就太好了。是否可以将inspect 输出转换为用于后续运行的配置? @Webman,是的,但对于在停止容器之前挂载的卷而言,情况并非如此。下次启动容器时,您必须明确附加相同的卷 @EmreTapcı,我认为这样做违背了 Docker 的意识形态。与虚拟机相比,容器旨在成为一次性使用的一次性实体。您可以尝试遵循 aaa90210 的答案,但这将是一个 hack。【参考方案2】:

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

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

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

重启 docker 服务(注意这会停止所有正在运行的容器):

service docker restart

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

docker ps -a

启动容器并附加到它,你现在应该在你的 shell 中!

docker start -ai mad_brattain

使用 Docker 1.7.1 在 Fedora 22 上工作。

注意:如果您的 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)。

【讨论】:

我的眼睛。我的眼睛。我希望这是一个功能请求,可以在 Docker 中正确处理。 @AlexeyStrakh 您可以尝试运行“/usr/bin/sleep 600”,然后执行“docker exec -it /bin/bash”来获取 shell。虽然我不确定如何将参数放在那个 Path 变量上。否则,请尝试找到另一个可以存活足够长的命令,以便您执行 exec,或者查看 Dmitriusan 的答案。 这是该问题唯一真正准确的答案:所有其他命题都运行“几乎相同”的容器,但它们忘记了卷、环境、UID…… 在我的情况下 /usr/bin/sleep 不可用。我在..."Path":"tail","Args":["-f","/dev/null"]... 上取得了成功 较新版本的 docker 有 config.v2.json,您需要在其中更改 EntrypointCmd【参考方案3】:

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

Docker 确实需要将此作为一项新功能来实现,但这里有另一个解决方法选项,适用于您的入口点在成功或失败后终止的情况,这会使调试变得困难。

如果您还没有入口点脚本,请创建一个运行容器所需的任何命令的脚本。然后,在该文件的顶部,将这些行添加到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 文件,从而导致入口点脚本与 cat 一起停止(它只是永远等待永远不会出现的输入,但使容器保持活动状态)。然后您可以执行调试bash 会话:

docker exec -i container_name bash

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

【讨论】:

此外,您可以让入口点运行/bin/sh 而不是cat——然后您总是可以在重新启动时进入。您的解决方案非常棒! cat/bin/sh 并没有为我停止执行,我最终无限期地循环/休眠。 while : do sleep 3600 done【参考方案4】:

我接受了@Dmitriusan 的回答并将其设为别名:

别名 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 别名,它会将您放入前一个容器中的外壳中。

帮助调试失败docker builds.

【讨论】:

【参考方案5】:

我的问题:

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

解决方案: 要在您首先创建的同一个容器上运行,请按照以下步骤操作

docker ps 获取容器的容器 docker container start <CONTAINER_ID> 启动现有容器 然后你可以从你离开的地方继续。例如docker exec -it <CONTAINER_ID> /bin/bash 然后您可以决定从中创建一个新图像

【讨论】:

这不能回答问题。 OP 想知道如何重新启动容器,但使用的参数与 docker run <containerID> 中使用的参数不同【参考方案6】:

这并不完全符合您的要求,但如果您只想检查文件,则可以在已停止的容器上使用 docker export

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

【讨论】:

【参考方案7】:

我找到了一个简单的命令

docker start -a [container_name]

这样就可以了

或者

docker start [container_name]

然后

docker exec -it [container_name] bash

【讨论】:

不幸的是,如果容器在您启动时立即崩溃,这也不起作用。 如果像这样运行它可以工作:docker start [container_name] && docker exec -it [container_name] bash @nggit 如果容器立即出错并退出,它仍然无法工作。 docker exec 不会让容器保持活动状态。 @dboshardy 它对我有用,只要“bash”或“sh”存在。所以我们可以调试为什么原来的 CMD / ENTRYPOINT 立即退出,通过在 shell 中运行原来的 CMD / ENTRYPOINT --- “bash” 或 “sh” 可以被另一个程序替换,只要它存在于容器中,可以执行。根据问题“运行不同的命令”,即 bash、sh 等。 我只是想,就在“立即退出”之前,有一点时间可以使用“&&”覆盖 PID 1【参考方案8】:

docker-compose run --entrypoint /bin/bash cont_id_or_name

(对于 conven,将您的 env、vol 挂载在 docker-compose.yml 中)

或使用 docker run 并手动指定所有参数

【讨论】:

【参考方案9】:

对我来说,Docker 总是给人一种它是为一个爱好系统创建的印象,它很适合这个。 如果某些事情失败或不起作用,不要指望有专业的解决方案。

也就是说:Docker 不仅不支持这些基本的管理任务,它还试图阻止它们。

解决方案:

    cd /var/lib/docker/overlay2/
    
    find | grep somechangedfile 
    # You now can see the changed file from your container in a hexcoded folder/diff
    
    cd hexcoded-folder/diff
    

    创建一个entrypoint.sh(如果存在,请确保备份一个现有的)

    cat > entrypoint.sh
    #!/bin/bash
    while ((1)); do sleep 1; done;
    

    Ctrl+C

     chmod +x entrypoint.sh
    
    docker stop
    docker start
    

你现在让你的 docker 容器运行一个无限循环而不是原来的条目,你可以执行 bash 进入它,或者做任何你需要的事情。 完成后停止容器,删除/重命名您的自定义入口点。

【讨论】:

【参考方案10】:

似乎 docker 在容器启动后无法更改入口点。但是你可以设置一个自定义入口点,下次重启时修改入口点的代码。

例如,您运行这样的容器:

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

这里是启动入口点:

#!/bin/bash
command_a

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

#!/bin/bash
command_b

然后重启:

docker restart c

【讨论】:

【参考方案11】:

我有一个 docker 容器,其中 MariaDB 容器在启动时由于 InnoDB 表损坏而不断崩溃。

我为解决我的问题所做的是:

将 docker-entrypoint.sh 从容器复制到本地文件系统(docker cp) 编辑它以包含所需的命令行参数(在我的情况下为--innodb-force-recovery=1) 将编辑后的文件复制回 docker 容器,覆盖现有的入口点脚本。

【讨论】:

以上是关于如何使用不同的命令启动停止的 Docker 容器?的主要内容,如果未能解决你的问题,请参考以下文章

Docker Review - docker 容器 常用命令

如何实现docker服务重启容器不停止

修改 docker 容器的启动参数等信息

02_docker容器命令

docker基本概念2

2.docker 常用命令