Fix space with bold markdown

This commit is contained in:
Baohua Yang
2026-02-21 17:39:37 -08:00
parent 6aa7a51fef
commit 2ab40eacc0
64 changed files with 148 additions and 301 deletions

4
.gitignore vendored
View File

@@ -17,3 +17,7 @@ docker-compose.override.yml
.vscode/
.agent/
__pycache__/
# Check scripts
check_project_rules.py

View File

@@ -4,7 +4,7 @@ Docker 是彻底改变了软件开发和交付方式的革命性技术。本节
### 一句话理解 Docker
> **Docker 是一种轻量级的虚拟化技术它让应用程序及其依赖环境可以被打包成一个标准化的单元在任何地方都能一致地运行**如果用一个生活中的类比**Docker 之于软件就像集装箱之于货物**
> **Docker 是一种轻量级的虚拟化技术它让应用程序及其依赖环境可以被打包成一个标准化的单元在任何地方都能一致地运行** 如果用一个生活中的类比**Docker 之于软件就像集装箱之于货物**
在集装箱发明之前货物的运输是一件麻烦的事情不同的货物需要不同的包装不同的装卸方式换一种运输工具就要重新装卸集装箱的出现改变了这一切无论里面装的是什么集装箱的外形是标准的可以用同样的方式装卸堆放和运输
@@ -44,7 +44,7 @@ flowchart LR
### Docker vs 虚拟机
很多人第一次接触 Docker 时会问**这不就是虚拟机吗**答案是**不是而且差别很大**
很多人第一次接触 Docker 时会问**这不就是虚拟机吗** 答案是**不是而且差别很大**
#### 传统虚拟机
@@ -123,4 +123,4 @@ Docker 的发展历程:
- **2015 **成立[开放容器联盟 (OCI)](https://opencontainers.org/),推动容器标准化
- **至今**[GitHub 项目](https://github.com/moby/moby)超过 7 万星标
Docker 的成功推动了整个容器生态的发展催生了 KubernetesPodman 等众多相关项目笔者认为Docker 最大的贡献不仅是技术本身更是它**让容器技术从系统管理员的工具变成了每个开发者都能使用的标准工具**
Docker 的成功推动了整个容器生态的发展催生了 KubernetesPodman 等众多相关项目笔者认为Docker 最大的贡献不仅是技术本身更是它 **让容器技术从系统管理员的工具变成了每个开发者都能使用的标准工具**

View File

@@ -10,7 +10,7 @@
具体内容如下
```
```bash
周五下午 5:00
├── 开发者:代码写完了,本地测试通过,提交!🎉
├── 周一早上 9:00
@@ -30,7 +30,7 @@
具体内容如下
```
```bash
新同事入职
├── Day 1领电脑配环境
├── Day 2继续配环境遇到问题
@@ -43,7 +43,7 @@
具体内容如下
```
```bash
运维:"我们需要把服务迁移到新服务器"
开发:"旧服务器上的配置文档在哪?"
运维:"当时是一个已经离职的同事配的……"
@@ -81,7 +81,7 @@ flowchart TD
#### 1环境一致性
Docker 镜像包含了应用运行所需的**一切**代码运行时系统工具配置这意味着
Docker 镜像包含了应用运行所需的 **一切**代码运行时系统工具配置这意味着
- 开发环境和生产环境完全一致
- 不会再有 在我机器上能跑 的问题
@@ -99,7 +99,7 @@ $ docker compose up
#### 2秒级启动
传统虚拟机启动需要几分钟 (引导操作系统) Docker 容器启动通常只需要**几秒甚至几百毫秒**
传统虚拟机启动需要几分钟 (引导操作系统) Docker 容器启动通常只需要 **几秒甚至几百毫秒**
笔者实测数据
@@ -159,7 +159,7 @@ flowchart LR
使用 [Dockerfile](../04_image/4.5_build.md) 定义镜像构建过程使得
- 构建过程**可重复可追溯**
- 构建过程 **可重复可追溯**
- 任何人都能从代码重建完全相同的镜像
- 配合 [GitHub Actions](../15_cases/ci/actions/README.md) CI 系统实现自动化
@@ -172,7 +172,7 @@ Docker 可以在几乎任何平台上运行:
- 私有云和自建数据中心
- 边缘设备和 IoT
**同一个镜像在任何地方运行结果都一致**这让应用迁移变得前所未有的简单
**同一个镜像在任何地方运行结果都一致** 这让应用迁移变得前所未有的简单
#### 6微服务架构的基石

View File

@@ -1,8 +1,8 @@
## 本章小结
- Docker 是一种轻量级虚拟化技术核心价值是**环境一致性**
- Docker 是一种轻量级虚拟化技术核心价值是 **环境一致性**
- 与虚拟机相比Docker 更轻量更快速资源利用率更高
- Docker 基于 Linux 内核的 NamespaceCgroups Union FS 技术
- Docker 推动了容器技术的标准化 (OCI) 和生态发展
Docker 的核心价值可以用一句话概括**让应用的开发测试部署保持一致同时极大提高资源利用效率**笔者认为对于现代软件开发者来说Docker 已经不是 要不要学 的问题而是**必备技能**无论你是前端后端运维还是全栈开发者掌握 Docker 都能让你的工作更高效
Docker 的核心价值可以用一句话概括**让应用的开发测试部署保持一致同时极大提高资源利用效率** 笔者认为对于现代软件开发者来说Docker 已经不是 要不要学 的问题而是 **必备技能**无论你是前端后端运维还是全栈开发者掌握 Docker 都能让你的工作更高效

View File

@@ -4,11 +4,11 @@ Docker 镜像作为容器运行的基石,其设计理念和实现机制至关
### 一句话理解镜像
> **Docker 镜像是一个只读的模板包含了运行应用所需的一切代码运行时环境变量和配置文件**如果用一个类比**镜像就像是一张光盘或 ISO 文件**你可以用同一张光盘在不同电脑上安装系统而光盘本身不会被修改同样一个镜像可以创建多个容器而镜像本身保持不变
> **Docker 镜像是一个只读的模板包含了运行应用所需的一切代码运行时环境变量和配置文件** 如果用一个类比**镜像就像是一张光盘或 ISO 文件**你可以用同一张光盘在不同电脑上安装系统而光盘本身不会被修改同样一个镜像可以创建多个容器而镜像本身保持不变
### 镜像与操作系统的关系
我们都知道操作系统分为**内核****用户空间**
我们都知道操作系统分为 **内核** **用户空间**
```mermaid
flowchart TD
@@ -25,9 +25,9 @@ flowchart TD
UserSpace --- KernelSpace
```
对于 Linux 而言内核启动后会挂载 `root` 文件系统来提供用户空间支持**Docker 镜像**本质上就是一个 `root` 文件系统
对于 Linux 而言内核启动后会挂载 `root` 文件系统来提供用户空间支持**Docker 镜像** 本质上就是一个 `root` 文件系统
例如官方镜像 `ubuntu:24.04` 包含了一套完整的 Ubuntu 24.04 最小系统的 root 文件系统**不包含 Linux 内核** (因为容器共享宿主机的内核)
例如官方镜像 `ubuntu:24.04` 包含了一套完整的 Ubuntu 24.04 最小系统的 root 文件系统 **不包含 Linux 内核** (因为容器共享宿主机的内核)
### 镜像包含什么
@@ -40,9 +40,9 @@ Docker 镜像是一个特殊的文件系统,包含:
| **配置文件** | nginx.confmy.cnf |
| **环境变量** | PATHLANG 等预设值 |
| **元数据**| 启动命令暴露端口数据卷定义 |**关键特性**
- 镜像是**只读**
- 镜像**不包含**动态数据
- 镜像构建后**内容不会改变**
- 镜像是 **只读**
- 镜像 **不包含** 动态数据
- 镜像构建后 **内容不会改变**
### 分层存储镜像的核心设计
@@ -110,7 +110,7 @@ flowchart TD
#### 分层存储的 陷阱
> **笔者特别提醒**理解这一点可以帮你避免构建出臃肿的镜像**关键原理**每一层的文件变化会被记录**删除操作只是标记不会真正减小镜像体积**
> **笔者特别提醒**理解这一点可以帮你避免构建出臃肿的镜像**关键原理**每一层的文件变化会被记录 **删除操作只是标记不会真正减小镜像体积**
```docker
## 错误示范 ❌

View File

@@ -4,7 +4,7 @@
### 一句话理解容器
> **容器是镜像的运行实例如果把镜像比作程序那么容器就是进程**用面向对象编程的术语来说**镜像是类 (Class)容器是对象 (Instance)**
> **容器是镜像的运行实例如果把镜像比作程序那么容器就是进程** 用面向对象编程的术语来说**镜像是类 (Class)容器是对象 (Instance)**
- 一个镜像可以创建多个容器
- 每个容器相互独立互不影响
@@ -78,7 +78,7 @@ flowchart TD
#### 镜像层 + 容器层
当容器运行时Docker 会在镜像的只读层之上创建一个**可写层** (容器存储层)
当容器运行时Docker 会在镜像的只读层之上创建一个 **可写层** (容器存储层)
```mermaid
flowchart TD
@@ -95,11 +95,11 @@ flowchart TD
当容器需要修改镜像层中的文件时
1. Docker 将该文件**复制**到容器存储层
1. Docker 将该文件 **复制** 到容器存储层
2. 在容器层中进行修改
3. 原始镜像层保持不变
```
```bash
读取文件:直接从镜像层读取(共享,高效)
修改文件:复制到容器层,然后修改(只有这个容器能看到修改)
```
@@ -125,7 +125,7 @@ $ docker rm abc123
#### 正确的数据持久化方式
按照 Docker 最佳实践容器存储层应该保持**无状态**需要持久化的数据应该使用
按照 Docker 最佳实践容器存储层应该保持 **无状态**需要持久化的数据应该使用
| 方式 | 说明 | 适用场景 |
|------|------|---------|
@@ -142,7 +142,7 @@ $ docker run -v mydata:/var/lib/mysql mysql
$ docker run -v /host/path:/container/path nginx
```
这些位置的读写**会跳过容器存储层**直接写入宿主机性能更好也不会随容器删除而丢失
这些位置的读写 **会跳过容器存储层**直接写入宿主机性能更好也不会随容器删除而丢失
### 容器的生命周期

View File

@@ -48,14 +48,14 @@ flowchart TB
| 概念 | 说明 | 示例 |
|------|------|------|
| **Registry** | 存储镜像的服务 | Docker Hubghcr.io |
| **Repository (仓库) ** | 同一软件的镜像集合 | `nginx``mysql``mycompany/myapp` |
| **Tag (标签) ** | 仓库内的版本标识 | `latest``1.25``alpine` |
| **Repository (仓库)** | 同一软件的镜像集合 | `nginx``mysql``mycompany/myapp` |
| **Tag (标签)** | 仓库内的版本标识 | `latest``1.25``alpine` |
#### 镜像的完整名称
一个完整的 Docker 镜像名称由 Registry 地址用户名/组织名仓库名和标签组成了解其结构有助于我们更准确地定位镜像基本格式如下
```
```bash
[registry地址/][用户名/]仓库名[:标签]
```
@@ -128,7 +128,7 @@ $ docker push username/myapp:v1.0
### 镜像加速器
由于网络原因在国内直接访问 Docker Hub 可能会很慢可以配置**镜像加速器** (Registry Mirror) 来加速下载配置示例如下
由于网络原因在国内直接访问 Docker Hub 可能会很慢可以配置 **镜像加速器** (Registry Mirror) 来加速下载配置示例如下
```json
// /etc/docker/daemon.json
@@ -190,7 +190,7 @@ $ docker pull localhost:5000/myapp:v1.0
如图 2-3 所示镜像从开发环境构建后推送到 Registry再由生产环境拉取并运行
```
```bash
开发者机器 Registry 生产服务器
│ │ │
│ docker build │ │

View File

@@ -38,8 +38,8 @@
| 概念 | 要点 |
|------|------|
| **Registry** | 存储和分发镜像的服务 |
| **仓库 (Repository) ** | 同一软件的镜像集合 |
| **标签 (Tag) ** | 版本标识默认为 latest |
| **仓库 (Repository)** | 同一软件的镜像集合 |
| **标签 (Tag)** | 版本标识默认为 latest |
| **Docker Hub** | 默认的公共 Registry |
| **私有 Registry** | 企业内部使用推荐 Harbor |

View File

@@ -1,6 +1,6 @@
## 3.10 开启实验特性
一些 docker 命令或功能仅当**实验特性**开启时才能使用请按照以下方法进行设置
一些 docker 命令或功能仅当 **实验特性** 开启时才能使用请按照以下方法进行设置
### Docker CLI 的实验特性

View File

@@ -26,21 +26,21 @@ $ brew install --cask docker
如同 macOS 其它软件一样安装也非常简单双击下载的 `.dmg` 文件然后将那只叫 [Moby](https://www.docker.com/blog/call-me-moby-dock/) 的鲸鱼图标拖拽到 `Application` 文件夹即可 (其间需要输入用户密码)。
![](../_images/install-mac-dmg.png)
![](../_images/install-mac-dmg.png)
### 运行
从应用中找到 Docker 图标并点击运行
![](../_images/install-mac-apps.png)
![](../_images/install-mac-apps.png)
运行之后会在右上角菜单栏看到多了一个鲸鱼图标这个图标表明了 Docker 的运行状态
![](../_images/install-mac-menubar.png)
![](../_images/install-mac-menubar.png)
每次点击鲸鱼图标会弹出操作菜单
![](../_images/install-mac-menu.png)
![](../_images/install-mac-menu.png)
之后你可以在终端通过命令检查安装后的 Docker 版本
@@ -57,7 +57,7 @@ $ docker run -d -p 80:80 --name webserver nginx
服务运行后可以访问 [http://localhost](http://localhost),如果看到了 “Welcome to nginx就说明 Docker Desktop for Mac 安装成功了。
![](../_images/install-mac-example-nginx.png)
![](../_images/install-mac-example-nginx.png)
要停止 Nginx 服务器并删除执行下面的命令

View File

@@ -28,11 +28,11 @@ $ winget install Docker.DockerDesktop
Windows 搜索栏输入 **Docker** 点击 **Docker Desktop** 开始运行
![](../_images/install-win-docker-app-search.png)
![](../_images/install-win-docker-app-search.png)
Docker 启动之后会在 Windows 任务栏出现鲸鱼图标
![](../_images/install-win-taskbar-circle.png)
![](../_images/install-win-taskbar-circle.png)
等待片刻当鲸鱼图标静止时说明 Docker 启动成功之后你可以打开 PowerShell 使用 Docker

View File

@@ -84,17 +84,13 @@ Registry Mirrors:
### Kubernetes 官方镜像地址迁移
可以登录[阿里云容器镜像服务](https://www.aliyun.com/product/acr?source=5176.11533457&userCode=8lx5zmtu&type=copy)**镜像中心**->**镜像搜索**查找。
可以登录[阿里云容器镜像服务](https://www.aliyun.com/product/acr?source=5176.11533457&userCode=8lx5zmtu&type=copy)**镜像中心**->**镜像搜索** 查找。
Kubernetes 社区已将官方镜像地址从 `k8s.gcr.io` 迁移到 `registry.k8s.io`建议优先使用新地址
一般情况下有如下对应关系
```bash
## 旧地址(已迁移)
## $ docker pull k8s.gcr.io/xxx
## 新地址(推荐)
$ docker pull registry.k8s.io/xxx
```
@@ -112,6 +108,6 @@ $ docker pull registry.k8s.io/xxx
### 云服务商
某些云服务商提供了**仅供内部**访问的镜像服务当您的 Docker 运行在云平台时可以选择它们
某些云服务商提供了 **仅供内部** 访问的镜像服务当您的 Docker 运行在云平台时可以选择它们
* [腾讯云 `https://mirror.ccs.tencentyun.com`](https://cloud.tencent.com/act/cps/redirect?redirect=10058&cps_key=3a5255852d5db99dcd5da4c72f05df61)

View File

@@ -14,7 +14,7 @@ docker pull [选项] [Registry地址/]仓库名[:标签]
Docker 镜像名称由 Registry 地址用户名仓库名和标签组成其标准格式如下
```
```bash
docker.io / library / ubuntu : 24.04
────┬──── ───┬─── ──┬─── ──┬──
│ │ │ │
@@ -91,7 +91,7 @@ docker.io/library/ubuntu:24.04
#### 分层下载
从输出可以看到镜像是**分层下载**
从输出可以看到镜像是 **分层下载**
```mermaid
flowchart TD

View File

@@ -58,7 +58,7 @@ Docker 镜像的大小可能与我们通常理解的文件大小有所不同,
由于镜像是分层存储不同镜像可能共享相同的层
```
```bash
ubuntu:24.04 nginx:latest redis:latest
│ │ │
└───────┬───────┘ │

View File

@@ -20,7 +20,7 @@ $ docker run --name webserver -d -p 80:80 nginx
直接用浏览器访问的话我们会看到默认的 Nginx 欢迎页面
![](../_images/images-mac-example-nginx.png)
![](../_images/images-mac-example-nginx.png)
现在假设我们非常不喜欢这个欢迎页面我们希望改成欢迎 Docker 的文字我们可以使用 `docker exec` 命令进入容器修改其内容
@@ -37,7 +37,7 @@ exit
现在我们再刷新浏览器的话会发现内容被改变了
![](../_images/images-create-nginx-docker.png)
![](../_images/images-create-nginx-docker.png)
我们修改了容器的文件也就是改动了容器的存储层我们可以通过 `docker diff` 命令看到具体的改动
@@ -130,6 +130,6 @@ docker run --name web2 -d -p 81:80 nginx:v2
首先如果仔细观察之前的 `docker diff webserver` 的结果你会发现除了真正想要修改的 `/usr/share/nginx/html/index.html` 文件外由于命令的执行还有很多文件被改动或添加了这还仅仅是最简单的操作如果是安装软件包编译构建那会有大量的无关内容被添加进来将会导致镜像极为臃肿
此外使用 `docker commit` 意味着所有对镜像的操作都是黑箱操作生成的镜像也被称为**黑箱镜像**换句话说就是除了制作镜像的人知道执行过什么命令怎么生成的镜像别人根本无从得知而且即使是这个制作镜像的人过一段时间后也无法记清具体的操作这种黑箱镜像的维护工作是非常痛苦的
此外使用 `docker commit` 意味着所有对镜像的操作都是黑箱操作生成的镜像也被称为 **黑箱镜像**换句话说就是除了制作镜像的人知道执行过什么命令怎么生成的镜像别人根本无从得知而且即使是这个制作镜像的人过一段时间后也无法记清具体的操作这种黑箱镜像的维护工作是非常痛苦的
而且回顾之前提及的镜像所使用的分层存储的概念除当前层外之前的每一层都是不会发生改变的换句话说任何修改的结果仅仅是在当前层进行标记添加修改而不会改动上一层如果使用 `docker commit` 制作镜像以及后期修改的话每一次修改都会让镜像更加臃肿一次所删除的上一层的东西并不会丢失会一直如影随形的跟着这个镜像即使根本无法访问到这会让镜像更加臃肿

View File

@@ -2,7 +2,7 @@
从刚才的 `docker commit` 的学习中我们可以了解到镜像的定制实际上就是定制每一层所添加的配置文件如果我们可以把每一层修改安装构建操作的命令都写入一个脚本用这个脚本来构建定制镜像那么之前提及的无法重复的问题镜像构建透明性的问题体积的问题就都会解决这个脚本就是 Dockerfile
Dockerfile 是一个文本文件其内包含了一条条的**指令 (Instruction)**每一条指令构建一层因此每一条指令的内容就是描述该层应当如何构建
Dockerfile 是一个文本文件其内包含了一条条的 **指令 (Instruction)**每一条指令构建一层因此每一条指令的内容就是描述该层应当如何构建
### 使用 docker init 快速创建 (推荐)
@@ -37,7 +37,7 @@ RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
### FROM 指定基础镜像
所谓定制镜像那一定是以一个镜像为基础在其上进行定制就像我们之前运行了一个 `nginx` 镜像的容器再进行修改一样基础镜像是必须指定的 `FROM` 就是指定**基础镜像**因此一个 `Dockerfile` `FROM` 是必备的指令并且必须是第一条指令
所谓定制镜像那一定是以一个镜像为基础在其上进行定制就像我们之前运行了一个 `nginx` 镜像的容器再进行修改一样基础镜像是必须指定的 `FROM` 就是指定 **基础镜像**因此一个 `Dockerfile` `FROM` 是必备的指令并且必须是第一条指令
[Docker Hub](https://hub.docker.com/search?q=&type=image&image_filter=official) 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如 [`nginx`](https://hub.docker.com/_/nginx/)、[`redis`](https://hub.docker.com/_/redis/)、[`mongo`](https://hub.docker.com/_/mongo/)、[`mysql`](https://hub.docker.com/_/mysql/)、[`httpd`](https://hub.docker.com/_/httpd/)、[`php`](https://hub.docker.com/_/php/)、[`tomcat`](https://hub.docker.com/_/tomcat/) 等;也有一些方便开发、构建、运行各种语言应用的镜像,如 [`node`](https://hub.docker.com/_/node)、[`openjdk`](https://hub.docker.com/_/openjdk/)、[`python`](https://hub.docker.com/_/python/)、[`ruby`](https://hub.docker.com/_/ruby/)、[`golang`](https://hub.docker.com/_/golang/) 等。可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。
@@ -72,9 +72,9 @@ Dockerfile 中每一个指令都会建立一层,`RUN` 也不例外。每一个
>
> 每一个 `RUN` 指令都会产生一个新的镜像层为了减少镜像体积和层数我们通常会将多个命令合并到一个 `RUN` 指令中执行
>
> 更多关于 `RUN` 指令的详细用法最佳实践 (如清理缓存使用 pipefail ) `Union FS` 的层数限制等内容请参阅**[第七章 Dockerfile 指令详解](../07_dockerfile/README.md)**中的** [RUN 指令](../07_dockerfile/7.1_run.md)**小节
> 更多关于 `RUN` 指令的详细用法最佳实践 (如清理缓存使用 pipefail ) `Union FS` 的层数限制等内容请参阅 **[第七章 Dockerfile 指令详解](../07_dockerfile/README.md)** 中的 **[RUN 指令](../07_dockerfile/7.1_run.md)** 小节
要想编写优秀的 `Dockerfile`必须了解每一条指令的作用和副作用**[第七章 Dockerfile 指令详解](../07_dockerfile/README.md)**我们将对 `COPY``ADD``CMD``ENTRYPOINT` 等指令进行详细讲解
要想编写优秀的 `Dockerfile`必须了解每一条指令的作用和副作用 **[第七章 Dockerfile 指令详解](../07_dockerfile/README.md)** 我们将对 `COPY``ADD``CMD``ENTRYPOINT` 等指令进行详细讲解
### 构建镜像
@@ -106,7 +106,7 @@ docker build [选项] <上下文路径/URL/->
### 镜像构建上下文
如果注意会看到 `docker build` 命令最后有一个 `.``.` 表示当前目录 `Dockerfile` 就在当前目录因此不少初学者以为这个路径是在指定 `Dockerfile` 所在路径这么理解其实是不准确的如果对应上面的命令格式你可能会发现这是在指定**上下文路径**那么什么是上下文呢
如果注意会看到 `docker build` 命令最后有一个 `.``.` 表示当前目录 `Dockerfile` 就在当前目录因此不少初学者以为这个路径是在指定 `Dockerfile` 所在路径这么理解其实是不准确的如果对应上面的命令格式你可能会发现这是在指定 **上下文路径**那么什么是上下文呢
首先我们要理解 `docker build` 的工作原理Docker 在运行时分为 Docker 引擎 (也就是服务端守护进程) 和客户端工具Docker 的引擎提供了一组 REST API被称为 [Docker Remote API](https://docs.docker.com/develop/sdk/),而如 `docker` 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 `docker` 功能,但实际上,一切都是使用的远程调用形式在服务端 (Docker 引擎) 完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。
@@ -120,7 +120,7 @@ docker build [选项] <上下文路径/URL/->
COPY ./package.json /app/
```
这并不是要复制执行 `docker build` 命令所在的目录下的 `package.json`也不是复制 `Dockerfile` 所在目录下的 `package.json`而是复制**上下文 (context)** 目录下的 `package.json`
这并不是要复制执行 `docker build` 命令所在的目录下的 `package.json`也不是复制 `Dockerfile` 所在目录下的 `package.json`而是复制 **上下文 (context)** 目录下的 `package.json`
因此`COPY` 这类指令中的源文件的路径都是*相对路径*这也是初学者经常会问的为什么 `COPY ../package.json /app` 或者 `COPY /opt/xxxx /app` 无法工作的原因因为这些路径已经超出了上下文的范围Docker 引擎无法获得这些位置的文件如果真的需要那些文件应该将它们复制到上下文目录中去

View File

@@ -8,7 +8,7 @@ Docker 镜像是怎么实现增量的修改和维护的?为什么容器启动
Docker 镜像并不是一个单纯的文件而是由一组文件系统叠加构成的
最底层的镜像称为**基础镜像 (Base Image)**通常是各种 Linux 发行版的 root 文件系统 UbuntuDebianCentOS
最底层的镜像称为 **基础镜像 (Base Image)**通常是各种 Linux 发行版的 root 文件系统 UbuntuDebianCentOS
当我们在基础镜像之上构建新的镜像时 (例如安装了 Nginx)Docker 并不是复制一份基础镜像而是在基础镜像之上**新建一个层 (Layer)**并在该层中仅记录为了安装 Nginx 而发生的文件变更 (添加修改删除)
@@ -23,7 +23,7 @@ Docker 镜像并不是一个单纯的文件,而是由一组文件系统叠加
那么既然镜像只读容器为什么能写文件呢
当容器启动时Docker 会在镜像的最上层添加一个新的**可写层 (Writable Layer)**通常被称为**容器层**
当容器启动时Docker 会在镜像的最上层添加一个新的 **可写层 (Writable Layer)**通常被称为 **容器层**
```mermaid
flowchart TD
@@ -40,7 +40,7 @@ flowchart TD
```
* **读取文件**当容器需要读取文件时Docker 会从最上层 (容器层) 开始向下层 (镜像层) 寻找直到找到该文件为止
* **修改文件**当容器需要修改某个文件时Docker 会从下层镜像中将该文件复制到上层的容器层然后对副本进行修改这被称为**写时复制 (Copy-on-WriteCoW)** 策略
* **修改文件**当容器需要修改某个文件时Docker 会从下层镜像中将该文件复制到上层的容器层然后对副本进行修改这被称为 **写时复制 (Copy-on-WriteCoW)** 策略
* **删除文件**当容器删除某个文件时Docker 并不是真的去下层删除它 (因为下层是只读的)而是在容器层创建一个特殊的 白障 (Whiteout) 文件用来标记该文件已被删除从而在容器视图中隐藏它
这就是为什么
@@ -60,6 +60,6 @@ Docker 镜像的每一层都有一个唯一的 ID这个 ID 是根据该层的
Docker 使用联合文件系统 (Union FS) 来实现这种分层挂载常见的驱动包括 `overlay2` (目前推荐)`aufs` (早期使用)`btrfs``zfs`
虽然实现细节不同但它们都遵循上述的**分层 + CoW** 模型
虽然实现细节不同但它们都遵循上述的 **分层 + CoW** 模型
> 想要深入了解 Overlay2 等文件系统的具体实现原理包括 WorkDirUpperDirLowerDir 等底层细节请阅读**[第十四章底层实现](../14_implementation/README.md)**中的**[联合文件系统](../14_implementation/14.4_ufs.md)**章节
> 想要深入了解 Overlay2 等文件系统的具体实现原理包括 WorkDirUpperDirLowerDir 等底层细节请阅读 **[第十四章底层实现](../14_implementation/README.md)** 中的 **[联合文件系统](../14_implementation/14.4_ufs.md)** 章节

View File

@@ -9,7 +9,7 @@
- **前台运行**程序占用当前终端输出直接显示关闭终端程序就停止
- **后台运行**程序在后台执行不占用终端终端关闭也不影响程序
Docker 容器默认是**前台运行**使用 `-d` (detach) 参数可以让容器在后台运行
Docker 容器默认是 **前台运行** 使用 `-d` (detach) 参数可以让容器在后台运行
### 基本使用
@@ -51,7 +51,7 @@ $ docker run -d ubuntu:24.04 /bin/sh -c "while true; do echo hello world; sleep
### 深入理解容器为什么会 立即退出
> **这是初学者最常遇到的困惑**理解这个问题你就理解了 Docker 的核心设计理念
> **这是初学者最常遇到的困惑** 理解这个问题你就理解了 Docker 的核心设计理念
很多人尝试这样启动容器
@@ -84,7 +84,7 @@ flowchart TD
**关键理解**
- `-d` 参数**不是**让容器 一直运行
- `-d` 参数 **不是** 让容器 一直运行
- `-d` 参数是让容器 在后台运行能运行多久取决于主进程
#### 常见的 立即退出 场景
@@ -163,7 +163,7 @@ $ docker run -d -p 6379:6379 redis
#### 2调试时先用前台模式
当容器启动有问题时**去掉 `-d` 参数**可以直接看到输出和错误
当容器启动有问题时**去掉 `-d` 参数** 可以直接看到输出和错误
```bash
## 有问题的容器,先前台运行看看发生了什么

View File

@@ -135,7 +135,7 @@ $ docker attach 容器名
#### 工作原理
`attach` 会附加到容器的**主进程** (PID 1) 的标准输入输出
`attach` 会附加到容器的 **主进程** (PID 1) 的标准输入输出
```mermaid
flowchart LR
@@ -181,7 +181,7 @@ CONTAINER ID IMAGE STATUS NAMES
#### 安全退出 attach
使用 `Ctrl+P` 然后 `Ctrl+Q` 可以从 attach 会话中**分离**而不停止容器
使用 `Ctrl+P` 然后 `Ctrl+Q` 可以从 attach 会话中 **分离**而不停止容器
```bash
$ docker attach myubuntu

View File

@@ -47,7 +47,7 @@ RUN echo "hello" > world.txt # 文件在根目录!
如下代码块所示展示了相关示例
```
```dockerfile
RUN cd /app
启动容器 → cd /app仅内存变化→ 提交镜像层 → 容器销毁

View File

@@ -80,7 +80,7 @@ USER 1001:1001
### 用户必须已存在
`USER` 指令只能切换到**已存在**的用户
`USER` 指令只能切换到 **已存在** 的用户
```docker
## ❌ 错误:用户不存在

View File

@@ -17,7 +17,7 @@ HEALTHCHECK NONE
### 为什么需要 HEALTHCHECK
在没有 HEALTHCHECK 之前Docker 只能通过**进程退出码**来判断容器状态**问题场景**
在没有 HEALTHCHECK 之前Docker 只能通过 **进程退出码** 来判断容器状态**问题场景**
- Web 服务死锁无法响应请求但进程仍在运行
- 数据库正在启动中尚未准备好接受连接
@@ -26,7 +26,7 @@ HEALTHCHECK NONE
**引入 HEALTHCHECK **
Docker 定期执行指定的检查命令根据返回值判断容器是否 健康
```
```bash
容器状态转换:
Starting ──成功──> Healthy ──失败N次──> Unhealthy
▲ │
@@ -211,6 +211,6 @@ HEALTHCHECK --start-period=60s CMD curl -f http://localhost/ || exit 1
#### 4只检查核心依赖
健康检查应主要关注**当前服务**是否可用而不是检查其下游依赖 (数据库等)下游依赖的检查应由应用逻辑处理
健康检查应主要关注 **当前服务** 是否可用而不是检查其下游依赖 (数据库等)下游依赖的检查应由应用逻辑处理
---

View File

@@ -10,13 +10,13 @@
ONBUILD <其它指令>
```
`ONBUILD` 是一个特殊的指令它后面跟的是其它指令 ( `RUN``COPY` )这些指令**在当前镜像构建时不会执行**只有当以当前镜像为基础镜像去构建下一级镜像时才会被执行
`ONBUILD` 是一个特殊的指令它后面跟的是其它指令 ( `RUN``COPY` )这些指令 **在当前镜像构建时不会执行**只有当以当前镜像为基础镜像去构建下一级镜像时才会被执行
---
### 为什么需要 ONBUILD
`ONBUILD` 主要用于制作**语言栈基础镜像****框架基础镜像**
`ONBUILD` 主要用于制作 **语言栈基础镜像** **框架基础镜像**
#### 场景维护 Node.js 项目
@@ -64,7 +64,7 @@ FROM my-node-base
如下代码块所示展示了相关示例
```
```bash
基础镜像构建:
Dockerfile (含 ONBUILD) ──build──> 基础镜像 (记录了 ONBUILD 触发器)
(指令未执行)
@@ -119,15 +119,15 @@ ONBUILD COPY dist/ /usr/share/nginx/html/
#### 1继承性限制
`ONBUILD` 指令**只会继承一次**
`ONBUILD` 指令 **只会继承一次**
- 镜像 A ( ONBUILD)
- 镜像 B (FROM A) -> 触发 ONBUILD
- 镜像 C (FROM B) -> **不会**再次触发 ONBUILD
- 镜像 C (FROM B) -> **不会** 再次触发 ONBUILD
#### 2构建上下文
子镜像构建时`ONBUILD COPY . .` 中的 `.` 指的是**子项目**的构建上下文而不是基础镜像的上下文
子镜像构建时`ONBUILD COPY . .` 中的 `.` 指的是 **子项目** 的构建上下文而不是基础镜像的上下文
#### 3不允许级联
@@ -147,7 +147,7 @@ ONBUILD COPY dist/ /usr/share/nginx/html/
建议在镜像标签中添加 `-onbuild` 后缀明确告知使用者该镜像包含触发器
```
```bash
node:20-onbuild
python:3.12-onbuild
```

View File

@@ -110,7 +110,7 @@ RUN echo "Using sh again"
### 对其他指令的影响
`SHELL` 影响的是所有使用 **shell 格式**的指令
`SHELL` 影响的是所有使用 **shell 格式** 的指令
| 指令格式 | 是否受 SHELL 影响 |
|---------|-------------------|

View File

@@ -172,7 +172,7 @@ $ docker run -dit --rm --network=laravel -p 8080:80 my/nginx
### 生产环境优化
本小节内容为了方便测试将配置文件直接放到了镜像中实际在使用时**建议**将配置文件作为 `config` `secret` 挂载到容器中请读者自行学习 `Kubernetes` 的相关内容
本小节内容为了方便测试将配置文件直接放到了镜像中实际在使用时 **建议** 将配置文件作为 `config` `secret` 挂载到容器中请读者自行学习 `Kubernetes` 的相关内容
由于篇幅所限本小节只是简单列出更多内容可以参考 https://github.com/khs1994-docker/laravel-demo 项目。

View File

@@ -11,7 +11,7 @@ RUN <command>
RUN ["executable", "param1", "param2"]
```
`RUN` 指令是 Dockerfile 中最常用的指令之一它在**当前镜像层**之上创建一个新层执行指定的命令并提交结果
`RUN` 指令是 Dockerfile 中最常用的指令之一它在 **当前镜像层** 之上创建一个新层执行指定的命令并提交结果
---

View File

@@ -58,9 +58,9 @@ COPY src/*.js /app/src/
COPY src/ /app/src/
```
> **注意**复制目录时复制的是目录的**内容**不包含目录本身
> **注意**复制目录时复制的是目录的 **内容**不包含目录本身
```
```bash
构建上下文: 镜像内:
src/ /app/src/
├── index.js → ├── index.js

View File

@@ -68,7 +68,7 @@ ADD ubuntu-noble-core-cloudimg-amd64-root.tar.gz /
如下代码块所示展示了相关示例
```
```bash
ADD app.tar.gz /app/
├─ 识别 .tar.gz 格式

View File

@@ -57,7 +57,7 @@ CMD echo $HOME
CMD ["sh", "-c", "echo $HOME"]
```
**优点**可以使用环境变量管道等 shell 特性**缺点**主进程是 sh信号无法正确传递给应用
**优点**可以使用环境变量管道等 shell 特性 **缺点**主进程是 sh信号无法正确传递给应用
---
@@ -109,7 +109,7 @@ $ docker run -it ubuntu # 进入 bash
$ docker run ubuntu cat /etc/os-release # 覆盖为 cat 命令
```
```
```bash
Dockerfile: docker run 命令:
CMD ["/bin/bash"] + cat /etc/os-release
│ │
@@ -138,7 +138,7 @@ CMD service nginx start
如下代码块所示展示了相关示例
```
```bash
1. CMD service nginx start
↓ 被转换为
2. CMD ["sh", "-c", "service nginx start"]
@@ -170,8 +170,8 @@ CMD ["nginx", "-g", "daemon off;"]
| 指令 | 用途 | 运行时行为 |
|------|------|-----------|
| **CMD**| 默认命令 | `docker run` 参数会**覆盖** |
| **ENTRYPOINT**| 入口点 | `docker run` 参数会**追加**到它后面 |
| **CMD**| 默认命令 | `docker run` 参数会 **覆盖** |
| **ENTRYPOINT**| 入口点 | `docker run` 参数会 **追加** 到它后面 |
#### 单独使用 CMD

View File

@@ -4,7 +4,7 @@
### 什么是 ENTRYPOINT
`ENTRYPOINT` 指定容器启动时运行的入口程序 CMD 不同ENTRYPOINT 定义的命令不会被 `docker run` 的参数覆盖而是**接收这些参数**
`ENTRYPOINT` 指定容器启动时运行的入口程序 CMD 不同ENTRYPOINT 定义的命令不会被 `docker run` 的参数覆盖而是 **接收这些参数**
> **核心作用**让镜像像一个可执行程序一样使用`docker run` 的参数作为这个程序的参数
@@ -141,7 +141,7 @@ HTTP/1.1 200 OK
如下代码块所示展示了相关示例
```
```bash
ENTRYPOINT ["curl", "-s", "http://myip.ipip.net"]
docker run myip -i
@@ -199,7 +199,7 @@ exec "$@"
如下代码块所示展示了相关示例
```
```bash
docker run redis docker run redis bash
│ │
▼ ▼

View File

@@ -26,7 +26,7 @@ ARG <参数名>[=<默认值>]
| **适用场景** | 构建参数 (版本号等)| 应用配置 |
| **可见性** | `docker history` 可见 | `docker inspect` 可见 |
```
```dockerfile
构建时 运行时
├─ ARG VERSION=1.0 │ ARG 已消失)
├─ ENV APP_ENV=prod │ APP_ENV=prod仍存在

View File

@@ -10,7 +10,7 @@
EXPOSE <端口> [<端口>/<协议>...]
```
`EXPOSE` 声明容器运行时提供服务的端口这是一个**文档性质的声明**告诉使用者容器会监听哪些端口
`EXPOSE` 声明容器运行时提供服务的端口这是一个 **文档性质的声明**告诉使用者容器会监听哪些端口
---

View File

@@ -4,7 +4,7 @@
## 什么是 Dockerfile
Dockerfile 是一个文本文件其內包含了一条条的**指令 (Instruction)**每一条指令构建一层therefore 每一条指令的内容就是描述该层应当如何构建
Dockerfile 是一个文本文件其內包含了一条条的 **指令 (Instruction)**每一条指令构建一层therefore 每一条指令的内容就是描述该层应当如何构建
[第四章](../04_image/README.md)我们通过 `docker commit` 学习了镜像的构成但是手动 `commit` 只能作为临时修补并不适合作为生产环境镜像的构建方式

View File

@@ -4,7 +4,7 @@
### 什么是绑定挂载
Bind Mount (绑定挂载) **宿主机的目录或文件**直接挂载到容器中容器可以读写宿主机的文件系统
Bind Mount (绑定挂载) **宿主机的目录或文件** 直接挂载到容器中容器可以读写宿主机的文件系统
```mermaid
flowchart LR

View File

@@ -7,7 +7,7 @@
Docker 容器的 DNS 配置有两种情况
1. **默认 Bridge 网络**继承宿主机的 DNS 配置 (`/etc/resolv.conf`)
2. **自定义网络** (推荐)使用 Docker 嵌入式 DNS 服务器 (Embedded DNS)支持通过**容器名**进行服务发现
2. **自定义网络** (推荐)使用 Docker 嵌入式 DNS 服务器 (Embedded DNS)支持通过 **容器名** 进行服务发现
---
@@ -58,6 +58,7 @@ $ docker run --dns-search=example.com myapp
```
#### 3. --hostname (-h)
设置容器的主机名
```bash
@@ -99,7 +100,7 @@ $ docker run -h myweb nginx
**现象**`ping db` 提示 `bad address 'db'`**原因**
- 你可能在使用**默认的 bridge 网络**默认 bridge 网络**不支持**通过容器名进行 DNS 解析 (这是一个历史遗留设计)
- 你可能在使用 **默认的 bridge 网络**默认 bridge 网络 **不支持** 通过容器名进行 DNS 解析 (这是一个历史遗留设计)
- **解决**使用自定义网络 (`docker network create ...`)
---

View File

@@ -10,7 +10,7 @@
- **宿主机访问容器**可以通过容器 IP 访问
- **外部网络访问容器** 默认无法直接访问
为了让外部 (如你的浏览器其他局域网机器) 访问容器内的服务我们需要将容器的端口**映射**到宿主机的端口
为了让外部 (如你的浏览器其他局域网机器) 访问容器内的服务我们需要将容器的端口 **映射** 到宿主机的端口
```mermaid
flowchart TD

View File

@@ -2,7 +2,7 @@
**BuildKit** 是下一代的镜像构建组件 https://github.com/moby/buildkit 开源。
> **重要** Docker 23.0 BuildKit 已成为**默认稳定构建器**无需手动启用Docker Engine v29 进一步将 Containerd 镜像存储设为默认提升与 Kubernetes 的互操作性
> **重要** Docker 23.0 BuildKit 已成为 **默认稳定构建器**无需手动启用Docker Engine v29 进一步将 Containerd 镜像存储设为默认提升与 Kubernetes 的互操作性
目前Docker Hub 自动构建已经支持 BuildKit具体请参考 https://github.com/docker-practice/docker-hub-buildx
@@ -48,7 +48,7 @@ COPY --from=builder /app/dist /app/dist
使用多阶段构建构建的镜像中只包含了目标文件夹 `dist`但仍然存在一些问题 `package.json` 文件变动时`RUN npm i && rm -rf ~/.npm` 这一层会重新执行变更多次后生成了大量的中间层镜像
为解决这个问题进一步的我们可以设想一个类似**数据卷**的功能在镜像构建时把 `node_modules` 文件夹挂载上去在构建完成后这个 `node_modules` 文件夹会自动卸载实际的镜像中并不包含 `node_modules` 这个文件夹这样我们就省去了每次获取依赖的时间大大增加了镜像构建效率同时也避免了生成了大量的中间层镜像
为解决这个问题进一步的我们可以设想一个类似 **数据卷** 的功能在镜像构建时把 `node_modules` 文件夹挂载上去在构建完成后这个 `node_modules` 文件夹会自动卸载实际的镜像中并不包含 `node_modules` 这个文件夹这样我们就省去了每次获取依赖的时间大大增加了镜像构建效率同时也避免了生成了大量的中间层镜像
`BuildKit` 提供了 `RUN --mount=type=cache` 指令可以实现上边的设想

View File

@@ -324,7 +324,3 @@ services:
* `--no-up` 不自动启动服务
* `--quiet` 静默模式
### 参考资料
* [官方文档](https://docs.docker.com/compose/reference/overview/)

View File

@@ -564,8 +564,3 @@ MONGO_VERSION=3.6
```
执行 `docker compose up` 则会启动一个 `mongo:3.6` 镜像的容器
### 参考资料
* [官方文档](https://docs.docker.com/compose/compose-file/)
* [awesome-compose](https://github.com/docker/awesome-compose)

View File

@@ -209,7 +209,7 @@ $ docker compose run --rm web django-admin startproject mysite .
生成的目录结构
```
```bash
django-docker/
├── compose.yaml
├── Dockerfile
@@ -266,7 +266,7 @@ $ docker compose up
2. 启动 db 服务等待健康检查通过
3. 启动 web 服务
```
```bash
db-1 | PostgreSQL init process complete; ready for start up.
db-1 | LOG: database system is ready to accept connections
web-1 | Watching for file changes with StatReloader
@@ -340,7 +340,7 @@ $ sudo chown -R $USER:$USER .
### 开发 vs 生产关键差异
笔者特别提醒本节的配置是**开发环境**配置生产环境需要以下调整
笔者特别提醒本节的配置是 **开发环境** 配置生产环境需要以下调整
| 配置项 | 开发环境 | 生产环境 |
|--------|---------|---------|

View File

@@ -202,7 +202,7 @@ $ docker compose up
输出示例
```
```bash
db-1 | PostgreSQL init process complete; ready for start up.
db-1 | LOG: database system is ready to accept connections
web-1 | => Booting Puma

View File

@@ -8,7 +8,7 @@ WordPress 是全球最流行的内容管理系统 (CMS)。使用 Docker Compose
如下代码块所示展示了相关示例
```
```bash
wordpress/
├── compose.yaml
├── .env # 环境变量(敏感信息)

View File

@@ -399,6 +399,7 @@ $ cosign verify --key cosign.pub $IMAGE
```
### 3. SLSA (Supply-chain Levels for Software Artifacts)
遵循 SLSA 框架确保构建过程的完整性例如使用 GitHub Actions 等受控环境进行构建而非在开发者本地机器上构建发布
---

View File

@@ -6,7 +6,7 @@
`etcdctl` 支持如下的命令大体上分为数据库操作和非数据库操作两类后面将分别进行解释
```
```bash
NAME:
etcdctl - A simple command line client for etcd3.

View File

@@ -10,7 +10,7 @@
Kubernetes (常简称为 K8s) Google 开源的容器编排引擎如果说 Docker 解决了 如何打包和运送集装箱 的问题那么 Kubernetes 解决的就是 如何管理海量集装箱的调度运行和维护 的问题
它不仅仅是一个编排系统更是一个**云原生应用操作系统**
它不仅仅是一个编排系统更是一个 **云原生应用操作系统**
> **名字由来**Kubernetes 在希腊语中意为 舵手 飞行员K8s 是因为 k s 之间有 8 个字母
@@ -71,7 +71,7 @@ Kubernetes 的最小调度单位。一个 Pod 可以包含一个或多个紧密
### 架构
Kubernetes 也是 C/S 架构 **Control Plane (控制平面) **** Worker Node (工作节点)** 组成
Kubernetes 也是 C/S 架构 **Control Plane (控制平面)** **Worker Node (工作节点)** 组成
- **Control Plane**负责决策 (API ServerSchedulerController Manageretcd)
- **Worker Node**负责干活 (KubeletKube-proxyContainer Runtime)

View File

@@ -2,7 +2,7 @@
[Kubernetes Dashboard](https://github.com/kubernetes/dashboard) 是基于网页的 Kubernetes 用户界面。
![](https://d33wubrfki0l68.cloudfront.net/349824f68836152722dab89465835e604719caea/6e0b7/images/docs/ui-dashboard.png)
![](https://d33wubrfki0l68.cloudfront.net/349824f68836152722dab89465835e604719caea/6e0b7/images/docs/ui-dashboard.png)
### 部署

View File

@@ -6,7 +6,7 @@
Docker Desktop 设置页面点击 `Kubernetes`选择 `Enable Kubernetes`稍等片刻看到左下方 `Kubernetes` 变为 `running`Kubernetes 启动成功
![](https://github.com/docker/docs/raw/main/assets/images/desktop/settings-kubernetes.png)
![](https://github.com/docker/docs/raw/main/assets/images/desktop/settings-kubernetes.png)
> 注意Kubernetes 的镜像存储在 `registry.k8s.io`如果国内网络无法直接访问可以在 Docker Desktop 配置中的 `Docker Engine` 处配置镜像加速器或者利用国内云服务商的镜像仓库手动拉取镜像并 retag

View File

@@ -32,7 +32,7 @@ sudo k3s kubectl get nodes
```
输出类似
```
```bash
NAME STATUS ROLES AGE VERSION
k3s-master Ready control-plane,master 1m v1.35.1+k3s1
```

View File

@@ -2,7 +2,7 @@
`kubeadm` 提供了 `kubeadm init` 以及 `kubeadm join` 这两个命令作为快速创建 `Kubernetes` 集群的最佳实践
> **重要说明** Kubernetes 1.24 内置 `dockershim` 已被移除Kubernetes 默认不再直接使用 Docker Engine 作为容器运行时 (CRI)因此**更推荐参考**同目录下的[使用 kubeadm 部署 Kubernetes (CRI 使用 containerd)](kubeadm.md)
> **重要说明** Kubernetes 1.24 内置 `dockershim` 已被移除Kubernetes 默认不再直接使用 Docker Engine 作为容器运行时 (CRI)因此**更推荐参考** 同目录下的[使用 kubeadm 部署 Kubernetes (CRI 使用 containerd)](kubeadm.md)
>
> 本文档主要用于历史环境/学习目的如果你确实需要在较新版本中继续使用 Docker Engine通常需要额外部署 `cri-dockerd` 并在 `kubeadm init/join` 中指定 `--cri-socket`
@@ -175,7 +175,7 @@ kubeadm join 192.168.199.100:6443 --token cz81zt.orsy9gm9v649e5lf \
#### node 工作节点
**另一主机**重复**部署**小节以前的步骤安装配置好 kubelet根据提示加入到集群
**另一主机** 重复 **部署** 小节以前的步骤安装配置好 kubelet根据提示加入到集群
```bash
$ kubeadm join 192.168.199.100:6443 --token cz81zt.orsy9gm9v649e5lf \

View File

@@ -22,7 +22,7 @@ $ sudo yum install containerd.io
新建 `/etc/systemd/system/cri-containerd.service` 文件
```
```bash
[Unit]
Description=containerd container runtime for kubernetes
Documentation=https://containerd.io
@@ -399,7 +399,7 @@ kubeadm join 192.168.199.100:6443 --token cz81zt.orsy9gm9v649e5lf \
#### node 工作节点
**另一主机**重复**部署**小节以前的步骤安装配置好 kubelet根据提示加入到集群
**另一主机** 重复 **部署** 小节以前的步骤安装配置好 kubelet根据提示加入到集群
```bash
$ systemctl enable cri-containerd

View File

@@ -27,11 +27,11 @@ graph LR
Docker 的内部架构如同洋葱一样分层每一层专注解决特定问题
#### 1Docker CLI (客户端)
#### 1. Docker CLI (客户端)
用户与 Docker 交互的主要方式它将用户命令 ( `docker run`) 转换为 API 请求发送给 dockerd
#### 2Dockerd (守护进程)
#### 2. Dockerd (守护进程)
Docker 的大脑
@@ -39,16 +39,16 @@ Docker 的大脑。
- 管理 Docker 对象 (镜像容器网络)
- 编排下层组件完成工作
#### 3Containerd (高级运行时)
#### 3. Containerd (高级运行时)
行业标准的容器运行时 (CNCF 毕业项目)
- 管理容器的完整生命周期 (启动停止)
- 镜像拉取与存储
- **不包含**复杂的与容器无关的功能 (如构建API)
- **不包含** 复杂的与容器无关的功能 (如构建API)
- Kubernetes 也可以直接使用 containerd (跳过 Docker)
#### 4Runc (低级运行时)
#### 4. Runc (低级运行时)
用于创建和运行容器的 CLI 工具
@@ -99,9 +99,12 @@ flowchart TD
Shim -.-> |8. Monitor IO/Exit| Container
```
1. **CLI** 发送请求给 **Dockerd**2**Dockerd** 解析请求调用 **Containerd**3**Containerd** 准备镜像转换为 OCI Bundle
1. **CLI** 发送请求给 **Dockerd**
2. **Dockerd** 解析请求调用 **Containerd**
3. **Containerd** 准备镜像转换为 OCI Bundle
4. **Containerd** 创建 **Shim** 进程
5. **Shim** 调用 **Runc**6**Runc** 与系统内核交互创建 Namespaces Cgroups
5. **Shim** 调用 **Runc**
6. **Runc** 与系统内核交互创建 Namespaces Cgroups
7. **Runc** 启动 nginx 进程后退出
8. **Shim** 接管容器 IO 和生命周期监控

View File

@@ -2,7 +2,7 @@
## 什么是 Namespace
> **Namespace Linux 内核提供的资源隔离机制它让容器内的进程仿佛运行在独立的操作系统中**Namespace 是容器技术的核心基础之一它回答了一个关键问题**如何让一个进程 以为 自己独占整个系统**
> **Namespace Linux 内核提供的资源隔离机制它让容器内的进程仿佛运行在独立的操作系统中** Namespace 是容器技术的核心基础之一它回答了一个关键问题**如何让一个进程 以为 自己独占整个系统**
```mermaid
flowchart LR
@@ -124,7 +124,7 @@ MNT Namespace 负责文件系统挂载点的隔离,确保容器看到独立的
如下代码块所示展示了相关示例
```
```bash
宿主机文件系统: 容器内看到的:
/ / ← 容器的根目录
├── bin/ ├── bin/

View File

@@ -4,7 +4,7 @@
### 什么是控制组
控制组 (Control Groups简称 cgroups) Linux 内核的一个特性用于**限制记录和隔离**进程组的资源使用 (CPU内存磁盘 I/O网络等)
控制组 (Control Groups简称 cgroups) Linux 内核的一个特性用于 **限制记录和隔离** 进程组的资源使用 (CPU内存磁盘 I/O网络等)
> **核心作用**让多个容器公平共享宿主机资源防止单个容器耗尽系统资源

View File

@@ -4,7 +4,7 @@
### 什么是联合文件系统
联合文件系统 (UnionFS) 是一种**分层轻量级**的文件系统它将多个目录 联合 挂载到同一个虚拟目录形成一个统一的文件系统视图
联合文件系统 (UnionFS) 是一种 **分层轻量级** 的文件系统它将多个目录 联合 挂载到同一个虚拟目录形成一个统一的文件系统视图
> **核心思想**将多个只读层叠加最上层可写形成完整的文件系统

View File

@@ -1,6 +1,6 @@
# CI/CD
**持续集成 (Continuous integration)**是一种软件开发实践每次集成都通过自动化的构建 (包括编译发布自动化测试) 来验证从而尽早地发现集成错误**持续部署 (continuous deployment)** 是通过自动化的构建测试和部署循环来快速交付高质量的产品
**持续集成 (Continuous integration)** 是一种软件开发实践每次集成都通过自动化的构建 (包括编译发布自动化测试) 来验证从而尽早地发现集成错误**持续部署 (continuous deployment)** 是通过自动化的构建测试和部署循环来快速交付高质量的产品
`Jenkins` 不同的是基于 Docker CI/CD 每一步都运行在 Docker 容器中所以理论上支持所有的编程语言

View File

@@ -88,7 +88,7 @@ $ git push origin master
打开我们部署好的 `Drone` 网站或者 Drone Cloud即可看到构建结果
![](../../../_images/drone-build.png)
![](../../../_images/drone-build.png)
当然我们也可以把构建结果上传到 GitHubDocker Registry云服务商提供的对象存储或者生产环境中

View File

@@ -18,7 +18,7 @@
登录 GitHub https://github.com/settings/applications/new 新建一个应用。
![](https://docs.drone.io/screenshots/github_application_create.png)
![](https://docs.drone.io/screenshots/github_application_create.png)
接下来查看这个应用的详情记录 `Client ID` `Client Secret`之后配置 Drone 会用到

View File

@@ -1,6 +1,6 @@
## Debian Ubuntu
`Debian` `Ubuntu` 都是目前较为流行的 **Debian **的服务器操作系统十分适合研发场景`Docker Hub` 上提供了官方镜像国内各大容器云服务也基本都提供了相应的支持
`Debian` `Ubuntu` 都是目前较为流行的 **Debian ** 的服务器操作系统十分适合研发场景`Docker Hub` 上提供了官方镜像国内各大容器云服务也基本都提供了相应的支持
### Debian 系统简介
@@ -9,7 +9,7 @@
![Debian 操作系统](../../_images/debian-logo.png)
`Debian` 是由 `GPL` 和其他自由软件许可协议授权的自由软件组成的操作系统 **Debian 计划 (Debian Project)**组织维护**Debian 计划**是一个独立的分散的组织 `3000` 人志愿者组成接受世界多个非盈利组织的资金支持`Software in the Public Interest` 提供支持并持有商标作为保护机构`Debian` 以其坚守 `Unix` 和自由软件的精神以及其给予用户的众多选择而闻名现时 `Debian` 包括了超过 `25,000` 个软件包并支持 `12` 个计算机系统结构
`Debian` 是由 `GPL` 和其他自由软件许可协议授权的自由软件组成的操作系统 **Debian 计划 (Debian Project)** 组织维护**Debian 计划** 是一个独立的分散的组织 `3000` 人志愿者组成接受世界多个非盈利组织的资金支持`Software in the Public Interest` 提供支持并持有商标作为保护机构`Debian` 以其坚守 `Unix` 和自由软件的精神以及其给予用户的众多选择而闻名现时 `Debian` 包括了超过 `25,000` 个软件包并支持 `12` 个计算机系统结构
`Debian` 作为一个大的系统组织框架其下有多种不同操作系统核心的分支计划主要为采用 `Linux` 核心的 `Debian GNU/Linux` 系统其他还有采用 `GNU Hurd` 核心的 `Debian GNU/Hurd` 系统采用 `FreeBSD` 核心的 `Debian GNU/kFreeBSD` 系统以及采用 `NetBSD` 核心的 `Debian GNU/NetBSD` 系统甚至还有利用 `Debian` 的系统架构和工具采用 `OpenSolaris` 核心构建而成的 `Nexenta OS` 系统在这些 `Debian` 系统中以采用 `Linux` 核心的 `Debian GNU/Linux` 最为著名

View File

@@ -7,7 +7,7 @@
1. 图号格式统一为`图 <章号>-<序号> <图题>`例如 `图 10-2 Rails + PostgreSQL 的 Compose 架构`
2. 图号在同一章内按出现顺序连续编号不重复不跳号
3. 正文引用图片统一写法`如图 <章号>-<序号> 所示`不使用 下图/上图/示意图如下
4. 所有图片必须提供有意义的 alt 文本不使用空 alt (`![](...)`)
4. 所有图片必须提供有意义的 alt 文本不使用空 alt (`![](...)`)
5. 图题单独成行放在图片下方
### 章节风格规范

View File

@@ -124,7 +124,7 @@ $ docker run --network=my-net --ip=172.25.3.3 -itd --name=my-container busybox
例如如下操作将默认存储位置迁移到 /storage/docker
```
```bash
[root@s26 ~]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/VolGroup-lv_root 50G 5.3G 42G 12% /

View File

@@ -6,9 +6,9 @@
[CentOS](https://en.wikipedia.org/wiki/CentOS) 是流行的 Linux 发行版,其软件包大多跟 RedHat 系列保持一致。
> **重要提示**CentOS 8 已于 2021 12 31 日停止维护 (EOL)CentOS 7 也已于 2024 6 30 **完全结束支持**Docker Hub 上的 CentOS 官方镜像**已停止更新**且存在未修复的安全漏洞
> **重要提示**CentOS 8 已于 2021 12 31 日停止维护 (EOL)CentOS 7 也已于 2024 6 30 **完全结束支持**Docker Hub 上的 CentOS 官方镜像 **已停止更新** 且存在未修复的安全漏洞
>
> 2026 年了对于任何新项目**强烈建议**使用以下生产级替代方案
> 2026 年了对于任何新项目**强烈建议** 使用以下生产级替代方案
> - [Rocky Linux](https://hub.docker.com/_/rockylinux)CentOS 原创始人发起的社区驱动项目,目前主流为 Rocky Linux 9。
> - [AlmaLinux](https://hub.docker.com/_/almalinux):由 CloudLinux 支持的企业级发行版,提供长期支持。
> - [CentOS Stream](https://hub.docker.com/r/centos/centos)RHEL 的上游开发分支 (适合开发测试,不建议用于生产环境)。

View File

@@ -2,9 +2,9 @@
下图直观地展示了本节内容
[![](https://img.shields.io/github/stars/yeasy/docker_practice.svg?style=social&label=Stars)](https://github.com/yeasy/docker_practice) [![](https://img.shields.io/github/release/yeasy/docker_practice/all.svg)](https://github.com/yeasy/docker_practice/releases) [![](https://img.shields.io/badge/Based-Docker%20Engine%20v29.x-blue.svg)](https://docs.docker.com/engine/release-notes/) [![](https://img.shields.io/badge/Docker%20%E6%8A%80%E6%9C%AF%E5%85%A5%E9%97%A8%E4%B8%8E%E5%AE%9E%E6%88%98-jd.com-red.svg)][1]
[![](https://img.shields.io/github/stars/yeasy/docker_practice.svg?style=social&label=Stars)](https://github.com/yeasy/docker_practice) [![](https://img.shields.io/github/release/yeasy/docker_practice/all.svg)](https://github.com/yeasy/docker_practice/releases) [![](https://img.shields.io/badge/Based-Docker%20Engine%20v29.x-blue.svg)](https://docs.docker.com/engine/release-notes/) [![](https://img.shields.io/badge/Docker%20%E6%8A%80%E6%9C%AF%E5%85%A5%E9%97%A8%E4%B8%8E%E5%AE%9E%E6%88%98-jd.com-red.svg)][1]
**v1.5.5**
**v1.5.6**
[Docker](https://www.docker.com) 是个划时代的开源项目,它彻底释放了计算虚拟化的威力,极大提高了应用的维护效率,降低了云计算应用开发的成本!使用 Docker可以让应用的部署、测试和分发都变得前所未有的高效和轻松
@@ -78,7 +78,7 @@ npx honkit serve
下图直观地展示了本节内容
[![](https://github.com/yeasy/docker_practice/raw/master/_images/docker_primer4.jpg)][1]
[![](https://github.com/yeasy/docker_practice/raw/master/_images/docker_primer4.jpg)][1]
[Docker 技术入门与实战][1]已更新到第 4 讲解最新容器技术栈知识欢迎大家阅读并反馈建议

View File

@@ -1,148 +0,0 @@
import os
import re
import argparse
def check_file(filepath, verbose=False):
violations = []
with open(filepath, 'r', encoding='utf-8') as f:
content = f.read()
lines = content.split('\n')
filename = os.path.basename(filepath)
is_readme = filename.lower() == 'readme.md'
is_summary = filename.lower() == 'summary.md'
is_section = re.match(r'^\d+\.\d+_.*\.md$', filename)
# 1.1 Bold Text: No spaces inside the bold markers
for i, line in enumerate(lines):
if '** ' in line or ' **' in line:
# We must be careful: '** ' might be a bold start with space inside, but it could also be regular text.
# Let's use a simpler, line-bound regex line by line
if re.search(r'\*\*\s+[^*]+\*\*|\*\*[^*]+\s+\*\*', line):
violations.append(f"1.1 Bold Text: Space inside bold markers at line {i+1}")
# 1.4 Trailing Newline
if not content.endswith('\n') or content.endswith('\n\n'):
if content: # ignore empty files
violations.append("1.4 Trailing Newline: File must end with exactly one newline character")
headers = []
for i, line in enumerate(lines):
m = re.match(r'^(#{1,6})\s+(.*)', line)
if m:
level = len(m.group(1))
text = m.group(2)
headers.append({'line': i, 'level': level, 'text': text})
# 1.2 Header Spacing
if i + 1 < len(lines):
next_line = lines[i+1].strip()
if next_line != '':
violations.append(f"1.2 Header Spacing: Header at line {i+1} not followed by a blank line")
if i + 2 < len(lines):
if lines[i+1].strip() == '' and lines[i+2].strip() == '':
violations.append(f"1.2 Header Spacing: Header at line {i+1} followed by multiple blank lines")
# 1.3 Header Hierarchy
for j in range(len(headers) - 1):
curr_level = headers[j]['level']
next_level = headers[j+1]['level']
if next_level > curr_level + 1:
violations.append(f"1.3 Header Hierarchy: Skipped header level from H{curr_level} to H{next_level} at line {headers[j+1]['line']+1}")
# 2.2 File Header Levels
if headers:
first_header_level = headers[0]['level']
if is_readme and first_header_level != 1:
violations.append("2.2 File Header Levels: README.md first header must be level 1")
if is_summary and first_header_level != 2:
violations.append("2.2 File Header Levels: SUMMARY.md first header must be level 2")
if is_section and first_header_level != 2:
violations.append("2.2 File Header Levels: Section file first header must be level 2")
# 2.2 No English Parentheses in Headers unless very common terminologies
for h in headers:
text = h['text']
i = h['line']
if re.search(r'[A-Za-z\s]+', text):
violations.append(f"2.2 No English Parentheses: Header at line {i+1} contains English in parentheses: {text}")
# 2.3 Single Child Headers
for j in range(len(headers)):
level = headers[j]['level']
children = 0
for k in range(j+1, len(headers)):
if headers[k]['level'] <= level:
break
if headers[k]['level'] == level + 1:
children += 1
if children == 1:
violations.append(f"2.3 Single Child Headers: Header at line {headers[j]['line']+1} has exactly 1 child")
# 2.5 Bridge Text
for j in range(len(headers)):
level = headers[j]['level']
child_line = -1
for k in range(j+1, len(headers)):
if headers[k]['level'] <= level:
break
if headers[k]['level'] == level + 1:
child_line = headers[k]['line']
break
if child_line != -1:
text_between = '\n'.join([l.strip() for l in lines[headers[j]['line']+1:child_line] if l.strip()])
if not text_between:
violations.append(f"2.5 Bridge Text: Header at line {headers[j]['line']+1} is followed by a sub-header without introductory text")
# 3.2 Content Introduction
in_code_block = False
for j, line in enumerate(lines):
if line.startswith('```'):
if not in_code_block:
for k in range(j-1, -1, -1):
if lines[k].strip():
if lines[k].startswith('#'):
violations.append(f"3.2 Content Introduction: Code block at line {j+1} immediately follows a header")
break
in_code_block = not in_code_block
elif "![" in line and "](" in line:
for k in range(j-1, -1, -1):
if lines[k].strip():
if lines[k].startswith('#'):
violations.append(f"3.2 Content Introduction: Image at line {j+1} immediately follows a header")
break
return violations
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-v', '--verbose', action='store_true')
args = parser.parse_args()
md_files = []
for root, dirs, files in os.walk('.'):
for f in files:
if f.endswith('.md') and '.git' not in root and 'node_modules' not in root and '.vuepress' not in root and 'book_rule.md' not in f and 'rules_result.txt' not in f:
md_files.append(os.path.join(root, f))
total_violations = 0
for f in md_files:
try:
violations = check_file(f, args.verbose)
if args.verbose:
print(f"Scanned {f}")
if violations:
print(f"\\nViolations in {f}:")
for v in violations:
print(f" - {v}")
total_violations += len(violations)
except Exception as e:
print(f"Error reading {f}: {e}")
if total_violations == 0:
print("No violations found!")
else:
print(f"\\nTotal violations: {total_violations}")
if __name__ == '__main__':
main()

View File

@@ -1 +0,0 @@
Total issues found: 0