如何以后台模式获取正在运行的容器中的bash或ssh?

我想要通过ssh或bash进入一个正在运行的docker容器。请看下面的示例:
$ sudo docker run -d webserver
webserver is clean image from ubuntu:14.04
$ sudo docker ps
CONTAINER ID  IMAGE            COMMAND    CREATED STATUS  PORTS          NAMES
665b4a1e17b6  webserver:latest /bin/bash  ...     ...     22/tcp, 80/tcp loving_heisenberg 

现在我想要得到类似这样的东西(进入正在运行的容器):
``` $ sudo docker run -t -i webserver ``` (或者也可以是 `665b4a1e17b6`)
``` $ root@665b4a1e17b6:/# ```
然而,当我运行上述命令时,我得到了一个新的容器ID:
$ root@42f1e37bd0e5:/#

我使用了 Vagrant,我想要获得与 vagrant ssh 类似的行为。


或者使用sudo docker exec -i -t 665b4a1e17b6 /bin/sh命令来安装apt程序和软件包。 - Yevgenii Shashkov
3请注意,使用SSH进入正在运行的容器是不良实践 - 请参阅此处的解释sudo docker exec -i -t container-name /bin/bash是一个可行的方法。 - patryk.beza
15个回答

答案是Docker的attach命令。所以根据我上面的示例,解决方案将是:
$ sudo docker attach 665b4a1e17b6 #by ID
or
$ sudo docker attach loving_heisenberg #by Name
$ root@665b4a1e17b6:/#

对于 Docker 版本 1.3 或更高版本:感谢用户 WiR3D 提出了另一种获取容器 shell 的方法。如果我们使用 attach,我们只能使用一个 shell 实例。因此,如果我们想要打开一个新的终端并获得一个容器 shell 的新实例,我们只需要运行以下命令:
$ sudo docker exec -i -t 665b4a1e17b6 /bin/bash #by ID

或者

$ sudo docker exec -i -t loving_heisenberg /bin/bash #by Name
$ root@665b4a1e17b6:/#

5或者,执行sudo docker attach loving_heisenberg - thiagowfx
57附加命令对我来说不起作用,它导致Docker冻结。有任何想法为什么会发生这种情况吗? - Mo J. Mughrabi
1首先,尝试通过运行命令 sudo docker ps 来查找您的活动容器。如果没有找到任何结果,请尝试使用命令 docker ps -a,然后复制您喜欢的容器ID,并启动容器 docker start your_id,最后附加它 docker attach your_id - Timur Fayzrakhmanov
10提醒给boot2docker用户:不要再使用sudo了 :) - Henno
这适用于我在OsX上运行docker$ sudo docker exec -i -t 665b4a1e17b6 bash #通过ID - Bhoom Suktitipat
1@MoJ.Mughrabi 你确定Docker卡住了吗?它将你连接到运行entrypoint的shell,所以当你附加到一个mongo容器时,实际上是在运行mongod前台进程的shell中。请参考更新内容以创建一个新的shell。 - mmlac
这真是太酷了——命令行上的两个附件相互镜像。对于基于云服务的技术支持来说很有趣,但对于安全性来说有点不安。有人知道是否有一个等效的取消附加或解除关联的命令吗?还是只需要终止创建附件的进程就可以了? - dhj
顺便问一下,这个奇怪的命名是怎么回事?我还看到了"loving_blabla",是谁创造了这种Docker容器的命名方式?哈哈 - Melroy van den Berg
@danger89,这是由Docker生成的快捷方式,可以通过容器名称而不是长ID来运行(附加或其他操作)。这样做只是为了方便。 - Timur Fayzrakhmanov
18-i -t 等于 -it - pasha.zhukov
如果你正在使用zsh,你可以使用这个gist来给你提供一个docker-connect <container_name>函数,该函数在容器名称上具有标签自动补全功能。https://gist.github.com/hailwood/beb377c588c41c9e93bacdca0fb3ff1d - Hailwood
51这是一个危险的答案被选中并且得到了如此高的投票。例如,docker attach到一个MongoDB实例将会终止该实例。正如在这个问题中更详细地解释的那样,attachexec是不同的操作。 - fred271828
@MoJ.Mughrabi 看下一个回答:如果容器是使用bash启动的,我们可以使用attach访问它,如果不是,则需要执行命令来创建一个交互式shell。 - wranvaud

从Docker 1.3版本开始:
docker exec -it <containerIdOrName> bash

基本上,如果Docker容器是使用/bin/bash命令启动的,您可以使用attach访问它。如果不是,则需要使用exec执行命令,在容器内部创建一个Bash实例。
此外,为了退出Bash而不保留在一个恶意进程中运行的Bash:
exit

是的,就是这么简单。

还没有弄清楚如何让Nano工作。我认为这可能涉及到从Phusion使用Docker-SSH。 - WiR3D
有没有办法在Docker中将bash设置为默认选项? - ipeacocks
@ipeacocks 是的,如果dockerfile中的RUN命令是/bin/bash。但这取决于您的意思是什么。如果您想要在同一终端中立即运行容器并使用bash,则应该使用-it进行运行。 - WiR3D
@WiR3D 我可以在连接到容器后设置默认的 Bash 吗?我想要运行 docker attach some_id,然后自动进入工作中的 Bash 环境。这可行吗? - ipeacocks
提醒Mac用户,不要以sudo身份运行,否则在boot2docker中会出现错误(http://stackoverflow.com/questions/25372781/docker-error-var-run-docker-sock-no-such-file-or-directory)。 - John David Five
10使用docker群组是不好的做法。任何加入docker群组的用户基本上都可以在不需要使用sudo的情况下以root权限使用。http://www.projectatomic.io/blog/2015/08/why-we-dont-let-non-root-users-run-docker-in-centos-fedora-or-rhel/ - Maiku Mori
@MaikuMori 同意。由于这是一个得到很多赞同的答案,它不应包括这个建议。 - r0estir0bbe
使用sudo的烦恼在于更严格的访问控制。理想情况下,这是你想要的;但实际上,可能会有一些情况,你明知故意地想要/可以安全地给予组访问权限。例如,包含/生成容器的容器可能希望绕过sudo(特别是如果你正在运行他人的代码,并且不想/不能添加sudo,即使需要)。离谱吗?当然...总的来说,不要无意中/恶意地打开你的实际用户账户以进行不良操作,但如果你了解风险,它确实具有合法的使用场景。 - Ajax
2我认为从主机安全的角度来看,无论您使用sudo还是docker组,都没有太大区别。无论您是使用docker组还是sudo来启动容器,都存在一个内置的安全漏洞,可以在宿主机文件系统中提供完全权限给客户端。 - Brent Bradburn
1当你的用户属于docker组时,任何你运行的程序都可以访问整个文件系统。但如果你使用sudo来运行docker(你需要先输入密码),这就不是真的了。 - Austin Adams
对于所有参与这次对话的人:我删除了关于docker组的建议。为了清晰起见,我建议在开发环境中进行这样的设置。原始评论: 注意:你应该尽量不要使用sudo运行命令。而是将你的用户添加到docker组,并以普通方式运行。 - WiR3D

尽管问题的提问者明确表示他们对正在运行的容器感兴趣,但值得注意的是,如果容器没有在运行,但你想要运行它以便进行探索,你可以运行以下命令: ``` docker run -i -t --entrypoint /bin/bash ```

11这提供了一个不同的容器,就像@kraxor的回答一样。 - Blaisorblade
谢谢你分享建议。当我需要退出图像时,我应该使用exit命令吗? - Cloud Cho


16这不合适,因为我得到了不同的容器ID(root@42f1e37bd0e5:/#而不是root@665b4a1e17b6:/#)。 - Timur Fayzrakhmanov

根据@Timur的回答,我创建了以下方便的脚本

设置

docker-ssh文件放在您的$PATH中,并包含以下内容

#!/bin/bash -xe

# docker container id or name might be given as a parameter
CONTAINER=$1

if [[ "$CONTAINER" == "" ]]; then
  # if no id given simply just connect to the first running container
  CONTAINER=$(docker ps | grep -Eo "^[0-9a-z]{8,}\b")
fi

# start an interactive bash inside the container
# note some containers don't have bash, then try: ash (alpine), or simply sh
# the -l at the end stands for login shell that reads profile files (read man)
docker exec -i -t $CONTAINER bash -l

注意:某些容器不包含bash,而是ash、sh等。在这些情况下,应该将上述脚本中的bash替换掉。
使用方法
如果您只有一个正在运行的实例,只需运行以下命令即可。
$> docker-ssh 

否则,提供一个来自docker ps(第一列)的docker id参数。
$> docker-ssh 50m3r4nd0m1d

我可以知道为什么我们需要在末尾加上“-l”吗? - Nam G VU
要启动bash作为登录shell,读取环境参数(在命令的上一行中描述)。 - Matyas

如果您的容器没有安装bash,您可以尝试使用sh。
docker exec -it CONTAINER /bin/sh

或者先在/bin目录下寻找外壳:
docker export CONTAINER|tar -t|egrep ^bin/

我创建了一个容器化的SSH服务器,为任何正在运行的容器提供SSH功能。您不需要更改您的容器。唯一的要求是容器必须有bash。
如果您有一个名为'web-server1'的容器。以下docker run命令将启动一个第二个容器,为第一个容器提供SSH服务。
docker run -ti --name sshd-web-server1 -e CONTAINER=web-server1 -p 2222:22 \
-v /var/run/docker.sock:/var/run/docker.sock -v $(which docker):/usr/bin/docker \
jeroenpeeters/docker-ssh

更多提示,请查看https://github.com/jeroenpeeters/docker-ssh

顺便问一下,使用您的解决方案,在启动SSH会话时如何自动加载.bashrc?同时在GitHub上发布了一个问题https://github.com/jeroenpeeters/docker-ssh/issues/30 - Nam G VU

@jpetazzo在这个主题上有一篇很棒的文章。简单来说,使用nsenter即可。
PID=$(docker inspect --format {{.State.Pid}} <container_name_or_ID>)
nsenter --target $PID --mount --uts --ipc --net --pid

附言:别忘了在帖子的评论中查看讨论...

干杯


1那是一篇相当古老的帖子,现在并不是真正必要的。@WiR3D的docker exec解决方案更加方便。 - drevicko

你也可以使用Pipework给Docker容器分配一个可路由的IP地址,然后使用这个新的IP地址通过SSH连接到机器上。
这种方式更加"传统"(使用ssh),而不是使用特定应用程序的命令如docker attach,这样做将使它在系统和版本之间更加'便携'。

请提供最简单的方法来完成这件事。说实话,我真的需要它,但是我没有时间去寻找最简单的解决方案。你能在这里发布你的答案吗?那将非常好。 - Timur Fayzrakhmanov
2有两种方法可以实现这个目标,但并不简单,而且可能会变成一篇很长的帖子。你可以自己查看这个 link ,使用pipework或者查看这个 link ,它与Pipework基本上实现了相同的功能,更简单一些,但需要手动操作。所以这取决于我们要讨论的服务器数量。如果你找不到更具体的解决方案,请告诉我。但是我也没有时间写一个完整的教程。 - radriaanse
你说得对 - 没有明显且简单的方法来做这件事(谢谢提供链接,我想我稍后会重新查看它。 - Timur Fayzrakhmanov