diff --git a/docs/Ch08/index.md b/docs/Ch08/index.md index 8f45b937..2174600a 100644 --- a/docs/Ch08/index.md +++ b/docs/Ch08/index.md @@ -16,12 +16,12 @@ icon: simple/docker Docker 能够利用 Linux 内核的容器特性,隔离出一个轻便的环境来运行程序。这有什么意义呢?试想以下这些情况: -- 你运行的 Linux 发行版很老,而你需要运行一个更新版本的 Linux 发行版,或者完全不同的 Linux 发行版设计的程序。 +- 你运行的 Linux 发行版很老,而你需要运行一个更新版本的 Linux 发行版,或者完全不同的 Linux 发行版设计的程序。基于 Docker 的实现方式,与虚拟机(VM)不同,它们都共用同一个 Linux 内核(虚拟机是完全的虚拟化,包括内核和用户空间)。 - 你和朋友在设计一个大型的程序,而因为你们配置的环境不同,有时候在某个人的机器上正常运行的程序,在另一台机器上没法正常运行。 - 你希望在多台服务器上部署一个项目,但是项目需要非常复杂的配置,一个一个配置服务器的成本非常大。 - ………… -Docker 就可以帮助解决这些问题。它可以快速配置不同的环境(比如说,通过 Docker,你可以在 Ubuntu 上使用 CentOS 的环境),部署应用。 +Docker 就可以帮助解决这些问题。它可以快速配置不同的环境(比如说,通过 Docker,你可以在 Ubuntu 发行版上使用 CentOS 发行版的环境),部署应用。 ## 安装 Docker {#install-docker} @@ -70,7 +70,7 @@ Docker 可以在 Windows, Linux 和 macOS 上安装。下面我们讨论内容 $ sudo adduser 用户名 docker ``` -将需要使用 Docker 的用户[加入](../Ch05/index.md#adduser) `docker` 用户组。**注意:`docker` 用户组中的用户拥有与 root 等效的权限。** +将需要使用 Docker 的用户[加入](../Ch05/index.md#adduser) `docker` 用户组,以便使用命令行方式操作 Docker。**注意:`docker` 用户组中的用户拥有与 root 等效的权限。** ### 配置 Registry Mirror(可选,推荐) {#setup-registry-mirror} @@ -115,13 +115,13 @@ For more examples and ideas, visit: ### 在 Ubuntu 容器中使用 shell {#use-ubuntu-bash} -- `docker run -it --rm --name ubuntu-container ubuntu:20.04` +- `docker run -it --rm --name ubuntu-container ubuntu:latest` 这里,`--rm` 代表容器停止运行(退出)之后,会被立刻删除;`--name` 参数代表给容器命名,如果没有加这个参数,那么 docker 会给容器随机起一个格式类似于 gracious_brahmagupta 的名字。 `-it` 是为了获得可交互的 Shell 所必须的。`-i` 会将容器的 init(主进程,这里是 `/bin/bash`)的标准输入与 `docker` 这个程序的标准输入相连接;而 `-t` 会告知主进程输入为终端(TTY)设备。 -在执行以上命令之后,你会获得一个 Ubuntu 20.04 的容器环境,退出 Shell 之后容器就会被销毁。 +在执行以上命令之后,你会获得一个 Ubuntu(版本为 `latest` 即最新的 LTS;如果需要指定版本,可以使用类似 `20.04` 的版本号替换 `latest`,推荐在生产环境中这么做,因为 `latest` 指定的最新版本可能随时间变化)的容器环境,退出 Shell 之后容器就会被销毁。 如果没有加上 `--rm`,退出后可以使用 `docker ps -a` 查看系统中所有的容器。 @@ -191,6 +191,27 @@ $ sudo docker rm ubuntu-container ## 构建自己的 Docker 镜像 {#build-docker-image} +### Docker 的关键概念 + +在继续之前,我们来梳理一下 Docker 中的几个关键概念:**容器(container)**、**镜像(image)**、**镜像仓库(registry)**。 + +- **镜像仓库**是存储镜像的地方 +- **镜像**是 Docker 容器内文件系统的一份快照 +- **Dockerfile** 包含生成镜像的指令序列,可以理解为构建镜像的脚本 +- **容器**是一个(隔离)的运行环境 + +它们之间的关系可以用下图表示,其中括号中的命令是查看相应对象列表的命令。 + +```mermaid +flowchart TD + Registry -->|pull| Image["Image (images)"] + Image -->|run| Container["Container (ps)"] + Container -->|commit| Image + Image -->|push| Registry["Registry (search)"] + + Dockerfile -->|build| Image +``` + ### 手工构建镜像 {#build-manually} `docker commit` 命令可以从当前运行的容器新建镜像。以下是一个简单的例子: @@ -246,7 +267,7 @@ CMD ["fish"] sudo docker build -t riscv-cross:example . ``` -`-t riscv-cross:example` 代表为这个镜像打上 `riscv-cross:example` 的标签。构建完成后,使用 `docker run` 执行即可: +`-t riscv-cross:example` 代表为这个镜像打上 `riscv-cross:example` 的标签,`.` 表示从当前目录下寻找 Dockerfile 并以当前目录作为构建过程的“工作路径”。构建完成后,使用 `docker run` 执行即可: ```console $ sudo docker run -v ${PWD}/workspace:/workspace -it riscv-cross:example @@ -303,7 +324,7 @@ Docker 在根据 Dockerfile 构建时,会从上到下执行这些指令,每 RUN yum clean all ``` - 当然,这不等于说必须要把所有命令都写在一条 `RUN` 里面。对于执行时间很长的命令,可以考虑放在 Dockerfile 的开头,并且使用单独的 `RUN` 运行,因为 Docker 在构建镜像时,可以重复使用之前构建好的层。这么做可以节约构建与调试 Dockerfile 的时间。 + 当然,这不等于说必须要把所有命令都写在一条 `RUN` 里面。对于执行时间很长的命令,可以考虑放在 Dockerfile 的开头,并且使用单独的 `RUN` 运行,因为 Docker 在构建镜像时,**可以重复使用之前构建好的层**。这么做可以节约构建与调试 Dockerfile 的时间。 #### 在生产环境中运行使用 Flask 编写的简单网站 {#flask-production-example}