困惑于Docker -t选项以分配伪TTY

时间:2015-05-09 07:11:59

标签: docker tty pty

这个选项到底是做什么的?我一直在读TTY,但我仍然感到困惑。我没有使用-t而只是-i,并且似乎期望用户输入的程序在没有-t的情况下抛出错误。为什么启用伪TTY很重要?

9 个答案:

答案 0 :(得分:135)

" -t"选项适用于Unix / Linux如何处理终端访问。在过去,终端是强硬连接,后来是基于调制解调器的连接。这些都有物理设备驱动程序(它们是真正的设备)。一旦广义网络投入使用,就开发了伪终端驱动程序。这是因为它在理解可以使用哪些终端功能而无需直接将其写入程序之间创建了分离(阅读sttycurses上的手册页)。

因此,以背景为例,运行一个没有选项的容器,默认情况下你有一个stdout流(所以docker run | <cmd>有效);运行&#34; -i&#34;,然后添加stdin流(所以<cmd> | docker run -i工作);使用&#34; -t&#34;,通常在组合&#34; -it&#34;并且您添加了一个终端驱动程序,如果您正在与该过程进行交互,则可能是您想要的。它基本上使容器启动看起来像终端连接会话。

答案 1 :(得分:66)

根据谷歌的搜索,“ - t”这个论点没有很好地记录,或者很多人经常提到。

当您通过在Bash提示符下键入“docker”(最新版本为1.8.1)显示所有docker客户端参数的列表时,它甚至不显示。

事实上,如果您尝试通过键入“docker -t --help”获得有关此参数的特定帮助,如果给出了这个非常模糊的回复:

“提供但未定义的标志:-t”

所以,你不能因为对这个论点感到困惑而受到指责!

Docker在线文档中提到它是“分配伪tty”并经常与-i:

一起使用

https://docs.docker.com/reference/run/

我通过以下方式看到它在极好的jwilder / nginx-proxy docker容器的文档中使用:

docker run -d -p 80:80 --name nginx -v /tmp/nginx:/etc/nginx/conf.d -t nginx

在这种情况下,它所做的是将输出发送到此docker容器中的'virtual'tty(Bash命令提示符/终端)。然后,您可以通过运行docker命令“docker logs CONTAINER”来查看此输出,其中CONTAINER是此容器ID的前几个字符。可以通过键入“docker ps -a”

找到此CONTAINER ID

我在下面的链接中看到过这个“-t”参数,它说:“-t和-i标志分配一个伪tty并保持stdin打开,即使没有附加。这将允许你只要bash提示符正在运行,就像传统VM一样使用容器。“

https://coreos.com/os/docs/latest/getting-started-with-docker.html

我希望这有帮助!我不确定为什么没有记录或使用太多。也许它是实验性的,并将在即将推出的版本中作为文档功能实现。

答案 2 :(得分:12)

最新答案,但可能会对某人有所帮助

docker run/exec -i将容器内命令的STDIN连接到docker run/exec本身的STDIN。

所以

  • docker run -i alpine cat为您提供一个空行等待输入。输入“ hello”,您将得到回声“ hello”。直到您发送CTRL + D,容器才会退出,因为主进程cat正在等待来自无限流的输入,该无限流是docker run的终端输入。
  • 另一方面,echo "hello" | docker -i run alpine cat将显示“ hello”并立即退出,因为cat注意到输入流已结束并自行终止。

如果您在退出以上任一方法后尝试docker ps,将找不到任何正在运行的容器。在这两种情况下,cat本身都已终止,因此docker已终止了该容器。

现在为“ -t”,它告诉docker内部的主要过程其输入是终端设备。

所以

  • docker run -t alpine cat将为您提供一个空行,但如果您尝试键入“ hello”,则不会得到任何回显。这是因为在cat连接到终端输入时,此输入未连接到您的输入。您输入的“ hello”未达到cat的输入。 cat正在等待永远不会到达的输入。
  • echo "hello" | docker run -t alpine cat也将为您提供一个空行,并且不会在CTRL-D上退出容器,但是由于您未通过-i
  • ,因此不会收到回声“ hello”

如果发送CTRL + C,将收回外壳,但是如果现在尝试docker ps,则会看到cat容器仍在运行。这是因为cat仍在等待从未关闭的输入流。如果不与-t结合使用,单独使用-i并没有任何有用的用途。

现在,一起-it。这告诉cat它的输入是端子,同时将此端子连接到docker run的输入,后者是端子。 docker run/exec在将其输入传递给cat之前,将确保其输入实际上是tty。这就是为什么尝试input device is not a TTY时会得到echo "hello" | docker run -it alpine cat的原因,因为在这种情况下,docker run本身的输入是来自先前回显的管道,而不是{{1} }被执行

最后,如果docker run可以将输入与-t的输入连接起来,为什么还要传递-i?这是因为如果命令是终端,则命令对输入的处理会有所不同。最好通过示例来说明

  • cat会提示您输入密码。如果您输入密码,则会清晰显示字符。
  • docker run -e MYSQL_ROOT_PASSWORD=123 -i mariadb mysql -uroot -p将给您空行。如果键入docker run -i alpine sh之类的命令,则会得到输出,但不会显示提示或彩色输出。

在后两种情况下,您会收到此行为,因为lsmysql都没有将输入视为tty,因此没有使用tty特定的行为,例如对输入进行遮罩或给输入着色输出。

答案 3 :(得分:6)

我对config/app.php的了解如下:

-t - 允许我在容器中“登录”。感觉就像sshing(事实并非如此)。

但问题是当我想要恢复数据库时。

通常我做docker exec -ti CONTAINER bash - 这里我在容器中执行mysql命令并获得一个交互式终端。

我将docker exec -ti mysql.5.7 mysql添加到上一个命令,以便我可以恢复数据库。但它失败了<dump.sql

删除cannot enable tty mode on non tty input有帮助。仍然不明白为什么:

-t

最后一个有效。希望这有助于人们。

答案 4 :(得分:3)

这里的大多数答案都是很好的概念性答案,但我发现它们遗漏了太多细节,让我坐在电脑前无法使用这些信息。 Ahmed Gnomin 的答案正在走向程序化,但让我们尝试更进一步。

先说点理论

The TTY Demystified 中的两个图像是关键:

enter image description here

我不能声称完全理解这张图片,但这里感兴趣的关系是当 xterm(或 ubuntu 中的 gnome-terminal;由上图中的“用户进程”气泡之一表示)打开时,它启动一个 bash(或任何默认的 shell),然后通过内核伪终端 (PTY) 主机和从机向它发送键盘输入:

xterm -> ptmx (pty master) -> pts (pty slave) -> bash

enter image description here

第二张图片代表了这个简短的 bash 会话中涉及的进程:

>>> cat
>>> ls | sort
...

信息的关键位是 TTY 和 stdin、stdout、stderr 行。这表明每个进程都与一个 TTY(电传终端)相关联,并且它们的 3 个流(stdin、stdout、stderr)很自然地与这个 TTY 相关联,除了管道或重定向的情况(注意管道 {{ 1}} 将 ls 的标准输出与排序的标准输入关联)。

现在有点测试理论

我们可以通过输入ls | sort找到bash使用的伪终端:

tty

因此 Bash 与 PTY 从属编号 2 相关联(这可能意味着有另一个终端打开,与主/从对 1 相关联)。我们还可以获取 bash 的 stdin、stdout 和 stderr 流:

>>> tty
/dev/pts/2

确实,它们都与 bash 的天生 TTY 奴隶有关。 (>>> ls -l /proc/$$/fd lrwx------ 1 samlaf samlaf 64 Jun 17 21:50 0 -> /dev/pts/2 lrwx------ 1 samlaf samlaf 64 Jun 17 21:50 1 -> /dev/pts/2 lrwx------ 1 samlaf samlaf 64 Jun 17 21:50 2 -> /dev/pts/2 是一个 bash 变量,它返回 bash 的 PID。我们同样可以通过使用 $$ 并手动输入来找到它。

最后用这个理论来回答最初的 Docker 问题

我们重现了上述步骤,但这次是在 docker 容器中:

ps

>>> docker run --rm -t ubuntu tty /dev/pts/0 >>> docker run --rm ubuntu tty not a tty allocates a pseudo-terminal 开始是有道理的。

-t 相关的命令更难解释。

-i

我仍然无法弄清楚 >>> docker run --rm ubuntu bash -c "ls -l /proc/\$\$/fd" lrwx------ 1 root root 64 Jun 18 02:37 0 -> /dev/null l-wx------ 1 root root 64 Jun 18 02:37 1 -> pipe:[9173789] l-wx------ 1 root root 64 Jun 18 02:37 2 -> pipe:[9173790] >>> docker run --rm -t ubuntu bash -c "ls -l /proc/\$\$/fd" lrwx------ 1 root root 64 Jun 18 02:39 0 -> /dev/pts/0 lrwx------ 1 root root 64 Jun 18 02:39 1 -> /dev/pts/0 lrwx------ 1 root root 64 Jun 18 02:39 2 -> /dev/pts/0 >>> docker run --rm -it ubuntu bash -c "ls -l /proc/\$\$/fd" lrwx------ 1 root root 64 Jun 18 02:39 0 -> /dev/pts/0 lrwx------ 1 root root 64 Jun 18 02:39 1 -> /dev/pts/0 lrwx------ 1 root root 64 Jun 18 02:39 2 -> /dev/pts/0 到底是做什么的...我希望得到一些帮助! 我能找到的唯一有趣的命令似乎有所区别:

-i

Docker documentation 提到 -a“附加到作为输入传递的流”,但我无法找到解释这意味着什么,以及它与 {{1} } 选项。

答案 5 :(得分:1)

每个进程都有三个数据流,即STDIN/ STDOUT/ STDERR。在容器中运行进程时,默认情况下,将终端与在容器中运行的进程的STDOUT流连接。因此,在终端中运行docker run命令时,所有输出流都是可见的。但是,如果要向容器中正在运行的进程提供输入,则必须连接进程的STDIN通道,该通道默认情况下不是使用docker run -i命令完成的。

-t用于交互式/格式化输入操作。

答案 6 :(得分:1)

-it 组合选项称为交互模式。

默认情况下,容器只有一个标准输出流(即 docker run | CMD 有效),为了与我们的容器交互,我们需要以下选项:

  • -i 添加一个标准输入流(即 CMD | docker run 有效)
  • -t 分配一个伪 TTY 主/从对,其中从部分与容器中正在运行的进程相关联,而主部分与您的终端模拟器相关联。

stdin 流将容器附加到 shell 的 stdin(因为 Docker 继承了 shell 的 stdin 流),而 TTY 线路规则使您能够以键盘方式与容器交互。

答案 7 :(得分:0)

在Linux中,当您运行命令时,需要一个终端(tty)来执行该命令。

因此,当您要连接到docker(或在docker容器中运行命令)时,必须提供选项-t,该选项考虑了docker容器内的终端。

答案 8 :(得分:-1)

-it指示Docker分配连接到容器stdin的伪TTY,在容器中创建交互式bash shell。

--interactive, -i   false   Keep STDIN open even if not attached

--tty, -t   false   Allocate a pseudo-TTY

https://docs.docker.com/engine/reference/commandline/run/