Docker 初探
安装 Docker
Docker Desktop for Windows 依赖于:
- Hyper-V
- WSL
确保开启 Hyper-V,并且已安装 WSL。
基本命令
拉取镜像
Docker 会先尝试从本地查找镜像。
1 | docker pull <image[:tag]> |
创建容器
run 命令同样会检查镜像是否在本地,并自动下载尚未 pull 的镜像。容器的 I/O 流会被自动重定向到宿主机。
1 | # COMMAND: 启动命令,可选 |
容器一经创建,其启动命令将在每次 docker start
时执行,该命令对应的进程 ( 根进程 ) 结束时,则容器自动退出。
一些常用选项:
-i
以交互方式启动容器,并保持STDIN
打开-t
让 Docker 为容器分配伪终端 ( pseudo-TTY )-d
以后台模式启动容器,反之前台启动--name
命名容器--rm
运行后销毁
-i
Description | Alias(es) |
---|---|
Keep STDIN open | --interactive |
The
--interactive
(or-i
) flag keeps the container’sSTDIN
open, and lets you send input to the container through standard input.
-i
选项用于打开容器的 STDIN
。docker run
会默认将容器的 STDOUT
和 STDERR
重定向到宿主机终端,但并不会自动将宿主机的 STDIN
重定向到容器。?
-t
Description | Alias(es) |
---|---|
Allocate a pseudo-TTY | --tty |
The
--tty
(or-t
) flag attaches a pseudo-TTY to the container, connecting your terminal to the I/O streams of the container. Allocating a pseudo-TTY to the container means that you get access to input and output feature that TTY devices provide.
-t
选项为容器分配伪终端。
关于伪终端
伪终端是运行在用户态的终端模拟器创建的一对字符设备。其中的 pts 对应 /dev/pts/ 目录下的一个文件,ptm 在内存中标识为一个文件描述符 ( fd )。
伪终端运行在用户空间,这是它与终端的本质区别。
在使用 -t
分配伪终端的情况下,容器的 I/O 流将经过伪终端;如同在实际 Linux 机器中,伪终端负责监听键盘事件并使用从 ptm 获取的输出绘制图形。
不指定 -t
选项时,bash 等程序仍然可以运行 ( bash 可以运行在交互模式或非交互模式 ),并且直接从 STDIN
读取输入,将结果发送到 STDOUT
。? 这会带来一些功能上的限制,如一些未经终端处理的命令不能被 bash 正确识别:
1 | ls |
同时,无法利用终端的交互特性 ( 如 echo-off 输出 ),并且依赖于 TTY 的程序将无法运行。
习惯上一般将 -i
和 -t
联动使用,因为使用伪终端多数时候是为了交互,需要标准输入保持打开。
-d
Description | Alias(es) |
---|---|
Run container in background | --detach |
The
--detach
(or-d
) flag starts a container as a background process that doesn’t occupy your terminal window.
-d
会切断宿主机终端的 I/O 流与容器的连接。使用该选项可以让容器在后台运行。
使用 Docker 运行服务
Docker Docs 给出了一个启动服务的例子。
1 | docker run -d -p 80:80 my_image service nginx start |
该命令无法正常启动 nginx,因为 service nginx start
对应了 PID 为 1 的根进程。该命令在启动 nginx 后结束,同时容器退出。
因此,在启动服务时,应直接启动目标服务进程,而不是通过一个瞬间执行完毕的启动脚本或命令,以确保服务能够持续在容器中运行。
正确的启动命令如下:
1 | docker run -d -p 80:80 my_image nginx -g 'daemon off;' |
nginx -g 'daemon off;'
指定 nginx 服务以前台方式启动。实际上,无论容器是否带 -d
创建,根进程结束后容器都会随之退出。
Why -id
?
尽管 -d
断开了本次连接中容器与宿主机的交互,但 -i
仍然十分重要,它决定着下一次 attach
到容器时能否关联 STDIN
。实际上,attach
只能恢复容器创建时与宿主机之间原有的 I/O 映射关系,如果容器创建时没有打开 STDIN
,则下次连接时,仍然无法通过宿主机终端进行输入。
Why docker run
terminates ?
这是偶然发现的现象。运行如下命令,容器启动后立即退出:
1 | docker run ubuntu bash |
这可能是由于 bash 需要一个交互式终端 ( -t
) 或保持标准输入打开 ( -i
),否则它就会立即退出。?
启动 / 关闭容器
1 | docker start | stop <container> |
操作容器
可以通过 attach 或 exec 命令进入容器。执行 docker run
实际上就是 attach 到启动命令对应的主进程。
attach
Use
docker attach
to attach your terminal’s standard input, output, and error (or any combination of the three) to a running container using the container’s ID or name.
通过 attach
命令在宿主机与容器之间建立交互,并连接到主进程。这里提到 any combination of the three,如对于未使用 -i
创建的容器,attach
无法关联 STDIN
,可以认为它只能恢复容器创建时的 I/O 映射。
重定向 STDIN
You can’t redirect the standard input of a
docker attach
command while attaching to a TTY-enabled container (using the-i
and-t
options).
带 -it
创建的容器不能对 STDIN
重定向。对于这样的容器,考虑下面的命令
1 | echo "echo HelloWorld" | docker attach <container_id> |
该命令将无法执行?,因为 STDIN
必须来自宿主机终端的用户输入。( GPT 给出的解释是维持容器交互的完整性和正确性 )
从容器中脱离
使用 CTRL-p CTRL-q
序列从容器中脱离,这个操作不会使容器退出。该序列仅对带 -it
创建的容器生效,否则只能通过 CTRL-c
终止容器。
exec
exec
命令用于运行中的容器 ( 主进程正在运行 ),在其中运行一个新的命令。
1 | # COMMAND: 必选 |
exec
实际上创建了一个新的连接,这解释了为什么从 exec
中退出后,容器仍在后台运行,因为主进程并没有受到影响。反之,创建容器和 attach
连接的都是主进程,因此从中退出时会导致容器退出,如果想让容器继续运行,应使用脱离操作代替。
1 | ps -ef |
打印运行日志
使用 docker logs
打印容器的运行日志,包括 STDOUT
和 STDERR
。
1 | docker logs [OPTIONS] CONTAINER |
该命令将打印容器启动到目前为止的全部输出。?
查看容器
打印运行中 / 全部容器
1 | docker ps [-a] |
部分解释基于或直接由 GPT-4 生成。
STDOUT (1)
STDERR (2)
,不包括STDIN (0)
。在很多场景下,运行 Docker 容器通常是为了启动一项服务,例如一个 Web 服务器或数据库,而不是一个交互式的 shell 或某些需要输入的应用。?深入剖析 docker run 與 docker exec 的 -i 與 -t 技術細節 | The Will Will Web