如何正确使用docker run -i -t -d 参数
在使用docker run
命令时,我们经常会使用到-i、-t
和-d
参数,那么这几个参数的作用究竟是什么呢,这篇文章简单讲一下。
选项说明
官方文档对参数的说明:
--interactive , -i Keep STDIN open even if not attached
--tty , -t Allocate a pseudo-TTY
--detach , -d Run container in background and print container ID
-d
作用:在后台运行容器,并且打印容器id-t
作用:分配一个伪TTY-i
作用:即使没有attached,也要保持STDIN打开状态
detach选项
大多数情况下,我们都是希望Docker能够在后台运行容器,而不是直接在宿主机上与之交互。如果不加-d
选项,容器会在宿主机终端运行,并且把输出的结果(STDOUT)打印到宿主机上面。
使用-d
选项后,容器会在后台运行,并输出容器ID,我们可以使用docker logs [containerID]
来查看容器的输出结果。如果想跟容器进行交互,可以使用docker exec -it [container ID] /bin/bash
来操作。
需要注意的是,容器是否会持久运行,和
-d
选项无关。关于这一点,在文章docker run 如何让容器启动后不会自动停止中有介绍。
interactive选项
-i
选项根据文档的解释就比较费劲了,我们不妨先看另外一个选项-a
。
-- attached , -a Attach to STDIN, STDOUT or STDERR
它的意思是把宿主机标准输入,输出和错误流附加到容器。
在Linux中,STDIN
是标准的输入流,通常对应终端的键盘;STDOUT
是标准输出流,STDERR
是标准输出流,STDERR
是标准错误输出流,它们都对应终端的屏幕。进程将从标准输入文件中得到输入数据,将正常
输出数据输出到标准输出文件,而将错误信息送到标准错误文件中。关于这方面的更多了解可以参照 What Are stdin, stdout, and stderr on Linux? 这篇文章。
那么我们再来看-i
的解释:Keep STDIN open even if not attached
。
Keep STDIN open
就是说让容器的标准输入保持打开状态,
even if not attached
强调的是即使没有把宿主机标准输入,输出和错误流附加到容器依然保持容器的标准输入为打开状态。
很多人比较费解的是后半句,其实它只是一个强调。在Docker run reference中有一段提到如果没有指定-a
选项,默认会attach stdout和stderr。
那么我们做一个小实验。下面的语句是通过管道把宿主机的echo test
命令执行的结果作为node
容器的输入,在容器中执行cat
命令再通过stdout
输出到宿主机。可以打印出test。
echo test | docker run --rm --name node -a stdin -a stdout -i node:12.18.3-slim cat
接下来我们不使用-a
选项,得到的也是一样的结果。
echo test | docker run --rm --name node -i node:12.18.3-slim cat
这也就是说,如果加了-i
选项,容器的STDIN
就会一直保持打开,我们就可以以交互模式(interactive mode)来运行容器了。
交互模式
我们可以让容器运行后执行一条命令,比如前面提到的cat
,看一下会发生什么:
docker run --rm --name node -i node:12.18.3-slim cat
如果我们执行的是一个持续与容器交互的程序,容器的状态就会一直保持运行状态。那么如果我们想结束掉与容器的交互呢?
在docker attach命令里有介绍到,使用CTRL-c
可以让容器停止运行,它是通过发送一个STGKILL
信号来实现的。
但是我们尝试了一下发现并不行,这是因为Linux 1号进程的特殊性。不过,我们可以通过CTRL-d
去停止容器,因为它不是发送一个信号,而是表示一个特殊的二进制值EOF
。
其实这里涉及到几个Linux的知识点:
- Linux PID1(Linux的1号进程)
- Linux的信号(SIGINT、SIGTERM、SIGQUIT、SIGKILL、SIGHUP)
- EOF
如果想让CTRL-c
也可以生效,我们可以在docker run
命令加上--init
选项
docker run --rm --init --name node -i node:12.18.3-slim /bin/bash
对上面提到的几个知识做了一定了解后,我们再来看直接通过-i
启动一个容器而不执行命令会发生什么。
docker run --rm --name node -i node:12.18.3-slim
可以看到容器正常启动并保持运行,我们在宿主机的终端输入任何字符都没有反应。
我们可以再开启另外一个终端,进入容器看一下1号进程,发现是node
$ docker exec -it node /bin/bash
$ ps -aux
所以我们可以得出一个结论:如果容器启动的1号进程是一个shell程序的话,我们通过-i
选项就可以与之交互,保持容器持续运行。不指定-i
,容器启动后就会自动退出。
现在还有一个问题就是,如果只指定了-i
选项,我们在宿主机终端输入任何字符都没有反应。这个问题,我们可以用-it
来解决,所以我们接下来就介绍一下-t
选项的作用。
tty选项
如果对Linux的TTY比较熟悉的话,对于这个选项就比较了解了。我们可以把它理解为Linux的终端程序,所以-t
选项就可以理解为为容器分配一个伪tty终端并绑定到容器的标准输入上,之后用户就可以通过终端来控制容器了。
一般-t
都是与-i
一起出现的,也就是-it
。再讲这个之前,我们先单独分析一下-t
吧。
我们首先试一下运行容器后执行一个交互命令,看与-i
的区别是什么:
docker run --rm --name node -t node:12.18.3-slim cat
执行完后,容器也可以保持运行。与单独指定-i
不一样的是,在终端中输入任何字符都没有反应;CTRL-d
终止不了容器;直接关闭宿主机的终端,容器还继续保持运行。
我们来解释一下执行的结果为什么是这样的。首先,输入任何字符都没有反应,是因为容器没有打开标准输入,我们输入的任何字符都是没有传递进容器的。所以cat
一直会在等待永远不会到来的输入,也就一直保持容器运行状态。CTRL-d
同样也不会传递到容器,直接关闭宿主机的终端也是同样的道理,因为它是在容器内部分配了一个伪终端。所以,我们关闭宿主机的终端后,开启一个新的终端输入docker ps
,可以看到容器仍然在运行。
通过-t
选项直接运行一个容器也是一样的结果。
docker run --rm --name node -t node:12.18.3-slim
执行完后,容器也可以持续运行。与单独指定-i
不一样对是,在终端中输入任何字符都没有反应;CTRL-d
终止不了容器;直接关闭宿主机的终端,容器还继续保持运行。
我们来解释一下执行的结果为什么是这样的。首先,输入任何字符都没有反应,是因为容器没有打开标准输入,我们输入的任何字符都是没有传递进容器的。所以cat
一直会等待永远不会到来的输入,也就一直保持容器运行状态。CTRL-d
同样也不会传递到容器,直接关闭宿主机的终端也是同样的道理,因为它是容器内部分配的一个伪终端。所以,我们关闭宿主机的终端后,开启一个新的终端输入docker ps
,可以看到容器仍然在运行。
通过-t
选项直接运行一个容器也是一样的结果。
docker run --rm --name node -t node:12.18.3-slim
我们可以看到与-i
不同的是,它会使用伪终端进入到node到shell中,但是我们输入任何字符都是没用的。关闭宿主机的终端后,容器依然运行。
这也就解释了为什么容器启动的1号进程是一个shell程序的话,我们使用-dt
就可以保持容器持续在后台运行。
-it
前面我们知道了如果直接执行docker run
,它默认会有stdout流,如果我们加了-i
,它会保持stdin流打开。那么,我们再加上-t
选项,就是说标准输入变成了一个伪tty终端。从这里也可以看出,交互模式下单独指定-t
选项是没有意义的,因为如果容器的标准输入没有打开,我们是输入不了任何内容的。
简单来说,指定-t
而不指定-i
,意味着在容器里开启了一个伪终端,但是我们的输入并不会传递到伪终端的输入。
所以,官方文档也写道了,在交互模式下,-i
与-t
选项必须结合使用,也就是-it
。
那么,我们使用-it
选项来启动一个容器,看一下有哪些变化:
docker run --rm --name node -it node:12.18.3-slim
我们进入了node的shell,并且可以使用快捷键Tab来显示信息。
也可以通过CTRL-c
来退出交互模式。
标签:node,容器,run,--,宿主机,参数,docker From: https://www.cnblogs.com/alvisClub/p/17091305.html参考:
https://jerrymei.cn/docker-run-interactive-tty-detach/
- [What's the difference between ^C and ^D for UNIX/Mac OS X terminal?](https://superuser.com/questions/169051/whats-the-difference-between-c-and-d-for-unix-mac-os-x-terminal#:~:text=Ctrl C tells the terminal,as a desire to exit.&text=Ctrl %2B D ( ^D ) means end of file.)
- Avoid running Node.js as PID 1 under Docker images
- Linux命令中Ctrl+z、Ctrl+c和Ctrl+d
- linux的 0号进程 和 1 号进程
- Termination-Signals
- Linux 的伪终端的基本原理 及其在远程登录(SSH,telnet等)中的应用
- What is a TTY on Linux? (and How to Use the tty Command)
- What does “the input device is not a TTY” exactly mean in “docker run” output?
- [Confused about Docker -t option to Allocate a pseudo-TTY](