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

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

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

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

【问题讨论】:

    标签: docker


    【解决方案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 不仅不支持这些基本的管理任务,它还试图阻止它们。

          解决方案:

          1. cd /var/lib/docker/overlay2/
            
          2. find | grep somechangedfile 
            # You now can see the changed file from your container in a hexcoded folder/diff
            
          3. cd hexcoded-folder/diff
            
          4. 创建一个entrypoint.sh(如果存在,请确保备份一个现有的)

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

            Ctrl+C

             chmod +x entrypoint.sh
            
          5. 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 容器,覆盖现有的入口点脚本。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-11-01
                • 2019-05-21
                • 1970-01-01
                相关资源
                最近更新 更多