安装 Docker

Docker Desktop for Windows 依赖于:

  • Hyper-V
  • WSL

确保开启 Hyper-V,并且已安装 WSL。

开启Hyper-V

基本命令

拉取镜像

Docker 会先尝试从本地查找镜像。

1
docker pull <image[:tag]>

创建容器

run 命令同样会检查镜像是否在本地,并自动下载尚未 pull 的镜像。容器的 I/O 流会被自动重定向到宿主机。

1
2
# COMMAND: 启动命令,可选
docker run [OPTIONS] <image[:tag]> [COMMAND[-c ARGS]]

容器一经创建,其启动命令将在每次 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’s STDIN open, and lets you send input to the container through standard input.

-i 选项用于打开容器的 STDINdocker run 会默认将容器的 STDOUTSTDERR 重定向到宿主机终端,但并不会自动将宿主机的 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
2
/# ls
bash: line 3: $'ls\r': command not found

同时,无法利用终端的交互特性 ( 如 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
2
# COMMAND: 必选
docker exec [OPTIONS] CONTAINER COMMAND[-c ARGS]

exec 实际上创建了一个新的连接,这解释了为什么从 exec 中退出后,容器仍在后台运行,因为主进程并没有受到影响。反之,创建容器和 attach 连接的都是主进程,因此从中退出时会导致容器退出,如果想让容器继续运行,应使用脱离操作代替。

1
2
3
4
5
/# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 13:31 pts/0 00:00:00 bash # 主进程
root 11 0 0 13:31 pts/1 00:00:00 bash # exec创建
root 23 11 0 13:32 pts/1 00:00:00 ps -ef

打印运行日志

使用 docker logs 打印容器的运行日志,包括 STDOUTSTDERR

1
docker logs [OPTIONS] CONTAINER

该命令将打印容器启动到目前为止的全部输出。?

查看容器

打印运行中 / 全部容器

1
docker ps [-a]




部分解释基于或直接由 GPT-4 生成。


  1. STDOUT (1) STDERR (2),不包括 STDIN (0)。在很多场景下,运行 Docker 容器通常是为了启动一项服务,例如一个 Web 服务器或数据库,而不是一个交互式的 shell 或某些需要输入的应用。?

  2. docker run | Docker Docs

  3. Linux 伪终端(pty) - sparkdev - 博客园

  4. 什么是交互式shell和非交互式shell? | 毛英东的个人博客

  5. 理解Linux 终端、终端模拟器和伪终端_Linux_swordholder_InfoQ写作社区

  6. docker container attach | Docker Docs

  7. docker exec | Docker Docs

  8. docker container logs | Docker Docs

  9. 深入剖析 docker run 與 docker exec 的 -i 與 -t 技術細節 | The Will Will Web

  10. docker 伪输入终端 docker 虚拟化技术_mob6454cc627440的技术博客_51CTO博客

  11. docker基本使用 | Hexo

  12. 如何执行docker run,docker run 命令参考文档 | Deepzz’s Blog

  13. 如何将docker 镜像上传到docker hub仓库 - JerryMouseLi - 博客园

  14. Docker 教程 | 菜鸟教程

  15. docker run | Docker Docs

  16. docker run | Docker Docs

  17. 前言 | Docker — 从入门到实践

  18. Docker 入门教程 - 阮一峰的网络日志