返回首页返回书籍《Docker 从入门到实践》
目录

目录 · CONTENTS

  1. 01前言
  2. 02修订记录
  3. 03如何贡献
  4. 04第一章 Docker 简介
  5. 051.1 快速上手
  6. 061.2 什么是 Docker
  7. 071.3 为什么要用 Docker
  8. 08本章小结
  9. 09第二章 基本概念
  10. 102.1 镜像
  11. 112.2 容器
  12. 122.3 仓库
  13. 13本章小结
  14. 14第三章 安装 Docker
  15. 153.1 Ubuntu
  16. 163.2 Debian
  17. 173.3 Fedora
  18. 183.4 CentOS
  19. 193.5 Raspberry Pi
  20. 203.6 Linux 离线安装
  21. 213.7 macOS
  22. 223.8 Windows 10/11
  23. 233.9 镜像加速器
  24. 243.10 开启实验特性
  25. 25本章小结
  26. 26第四章 使用镜像
  27. 274.1 获取镜像
  28. 284.2 列出镜像
  29. 294.3 删除本地镜像
  30. 304.4 利用 commit 理解镜像构成
  31. 314.5 使用 Dockerfile 定制镜像
  32. 324.6 其他制作镜像的方式
  33. 334.7 实现原理
  34. 34本章小结
  35. 35第五章 操作容器
  36. 365.1 启动
  37. 375.2 守护态运行
  38. 385.3 终止
  39. 395.4 进入容器
  40. 405.5 导出和导入
  41. 415.6 删除
  42. 42本章小结
  43. 43第六章 访问仓库
  44. 446.1 Docker Hub
  45. 456.2 私有仓库
  46. 466.3 私有仓库高级配置
  47. 476.4 Nexus 3
  48. 48本章小结
  49. 49第七章 Dockerfile 指令详解
  50. 507.1 RUN 执行命令
  51. 517.2 COPY 复制文件
  52. 527.3 ADD 更高级的复制文件
  53. 537.4 CMD 容器启动命令
  54. 547.5 ENTRYPOINT 入口点
  55. 557.6 ENV 设置环境变量
  56. 567.7 ARG 构建参数
  57. 577.8 VOLUME 定义匿名卷
  58. 587.9 EXPOSE 暴露端口
  59. 597.10 WORKDIR 指定工作目录
  60. 607.11 USER 指定当前用户
  61. 617.12 HEALTHCHECK 健康检查
  62. 627.13 ONBUILD 为他人作嫁衣裳
  63. 637.14 LABEL 为镜像添加元数据
  64. 647.15 SHELL 指令
  65. 657.16 参考文档
  66. 667.17 多阶段构建
  67. 677.18 实战多阶段构建 Laravel 镜像
  68. 68本章小结
  69. 69第八章 数据管理
  70. 708.1 数据卷
  71. 718.2 挂载主机目录
  72. 728.3 tmpfs 挂载
  73. 73本章小结
  74. 74第九章 网络配置
  75. 759.1 配置 DNS
  76. 769.2 网络类型
  77. 779.3 自定义网络
  78. 789.4 容器互联
  79. 799.5 外部访问容器
  80. 809.6 网络隔离
  81. 819.7 容器网络高级特性
  82. 82本章小结
  83. 83第十章 Docker Buildx
  84. 8410.1 BuildKit
  85. 8510.2 使用 buildx 构建镜像
  86. 8610.3 使用 buildx 构建多种系统架构支持的 Docker 镜像
  87. 87本章小结
  88. 88第十一章 Docker Compose
  89. 8911.1 简介
  90. 9011.2 安装与卸载
  91. 9111.3 使用
  92. 9211.4 命令说明
  93. 9311.5 Compose 模板文件
  94. 9411.6 实战 Django
  95. 9511.7 实战 Rails
  96. 9611.8 实战 WordPress
  97. 9711.9 实战 LNMP
  98. 98本章小结
  99. 99第十二章 底层实现
  100. 10012.1 基本架构
  101. 10112.2 命名空间
  102. 10212.3 控制组
  103. 10312.4 联合文件系统
  104. 10412.5 容器格式
  105. 10512.6 网络
  106. 106本章小结
  107. 107第十三章 容器编排基础
  108. 10813.1 简介
  109. 10913.2 基本概念
  110. 11013.3 架构设计
  111. 11113.4 高级特性
  112. 11213.5 实战练习
  113. 113本章小结
  114. 114第十四章 部署 Kubernetes
  115. 11514.1 使用 kubeadm 部署 Kubernetes
  116. 11614.2 使用 kubeadm 部署 Kubernetes:使用 Docker
  117. 11714.3 在 Docker Desktop 使用
  118. 11814.4 Kind - Kubernetes IN Docker
  119. 11914.5 K3s - 轻量级 Kubernetes
  120. 12014.6 一步步部署 Kubernetes 集群
  121. 12114.7 可视化管理界面
  122. 12214.8 Kubernetes 命令行 kubectl
  123. 123本章小结
  124. 124第十五章 Etcd 项目
  125. 12515.1 简介
  126. 12615.2 安装
  127. 12715.3 集群
  128. 12815.4 使用 etcdctl
  129. 129本章小结
  130. 130第十六章 容器与云计算
  131. 13116.1 简介
  132. 13216.2 腾讯云
  133. 13316.3 阿里云
  134. 13416.4 亚马逊云
  135. 13516.5 多云部署策略
  136. 136本章小结
  137. 137第十七章 容器其他生态
  138. 13817.1 Fedora CoreOS 简介
  139. 13917.2 Fedora CoreOS 安装
  140. 14017.3 Podman - 下一代 Linux 容器工具
  141. 14117.4 Buildah - 容器镜像构建工具
  142. 14217.5 Skopeo - 容器镜像管理工具
  143. 14317.6 containerd - 核心容器运行时
  144. 14417.7 安全容器运行时
  145. 14517.8 WebAssembly 与容器
  146. 146本章小结
  147. 147第十八章 安全
  148. 14818.1 内核命名空间
  149. 14918.2 控制组
  150. 15018.3 服务端防护
  151. 15118.4 内核能力机制
  152. 15218.5 其他安全特性
  153. 15318.6 容器镜像安全扫描与供应链安全
  154. 154本章小结
  155. 155第十九章 容器监控与日志
  156. 15619.1 Prometheus
  157. 15719.2 ELK 套件
  158. 15819.3 容器性能优化与故障诊断
  159. 159本章小结
  160. 160第二十章 实战案例 - 操作系统
  161. 16120.1 Busybox
  162. 16220.2 Alpine
  163. 16320.3 Debian Ubuntu
  164. 16420.4 CentOS Fedora
  165. 165本章小结
  166. 166第二十一章 实战案例 - DevOps
  167. 16721.1 DevOps 完整工作流
  168. 16821.2 GitHub Actions
  169. 16921.3 Drone
  170. 17021.4 Drone Demo
  171. 17121.5 在 IDE 中使用 Docker
  172. 17221.6 VS Code
  173. 17321.7 实战案例:Go/Rust/数据库/微服务
  174. 174本章小结
  175. 175附录
  176. 176附录一:常见问题与错误速查
  177. 177常见错误处理
  178. 178附录二:热门镜像介绍
  179. 179Ubuntu
  180. 180CentOS
  181. 181Nginx
  182. 182PHP
  183. 183Node.js
  184. 184MySQL
  185. 185WordPress
  186. 186MongoDB
  187. 187Redis
  188. 188Minio
  189. 189附录三:Docker 命令查询
  190. 190客户端命令 - docker
  191. 191服务端命令 - dockerd
  192. 192附录四:Dockerfile 最佳实践
  193. 193附录五:如何调试 Docker
  194. 194附录六:资源链接
  195. 195附录七:术语表
  196. 196附录八:Docker 学习路线图与知识体系
第 176 / 196 节

第 176 节 · SECTION 176

附录一:常见问题与错误速查

更多错误排查条目见 常见错误处理。

镜像相关

如何批量清理临时镜像文件?

答:可以使用 docker image prune 命令。

如何查看镜像支持的环境变量?

答:可以使用 docker run IMAGE env 命令。

本地的镜像文件都存放在哪里?

答:与 Docker 相关的本地资源默认存放在 /var/lib/docker/ 目录下,以 overlay2 文件系统为例,其中 containers 目录存放容器信息,image 目录存放镜像信息,overlay2 目录下存放具体的镜像层文件。

构建 Docker 镜像应该遵循哪些原则?

答:整体原则上,尽量保持镜像功能的明确和内容的精简,要点包括

  • 尽量选取满足需求但较小的基础系统镜像,例如大部分时候可以选择 alpine 镜像,仅有不足六兆大小;
  • 清理编译生成文件、安装包的缓存等临时文件;
  • 安装各个软件时候要指定准确的版本号,并避免引入不需要的依赖;
  • 从安全角度考虑,应用要尽量使用系统的库和依赖;
  • 如果安装应用时候需要配置一些特殊的环境变量,在安装后要还原不需要保持的变量值;
  • 使用 Dockerfile 创建镜像时候要添加 .dockerignore 文件或使用干净的工作目录。

更多内容请查看 Dockerfile 最佳实践

碰到网络问题,无法 pull 镜像,命令行指定 http_proxy 无效?

答:先区分代理要作用在哪一层。Docker daemon 拉取镜像时,推荐在 daemon.json 的 proxies 字段或 systemd drop-in 中配置 HTTP_PROXY / HTTPS_PROXY / NO_PROXY,然后重启 Docker 服务。Docker CLI、构建过程和容器内应用的代理应分别使用 ~/.docker/config.json、--build-arg 或 docker run --env 配置;不要把 export http_proxy=... 当作 daemon.json 内容写入。

容器相关

容器退出后,通过 docker container ls 命令查看不到,数据会丢失么?

答:容器退出后会处于终止 (exited) 状态,此时可以通过 docker container ls -a 查看。其中的数据也不会丢失,还可以通过 docker start 命令来启动它。只有删除掉容器才会清除所有数据。

如何停止所有正在运行的容器?

答:可以使用 docker stop $(docker container ls -q) 命令。

如何批量清理已经停止的容器?

答:可以使用 docker container prune 命令。

如何获取某个容器的 PID 信息?

答:可以使用

docker inspect --format '{{ .State.Pid }}' <CONTAINER ID or NAME>

如何获取某个容器的 IP 地址?

答:可以使用

docker inspect --format '{{ .NetworkSettings.IPAddress }}' <CONTAINER ID or NAME>

如何给容器指定一个固定 IP 地址,而不是每次重启容器 IP 地址都会变?

答:使用以下命令启动容器可以使容器 IP 固定不变

$ docker network create -d bridge --subnet 172.25.0.0/16 my-net

$ docker run --network=my-net --ip=172.25.3.3 -itd --name=my-container busybox

这个固定 IP 主要用于同一 Docker daemon 内的容器间通信。Docker Desktop 上不要依赖宿主机直接访问 Linux 容器 IP;宿主机访问容器服务仍应使用端口映射或 host.docker.internal 等机制。

如何临时退出一个正在交互的容器的终端,而不终止它?

答:按 Ctrl-p Ctrl-q。如果按 Ctrl-c 往往会让容器内应用进程终止,进而会终止容器。

使用 docker port 命令映射容器的端口时,系统报错 “Error:No public port ‘80’ published for xxx”?

答:

  • 创建容器时用 -p HOST_PORT:CONTAINER_PORT 或 --publish 显式发布端口,例如 docker run -p 8080:80 nginx;
  • 只想把镜像声明的 EXPOSE 端口随机发布到宿主机端口时,用 -P / --publish-all,之后通过 docker ps 查看实际端口;
  • Dockerfile 中的 EXPOSE 只是镜像元数据,不会自动发布端口。

可以在一个容器中同时运行多个应用进程么?

答:一般并不推荐在同一个容器内运行多个应用进程。如果有类似需求,可以通过一些额外的进程管理机制,比如 supervisord 来管理所运行的进程。可以参考 Docker 官方说明。

如何控制容器占用 CPU、内存等系统资源的份额?

答:在使用 docker create 命令创建容器或使用 docker run 创建并启动容器的时候,可以使用 -c|--cpu-shares[=0] 参数来调整容器使用 CPU 的权重;使用 -m|--memory[=MEMORY] 参数来调整容器使用内存的大小。

仓库相关

仓库、注册服务器、注册索引有何关系?

首先,仓库是存放一组关联镜像的集合,比如同一个应用的不同版本的镜像。

注册服务器是存放实际的镜像文件的地方。注册索引则负责维护用户的账号、权限、搜索、标签等的管理。因此,注册服务器利用注册索引来实现认证等管理。

配置相关

Docker 的配置文件放在哪里,如何修改配置?

答:使用 systemd 的系统(如 Ubuntu 22.04+、Debian 12+、Rocky/Alma/CentOS Stream 9+)的配置文件在 /etc/docker/daemon.json。

如何更改 Docker 的默认存储位置?

答:Docker 的默认存储位置是 /var/lib/docker,如果希望将 Docker 的本地文件存储到其他分区,可以使用 Linux 软连接的方式来完成,或者修改配置文件 /etc/docker/daemon.json 的 data-root 项。可以使用 docker info | grep "Docker Root Dir" 查看当前使用的存储位置。

例如,如下操作将默认存储位置迁移到 /storage/docker。

[root@s26 ~]# df -h
Filesystem                    Size  Used Avail Use% Mounted on
/dev/mapper/VolGroup-lv_root   50G  5.3G   42G  12% /
tmpfs                          48G  228K   48G   1% /dev/shm
/dev/sda1                     485M   40M  420M   9% /boot
/dev/mapper/VolGroup-lv_home  222G  188M  210G   1% /home
/dev/sdb2                     2.7T  323G  2.3T  13% /storage
[root@s26 ~]# service docker stop
[root@s26 ~]# cd /var/lib/
[root@s26 lib]# mv docker /storage/
[root@s26 lib]# ln -s /storage/docker/ docker
[root@s26 lib]# ls -la docker
lrwxrwxrwx. 1 root root 15 11月 17 13:43 docker -> /storage/docker
[root@s26 lib]# service docker start

使用内存和 swap 限制启动容器时候报内核不支持警告?

答:如果遇到 WARNING: Your kernel does not support cgroup swap limit 等警告,这是因为系统默认没有开启对内存和 swap 使用的统计功能,引入该功能会带来性能的下降。要开启该功能,可以采取如下操作:

  • 编辑 /etc/default/grub 文件(Ubuntu 系统为例),配置 GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"
  • 更新 grub:$ sudo update-grub
  • 重启系统,即可。

Docker 与虚拟化

Docker 与 LXC 有何不同?

答:LXC 利用 Linux 上相关技术实现了容器。Docker 则在如下的几个方面进行了改进:

  • 移植性:通过抽象容器配置,容器可以实现从一个平台移植到另一个平台;
  • 镜像系统:基于 OverlayFS 的镜像系统为容器的分发带来了很多的便利,同时共同的镜像层只需要存储一份,实现高效率的存储;
  • 版本管理:类似于 Git 的版本管理理念,用户可以更方便的创建、管理镜像文件;
  • 仓库系统:仓库系统大大降低了镜像的分发和管理的成本;
  • 周边工具:各种现有工具(配置管理、云平台)对 Docker 的支持,以及基于 Docker 的 PaaS、CI 等系统,让 Docker 的应用更加方便和多样化。

Docker 与 Vagrant 有何不同?

答:两者的定位完全不同。

  • Vagrant 类似 Boot2Docker(一款运行 Docker 的最小内核),是一套虚拟机的管理环境。Vagrant 可以在多种系统上和虚拟机软件中运行,可以在 Windows,Mac 等非 Linux 平台上为 Docker 提供支持,自身具有较好的包装性和移植性。
  • 原生的 Docker 自身只能运行在 Linux 平台上,但启动和运行的性能都比虚拟机要快,往往更适合快速开发和部署应用的场景。

简单说:Vagrant 适合用来管理虚拟机,而 Docker 适合用来管理应用环境。

开发环境中 Docker 和 Vagrant 该如何选择?

答:Docker 不是虚拟机,而是进程隔离,对于资源的消耗很少,但是目前需要 Linux 环境支持。Vagrant 是虚拟机上做的封装,虚拟机本身会消耗资源。

如果本地使用的 Linux 环境,推荐都使用 Docker。

如果本地使用的是 macOS 或者 Windows 环境,那就需要开虚拟机,单一开发环境下 Vagrant 更简单;多环境开发下推荐在 Vagrant 里面再使用 Docker 进行环境隔离。

其他

Docker 能在非 Linux 平台上运行么?比如 Windows 或 macOS

答:完全可以。安装方法请查看安装 Docker 一节

如何将一台宿主主机的 Docker 环境迁移到另外一台宿主主机?

答:停止 Docker 服务。将整个 Docker 存储文件夹复制到另外一台宿主主机,然后调整另外一台宿主主机的配置即可。

如何进入 Docker 容器的网络命名空间?

答:Docker 在创建容器后,删除了宿主主机上 /var/run/netns 目录中的相关的网络命名空间文件。因此,在宿主主机上是无法看到或访问容器的网络命名空间的。

用户可以通过如下方法来手动恢复它。

首先,使用下面的命令查看容器进程信息,比如这里的 1234。

$ docker inspect --format='{{ .State.Pid }}' $container_id
1234

接下来,在 /proc 目录下,把对应的网络命名空间文件链接到 /var/run/netns 目录。

$ sudo ln -s /proc/1234/ns/net /var/run/netns/

然后,在宿主主机上就可以看到容器的网络命名空间信息。例如

$ sudo ip netns show
1234

此时,用户可以通过正常的系统命令来查看或操作容器的命名空间了。例如修改容器的 IP 地址信息为 172.17.0.100/16。

$ sudo ip netns exec 1234 ifconfig eth0 172.17.0.100/16

如何获取容器绑定到本地哪个 veth 接口上?

答:Docker 容器启动后,会通过 veth 接口对连接到本地网桥,veth 接口命名跟容器命名毫无关系,十分难以找到对应关系。

最简单的一种方式是通过查看接口的索引号,在容器中执行 ip a 命令,查看到本地接口最前面的接口索引号,如 205,将此值加上 1,即 206,然后在本地主机执行 ip a 命令,查找接口索引号为 206 的接口,两者即为连接的 veth 接口对。