docker attach 和 docker exec 之间的区别

difference between docker attach and docker exec

两者都可以在容器中执行命令。 两者都可以分离容器。

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

2015:有一个 commit PR 添加到文档中:

Note: This command (attach) is not for running a new process in a container. See: docker exec.

Docker. How to get bash\ssh inside runned container (run -d)?”的答案说明了区别:

(docker >= 1.3) If we use docker attach, we can use only one instance of shell.
So if we want to open new terminal with new instance of container's shell, we just need to run docker exec

if the docker container was started using /bin/bash command, you can access it using attach, if not then you need to execute the command to create a bash instance inside the container using exec.

this issue所述:

  • Attach isn't for running an extra thing in a container, it's for attaching to the running process.
  • "docker exec" is specifically for running new things in a already started container, be it a shell or some other process.

同题补充:

While attach is not well named, particularly because of the LXC command lxc-attach (which is more akin docker exec <container> /bin/sh, but LXC specific), it does have a specific purpose of literally attaching you to the process Docker started.
Depending on what the process is the behavior may be different, for instance attaching to /bin/bash will give you a shell, but attaching to redis-server will be like you'd just started redis directly without daemonizing.


2022 年更新:使用“Containers 101: attach vs. exec - what's the difference?" (Dec. 2021) from Ivan Velichko:

查看更多内容

摘录:

Difference between attach and logs

On the diagram above, docker attach streams the container's logs back to the terminal.
However, the docker logs command does a similar thing.
So, what's the difference?

The logs command provides various options to filter the logs while attach in that regard acts as a simple tail.
But what's even more important is that the stream established by the logs command is always unidirectional and connected to the container's logs, not the container's stdio streams directly.

The logs command simply streams the content of the container's logs back to your terminal, and that's it.
So, regardless of how you created your container (interactive or non-interactive, controlled by a pseudo-terminal or not), you cannot accidentally impact the container while using the logs command.

However, when attach is used:

  • If a container was created in the interactive mode (-i), everything you type in the terminal after attach-ing to the container will be sent to its stdin.
  • You can (intentionally or accidentally) send a signal to the container - for instance, hitting ctrl+c on your end while attached sends SIGINT to the container.

What does exec command do

The exec command is actually a totally different story.

In the case of attach, we were connecting our terminal to an existing container (read, process).

However, the exec command starts a totally new container!
In other words, exec is a form of the run command (which itself is just a shortcut for create + start).

Bart reminds us in that docker exec运行 容器中运行新命令。不是“全新的”。

当使用 /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 > 您可以使用以下方法找到容器的 PID:docker inspect | grep PID

注意:如果您已经使用 -d 标志启动容器,那么退出容器将不会停止容器,无论您是使用 attach 还是 exec 进入容器。

Docker exec 在容器的环境中执行一个新命令/创建一个新进程,而 docker attach 只是连接标准的 input/output/error 主进程(PID 1)在容器内到当前终端的相应标准input/output/error(您正在使用的终端运行命令)。

容器是一个隔离的环境,环境中有一些进程运行。具体来说,一个容器有自己的文件系统 space 和 PID space,它们与主机和其他容器隔离。 当使用“docker 运行 –it …”启动容器时,主进程将有一个伪 tty 和 STDIN 保持打开状态。 当以 tty 模式连接时,您可以使用可配置的键序列与容器分离(并保留它 运行ning)。默认顺序是 CTRL-p CTRL-q。您可以使用 --detach-keys 选项或配置文件配置键序列。 您可以使用 docker attach.

重新附加到分离的容器

Docker exec 只是启动了一个新的进程,在容器的环境里面,也就是属于容器的PIDspace。

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

正如 Michael Sun 在他的回答中所说的那样

docker exec executes a new command / create a new process in the container’s environment, while docker attach just connects the standard input/output/error of the main process(with PID 1) inside the container to corresponding standard input/output/error of current terminal(the terminal you are using to run the command).

我的回答会更侧重于让你验证上面的说法,让你更清楚地理解它。

打开终端 window 和 运行 命令 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。这会将容器的标准 input/output/error 流附加到您的终端。

附加容器后,通过 运行 命令 sh 创建一个 shell 会话。按 CTRL-p CTRL-q 序列。这会将终端与容器分离,并将保持容器 运行ning。如果您现在 运行 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 在另一个终端 window 中检查容器 busybox 的 运行ning 进程列表。你应该看到这样的东西

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只是连接标准[=容器内主进程的66=]对应当前终端的标准input/output/error。