docker attach和docker exec的区别

110

两者均能在容器中执行命令。

两者都可以分离容器。

那么,docker exec和docker attach之间的真正区别是什么?

4个回答

108
2015年:有一个提交PR添加到文档中:

注意:此命令(attach)不用于在容器中运行新进程。请参见:docker exec

"Docker如何在已运行的容器(run -d)内获取bash\ssh" 的答案说明了两者之间的区别:

(docker >= 1.3)如果我们使用docker attach我们只能使用一个shell实例。因此,如果我们想要用新的容器shell实例打开新的终端,我们只需要运行 docker exec
如果使用了/bin/bash命令启动docker容器,则可以使用attach访问它,否则需要使用exec执行命令来在容器内创建bash实例。
this issue中提到:
  • Attach不是用于在容器中运行额外的东西,而是用于连接正在运行的进程。
  • "docker exec"专门用于在已经启动的容器中运行新的东西,无论是shell还是其他进程。
同一问题补充道:
虽然 `attach` 的命名不太合适,尤其是因为 LXC 命令 `lxc-attach`(类似于 `docker exec /bin/sh`,但是与 LXC 相关),但它确实有一个明确的目的,即将您连接到 Docker 启动的进程。根据进程的不同,行为可能会有所不同,例如连接到 `/bin/bash` 将给您一个 shell,但连接到 redis-server 就像您直接启动了 redis 而没有进行守护进程。

2022年更新:请前往容器 101:附加与执行 - 有何区别?(2021年12月)阅读更多信息,该文章由Ivan Velichko发布:

https://iximiuz.com/containers-101-attach-vs-exec/docker-attach-2000-opt.png

提取:

attach 和 logs 的区别

在上面的图表中,docker attach 将容器日志流返回到终端。
然而,docker logs 命令也可以做类似的事情。
那么,它们之间有什么区别呢?

logs 命令提供了各种选项来过滤日志,而 attach 则像一个简单的 tail。
但更重要的是,logs 命令建立的流始终是单向的,并且连接到容器的日志,而不是直接连接到容器的 stdio 流。

logs 命令只是将容器日志的内容流回您的终端,仅此而已。
因此,无论您如何创建容器(交互式或非交互式,由伪终端控制还是不控制),使用 logs 命令时都不会意外影响容器。

然而,当使用 attach 时:

exec 命令是什么

exec 命令实际上是完全不同的故事。

在 attach 的情况下,我们将终端连接到现有的容器(即进程)。

然而,exec 命令会启动一个全新的容器!
换句话说,execrun 命令的一种形式(它本身只是 create + start 的快捷方式)。

Bart评论中 提醒我们,docker exec 命令在一个 正在运行的 容器中运行新命令,而不是完全新的命令。


1
很好知道。然而关于你最后提到的docker exec:docker exec并不会启动一个(全新的)容器。它在你指定的正在运行的容器中运行一个(新的)命令。正如docker exec --help所告诉你的那样:用法:docker exec [选项] CONTAINER COMMAND [ARG...] 在正在运行的容器中运行一个命令[...] - Bart
@Bart 谢谢,好观点。我已经编辑了答案,使其更清晰,并包括您的评论以增加可见性。 - VonC

31
当使用/bin/bash启动容器时,它将成为容器的PID 1,并且使用docker attach可以进入容器的PID 1。因此,docker attach < container-id >将带您进入bash终端,因为我们在启动容器时提到了它的PID 1。退出容器将停止容器。
而在docker exec命令中,您可以指定要进入哪个shell。它不会将您带到容器的PID 1。它将为bash创建一个新进程。 docker exec -it < container-id > bash。 退出容器不会停止容器。
您还可以使用nsenter进入容器。 nsenter -m -u -n -p -i -t < pid of container > 您可以使用以下命令找到容器的PID:docker inspect < container-id > | grep PID
注意:如果您使用-d标志启动容器,则无论您使用attach还是exec进入容器后退出容器都不会停止容器。

1
使用 nsenter 的想法很有趣。你能详细说明一下吗?解释选项应该是有序的。为什么不进入所有命名空间?为什么选择这些特定的命名空间? - x-yuri

22

正如Michael Sun在他的回答中所述:

docker exec 在容器环境中执行新命令 / 创建新进程,而 docker attach 仅将容器内主进程(PID 1)的标准输入/输出/错误连接到当前终端(您用来运行命令的终端)的相应标准输入/输出/错误。

我的答案将更加聚焦于让您验证上述语句并更清晰地理解它。

打开一个终端窗口并运行命令 docker run -itd --name busybox busybox /bin/sh。如果镜像busybox不存在,该命令将拉取此镜像。然后,使用该镜像创建一个名为busybox的容器。

您可以通过运行命令docker ps -a | grep busybox来检查容器的状态。

如果运行docker top busybox,您应该会看到类似以下的输出。

UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                7469                7451                0                   11:40               pts/0               00:00:00            /bin/sh

当然,PIDPPID和其他数值在您的情况下将是不同的。您也可以使用其他工具和实用程序,如pstreetophtop来查看PIDPPID的列表。

PIDPPID表示进程ID和父进程ID。进程是在我们使用命令/bin/sh创建并启动容器时启动的。现在,运行命令docker attach busybox。这将把容器的标准输入/输出/错误流附加到您的终端。

附加容器后,通过运行命令sh创建一个shell会话。按CTRL-p CTRL-q序列。这将使终端从容器中分离出来,并使容器保持运行状态。如果现在运行docker top busybox,您应该在列表中看到两个进程。

UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                7469                7451                0                   11:40               pts/0               00:00:00            /bin/sh
root                7737                7469                0                   11:43               pts/0               00:00:00            sh

但是这两个进程的 PPID 将不同。 实际上,第二个进程的 PPID 将与第一个进程的 PID 相同。 第一个进程充当我们刚刚创建的 shell 会话的父进程。

现在运行 docker exec -it busybox sh 。 进入容器后,在另一个终端窗口中通过运行命令 docker top busybox 检查容器 busybox 的正在运行的进程列表。 您应该看到类似于此的内容

UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                7469                7451                0                   11:40               pts/0               00:00:00            /bin/sh
root                7737                7469                0                   11:43               pts/0               00:00:00            sh
root                7880                7451                0                   11:45               pts/1               00:00:00            sh

第一个和第三个进程的PPID将是相同的,这证实了docker exec在容器环境中创建了一个新进程,而docker attach只是将容器内主进程的标准输入/输出/错误连接到当前终端的相应标准输入/输出/错误。


6

Docker exec命令在容器的环境中执行一个新的命令/创建一个新的进程,而docker attach只是将容器内主进程(PID为1)的标准输入/输出/错误连接到当前终端(您用来运行命令的终端)的相应标准输入/输出/错误。

容器是一个隔离的环境,其中有一些进程在运行。具体而言,容器具有自己的文件系统空间和PID空间,这些空间与主机和其他容器隔离开来。 当使用“docker run –it …”启动容器时,主进程将具有伪tty和保持打开的STDIN。 在tty模式下附加时,您可以使用可配置的键序列从容器中分离出来(并使其保持运行)。默认序列是CTRL-p CTRL-q。您可以使用--detach-keys选项或配置文件配置键序列。 您可以使用docker attach重新附加到已分离的容器。

Docker exec仅启动一个新进程,在容器的环境中,即属于容器的PID空间。

例如,如果您使用“docker run –dit XXX /bin/bash”启动容器,则可以使用两个不同的终端连接到容器(的主进程)。当您在一个终端输入时,您可以看到它出现在另一个终端中,因为两个终端都连接到同一个tty。 请注意,您现在在容器的主进程中,如果输入“exit”,则会退出容器(因此要小心,使用detach-keys进行分离),并且您将看到两个终端都已退出。 但是,如果您在两个终端中运行“docker exec –it XXX /bin/bash”,则已在容器内启动了两个新进程,它们彼此不相关,也与主进程不相关,并且您可以安全地从它们中退出。


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