如何在已有的Docker容器中运行命令?

600

我使用了-d参数创建了一个容器,因此它不是交互式的。

docker run -d shykes/pybuilder bin/bash

我看到容器已经退出:

CONTAINER ID        IMAGE                     COMMAND             CREATED             STATUS                      PORTS               NAMES
d6c45e8cc5f0        shykes/pybuilder:latest   "bin/bash"          41 minutes ago      Exited (0) 2 seconds ago                        clever_bardeen

现在我想在机器上运行偶尔的命令并退出,只是为了获取响应。

我尝试启动机器。我尝试附加。我认为我可以使用容器调用run,但似乎不被允许。使用start似乎只是快速运行然后退出。

我希望在退出后能够重新进入交互模式。

我尝试过:

docker attach d6c45e8cc5f0

但我得到:

2014/10/01 22:33:34 You cannot attach to a stopped container, start it first

但如果我开始它,它无论如何都会退出。进退两难。我无法获胜。


你是怎么知道 Docker 容器已经退出的?你运行了什么命令? - Thufir
docker container ls -a - Brandon Manchester
如果您只需要文件系统:如何使用不同的命令启动已停止的 Docker 容器?(请注意,当容器停止时,内存中的环境变量和其他东西已经丢失。) - Franklin Yu
20个回答

631

2014年10月,Docker团队推出了docker exec命令: https://docs.docker.com/engine/reference/commandline/exec/

现在,只需要知道容器的ID(或名称),就可以在运行中的容器中运行任何命令:

docker exec -it <container_id_or_name> echo "Hello from container!"

请注意,exec 命令仅适用于已运行的容器。如果容器当前已停止,则需要首先使用以下命令运行它:
docker run -it -d shykes/pybuilder /bin/bash

这里最重要的是-d选项,它代表detached。这意味着您最初提供给容器的命令(/bin/bash)将在后台运行,容器不会立即停止。

153
该方法仅适用于正在运行的容器,对于已停止的容器无效。因此,如果您有一个会立即停止的容器(如问题中所述),则该方法实际上不能使其内部运行其他内容。 - interfect
7
@interfect是正确的,而CDR LDN提供了更全面的答案。 - Dr. Jan-Philip Gehrcke
11
顺便提一句,这个人的用户名已经从“CDR LDN”更改为“cdrev”,用于下面的答案(https://dev59.com/Xl8e5IYBdhLWcg3wCW12#26181666)。 - Taylor D. Edmiston
6
为什么要使用-it参数? - Iulian Onofrei
9
哎呀,这为什么这么复杂啊?感觉这是最基本的事情了。我们肯定没用对他们打算的方式使用它。 - sudo
显示剩余9条评论

314
您的容器将会在您输入的命令结束后退出。使用以下选项来保持其运行:
  • -i 即使未附加,也要保持 STDIN 开启。
  • -t 分配一个伪 TTY。
因此,您的新的run命令是:
docker run -it -d shykes/pybuilder bin/bash

如果您想附加到已经运行的容器:

docker exec -it CONTAINER_ID /bin/bash

在这些例子中,/bin/bash被用作命令。

2
尝试使用 docker exec -it CONTAINER_ID /bin/bash -c "export VAR=1 && echo $VAR" 命令,但输出为空(期望输出 1)。我错过了什么? - fabda01
1
运行命令 'docker exec -it CONTAINER_ID /bin/bash' 后,可以正确进入 bash 但无法与其交互。 - Blue Clouds
1
但是如果我使用docker-compose,那么-it选项就不可用了。 - adnanmuttaleb

198

我认为答案比上面许多误导性答案都要简单。

启动一个已经停止的现有容器

docker start <container-name/ID>

停止正在运行的容器

docker stop <container-name/ID>

然后登录容器的交互式 shell

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

一条命令启动现有容器并附加到它

docker start -ai <container-name/ID>

注意,这将在退出时停止容器。但通常情况下,您需要启动容器,在完成后附加并停止它。


17
@Peter T. 实际上,我发现你的回答比其他人提供的要简洁得多。我不理解为什么人们喜欢把一个非常简单的问题复杂化。谢谢Peter的回答。 - Helen Neely
3
这意味着当您使用docker create命令创建容器时,必须使用-it参数,否则容器将无法启动。因此,您需要运行docker start <container-id>命令,然后运行docker ps -l命令,以查看容器是否已启动。如果容器未启动,则附加操作将失败。因此,也必须使用-it参数来创建容器。 - barlop
2
@Peter 最相关的答案 - Nilanjan Sarkar
2
这是最准确的答案! - nagendra547
6
我执行docker start -ai <ID>,但它立即停止。所以我无法进入容器。应该如何创建容器以允许进入? - RodriKing
显示剩余7条评论

105

继承katrmr的回答,如果容器由于错误而无法启动并且已经停止,您需要将其提交为一个镜像。然后,您可以在新镜像中启动bash:

docker commit [CONTAINER_ID] temporary_image
docker run --entrypoint=bash -it temporary_image

1
FYI,我经常这样做,所以我编写了一个名为“dshell”的命令来自动处理各种情况 - https://github.com/avirshup/docker-cli-sugar - Aaron V

43
这里有一些误导性的答案,因为它们涉及正在运行而非已停止的容器。
在 Docker 论坛上,Sven Dowideit 解释说容器绑定到其进程(Docker 无法更改已停止容器的进程,似乎至少是由于其内部结构:https://github.com/docker/docker/issues/1437)。所以,基本上唯一的选择是将容器提交为映像,然后使用不同的命令运行它。
请参见https://forums.docker.com/t/run-command-in-stopped-container/343 (我认为“带参数的 ENTRYPOINT”方法也不起作用,因为您仍然无法更改已停止容器的参数。)

2
注意:在容器中运行bin/bash而不带-it参数不会改变任何内容,因此提交它并不是必要的。CDR LDN已经为OP的特定情况给出了正确的答案。尽管如此,commit仍然是解决如何更改容器进程的技术问题的答案。 - katrmr
candlerb在run-command-in-stopped-container的评论中建议使用一个临时映像,该映像包含来自未激活容器的卷。对我有效的命令是:docker run --rm --volumes-from CONTAINER -i busybox tar cO /var/DIR | gzip -c > ~/mydir_backup.tgz。 - eel ghEEz
这是对所提出问题的实际答案。容器绑定到它们的进程,因此命令无法更改。 - cjsimon

25

我必须使用 bash -c 来运行我的命令:

docker exec -it CONTAINER_ID bash -c "mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql mysql"

1
-c 对我起作用。不知道为什么仅使用bash不起作用(没有得到提示符)。 - André Werlang

19

创建一个容器并逐个发送命令:

docker create --name=my_new_container -it ubuntu
docker start my_new_container
// ps -a says 'Up X seconds'
docker exec my_new_container /path/to/my/command
// ps -a still says 'Up X+Y seconds'
docker exec my_new_container /path/to/another/command

1
这是对该问题的正确答案。如果你想在创建之后启动容器并能够"docker exec"命令进入它,则必须在docker create命令中使用"-it"标志来创建它。 - joanlofe

10
如果您想运行 shell 脚本,需要使用 bash 命令来运行。
docker exec -it containerid bash -c /path/to/your/script.sh

这就是我想要做的,非常感谢。 - Máxima Alekz

8

我想指出顶部答案有些误导。

使用docker run的问题在于每次都会创建一个新的容器。但是,有些情况下我们希望回访旧容器或不使用新容器占用空间。

(假设clever_bardeen是创建的容器的名称...)

在OP的情况下,请确保docker镜像首先正在运行,通过执行以下命令:

docker start clever_bardeen

接着,使用以下命令执行docker容器:

docker exec -it clever_bardeen /bin/bash

8
我用CDR LDN答案和我在这里找到的答案合并起来得出了以下答案。
以下示例从一个映像开始启动Arch Linux容器,然后使用pacman工具在该容器上安装git:
sudo docker run -it -d archlinux /bin/bash
sudo docker ps -l
sudo docker exec -it [container_ID] script /dev/null -c "pacman -S git --noconfirm"

那就这样吧。


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