将tty/std-in-out附加到Docker或LXC是什么意思?

102

我阅读了一些Docker文档,但是不理解以下术语的含义:

  • 附加TTY
  • 附加标准输入和输出

为了达到这些目的,我看到使用了-i-t标志。

这个过程是什么意思呢?

5个回答

98

stdinstdout和ttys是相关的概念。 stdinstdout是进程的输入和输出流。伪终端(也称为ttypts)通过一个shell(例如bash),通常(但不一定)连接用户的“终端”与stdinstdout流。我在“终端”周围使用引号,因为我们今天并不像以前那样使用终端。

在docker的情况下,当您以交互模式运行进程时,例如启动bash shell时,您经常会同时使用-t-i。在shell的情况下,您希望能够发出命令并读取输出。

docker用于附加stdout/stdin的代码具有所有详细信息。


1
在 Docker 1.2.0 中,所有上述命令均成功执行,并显示“hello”而无错误。 - Air
当我从伪终端退出时,这意味着Docker容器已停止。那么这是否意味着如果我在实际项目中使用它,就不能退出终端? - NothingBox

39
我们可以使用lsof命令来查看底层发生了什么。为了进行演示,我们可以从Debian镜像创建一个简单的Docker容器,只运行sleep命令:
docker run -d --name tty-test debian /bin/bash -c "sleep 1000"

这将在一个新的容器中启动睡眠命令(请注意,我们没有使用-i-t)。
接下来,我们通过exec命令“登录”到容器中,并启动bash:
docker exec -it tty-test /bin/bash

一个纯净的Debian镜像中没有安装lsof,因此我们需要安装它:

apt update && apt install -y lsof

接下来我们运行lsof命令:

lsof

如果不带任何选项运行,lsof将打印所有正在运行的进程的打开文件。您应该在其输出中看到三个进程(sleep、bash和lsof本身)。
这里的相关行是显示文件描述符(FD列)02的行。
请注意,我们没有使用-t选项启动的sleep进程有三个FIFO管道用于stdinstdoutstderr
sleep     1 root    0r  FIFO   0,10      0t0 8226490 pipe
sleep     1 root    1w  FIFO   0,10      0t0 8226491 pipe
sleep     1 root    2w  FIFO   0,10      0t0 8226492 pipe

虽然 bash 进程实际上连接了一个设备到 stdinstdoutstderr

bash      7 root    0u   CHR 136,15      0t0      18 /dev/pts/15
bash      7 root    1u   CHR 136,15      0t0      18 /dev/pts/15
bash      7 root    2u   CHR 136,15      0t0      18 /dev/pts/15

让我们使用-t选项创建另一个容器:
docker run -d -t --name tty-test2 debian /bin/bash -c "sleep 1000"

安装完lsof后(见上文),我们可以得到sleep进程的不同输出:
sleep     1 root    0u   CHR 136,15      0t0      18 /15
sleep     1 root    1u   CHR 136,15      0t0      18 /15
sleep     1 root    2u   CHR 136,15      0t0      18 /15

请注意类型列已更改为CHR,名称列显示/15
最后,当我们从exec命令中省略-t选项时,就像这样:
docker exec -it tty-test /bin/bash

那么我们可以注意到两件事情。首先,现在我们无法从bash中得到shell提示符,但我们仍然可以输入命令并查看它们的输出。当我们运行lsof时,我们可以看到bash进程现在也有管道而不是附加到stdinstdoutstderr的tty。

bash    379 root    0r  FIFO   0,10      0t0 8263037 pipe
bash    379 root    1w  FIFO   0,10      0t0 8263038 pipe
bash    379 root    2w  FIFO   0,10      0t0 8263039 pipe

2
好的分析。如果您只使用“-t”而不是“-d -t”,会怎样呢?另外,如果您将“--entrypoint”传递给“sleep”(而不是bash),会怎样呢? - dashesy
1
谢谢您的回答!我想知道在终端中以守护进程模式运行容器实际上给了我什么? - Guy

9
这意味着您可以使用 TTY 登录到您的容器中,即终端。就像您面前有一台 Linux 机器并登录到其中一样。如果您有一个没有运行 SSH 服务器或 telnet 的容器,则这是进入命令行提示符的唯一方式。
至于为什么 -i-t 是不同的参数,我不确定,我无法想象出一种情况,在这种情况下,您希望使用 TTY 连接而不想使用 stdin/stdout 选项或反之亦然。

如果你只是想使用像 ssh-keygen 这样的程序,我相信你不需要 -t 标志。类似 docker run -i ubuntu /usr/bin/ssh-keygen ... 的命令即可。 - Uri
另一个回答比这个好得多。 - episodeyang
3
将tty标志省略掉在将某些内容管道传输到命令时是有价值/强制要求的。 - Matt Kenefick

5

选项-i -t对于与某些交互式进程进行交互非常有用,例如在容器内运行的bash shell。

考虑下面的命令

PS C:\Users\nssh> docker run -it ubuntu /bin/bash
root@c7537bbf2941:/# ls
bin  boot  dev  etc  home  lib  lib32  lib64

当我们运行此命令时,Docker会启动一个包含Bash shell的Ubuntu容器。
-i选项告诉Docker将我们键入的任何内容发送到Bash进程的标准输入stdin中。上面输入的ls命令被发送到Bash。
-t选项告诉Docker这将是一个交互式会话,并且stdin将是一个tty。
要获得类似SSH的体验,必须同时使用-t和-i选项。
唯一不使用-t选项的原因是如果stdin是管道(而不是像上面那样的tty)。当客户端从管道接收其标准输入时,例如:禁止指定-t选项。
PS C:\Users\nssh> echo test | docker run -it ubuntu cat
the input device is not a TTY.  If you are using mintty, try prefixing the command with 'winpty'

如果 stdin 是管道,请省略 -t 标志。

PS C:\Users\nssh> echo test | docker run -i ubuntu cat
test

1

简单来说,它允许我们连接和断开容器的终端。要连接,我们使用docker attach命令,要断开,我们使用CTRL+P & CTRL+Q命令。


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