Use a better structure

This commit is contained in:
Baohua Yang
2026-02-09 09:32:05 -08:00
parent fdb879dcf2
commit e669ee0fe8
167 changed files with 2462 additions and 2462 deletions

View File

@@ -1,6 +1,6 @@
# 基本架构
## 基本架构
## 核心架构图
### 核心架构图
Docker 采用了 **C/S (客户端/服务端)** 架构Client Daemon 发送请求Daemon 负责构建运行和分发容器
@@ -21,33 +21,33 @@ graph LR
---
## 组件详解
### 组件详解
Docker 的内部架构如同洋葱一样分层每一层专注解决特定问题
### 1. Docker CLI (客户端)
#### 1. Docker CLI (客户端)
用户与 Docker 交互的主要方式它将用户命令 `docker run`转换为 API 请求发送给 dockerd
### 2. Dockerd (守护进程)
#### 2. Dockerd (守护进程)
Docker 的大脑
- 监听 API 请求
- 管理 Docker 对象镜像容器网络
- 编排下层组件完成工作
### 3. Containerd (高级运行时)
#### 3. Containerd (高级运行时)
行业标准的容器运行时CNCF 毕业项目
- 管理容器的完整生命周期启动停止
- 镜像拉取与存储
- **不包含** 复杂的与容器无关的功能如构建API
- Kubernetes 也可以直接使用 containerd跳过 Docker
### 4. Runc (低级运行时)
#### 4. Runc (低级运行时)
用于创建和运行容器的 CLI 工具
- 直接与内核交互Namespaces, Cgroups
- 遵循 OCI (Open Container Initiative) 规范
- **主要职责**根据配置启动一个容器然后退出将控制权交给容器进程
### 5. Shim
#### 5. Shim
每个容器都有一个 shim 进程
- **解耦**允许 dockerd 重启而不影响容器运行
- **保持 IO**维持容器的标准输入输出
@@ -55,7 +55,7 @@ Docker 的大脑。
---
## 容器启动流程
### 容器启动流程
当执行 `docker run -d nginx` 内部发生了什么
@@ -99,7 +99,7 @@ flowchart TD
---
## Docker Engine v29+ 变化
### Docker Engine v29+ 变化
Docker Engine v29 (2025/2026) 开始架构进一步简化和标准化
@@ -108,7 +108,7 @@ flowchart TD
---
## Docker Desktop 架构
### Docker Desktop 架构
macOS Windows 因为内核差异架构稍微复杂
@@ -132,7 +132,7 @@ flowchart TD
---
## 总结
### 总结
| 组件 | 角色 | 关键职责 |
|------|------|----------|
@@ -142,8 +142,8 @@ flowchart TD
| **Shim** | 监工 | 保持 IO允许无守护进程重启 |
| **Runc** | 工人 | 真正干活创建容器干完就走 |
## 延伸阅读
### 延伸阅读
- [命名空间](./namespace.md)Runc 如何隔离容器
- [控制组](./cgroups.md)Runc 如何限制资源
- [联合文件系统](./ufs.md)镜像如何存储
- [命名空间](./13.2_namespace.md)Runc 如何隔离容器
- [控制组](./13.3_cgroups.md)Runc 如何限制资源
- [联合文件系统](./13.4_ufs.md)镜像如何存储

View File

@@ -1,6 +1,6 @@
# 命名空间
## 命名空间
## 什么是 Namespace
### 什么是 Namespace
> **Namespace Linux 内核提供的资源隔离机制它让容器内的进程仿佛运行在独立的操作系统中**
@@ -17,7 +17,7 @@ Namespace 是容器技术的核心基础之一。它回答了一个关键问题
```
## Namespace 的类型
### Namespace 的类型
Linux 内核提供了以下几种 NamespaceDocker 容器使用了全部
@@ -33,28 +33,28 @@ Linux 内核提供了以下几种 NamespaceDocker 容器使用了全部:
---
## PID Namespace
### PID Namespace
### 作用
#### 作用
隔离进程 ID让每个容器有自己的进程编号空间
### 效果
#### 效果
```bash
# 宿主机上查看进程
## 宿主机上查看进程
$ ps aux | grep nginx
root 12345 0.0 0.1 nginx: master process
root 12346 0.0 0.1 nginx: worker process
# 容器内查看进程
## 容器内查看进程
$ docker exec mycontainer ps aux
PID USER COMMAND
1 root nginx: master process 在容器内是 PID 1
2 root nginx: worker process
```
### 关键点
#### 关键点
- 容器内的 PID 1 进程特殊重要它是容器的主进程退出则容器停止
- 容器内无法看到宿主机或其他容器的进程
@@ -62,13 +62,13 @@ PID USER COMMAND
---
## NET Namespace
### NET Namespace
### 作用
#### 作用
隔离网络栈每个容器拥有独立的网络环境
### 效果
#### 效果
```
宿主机 容器
@@ -79,7 +79,7 @@ PID USER COMMAND
```
### 关键点
#### 关键点
- 每个容器有独立的网卡IP路由表iptables 规则
- 多个容器可以监听相同端口如都监听 80
@@ -87,13 +87,13 @@ PID USER COMMAND
---
## MNT Namespace
### MNT Namespace
### 作用
#### 作用
隔离文件系统挂载点每个容器有自己的根目录
### 效果
#### 效果
```
宿主机文件系统 容器内看到的
@@ -108,7 +108,7 @@ PID USER COMMAND
... ...
```
### chroot 的区别
#### chroot 的区别
| 特性 | chroot | MNT Namespace |
|------|--------|---------------|
@@ -118,20 +118,20 @@ PID USER COMMAND
---
## UTS Namespace
### UTS Namespace
### 作用
#### 作用
隔离主机名和域名让每个容器可以有自己的主机名
### 效果
#### 效果
```bash
# 宿主机
## 宿主机
$ hostname
my-server
# 容器内
## 容器内
$ docker run --hostname mycontainer ubuntu hostname
mycontainer
```
@@ -140,32 +140,32 @@ UTS = "UNIX Time-sharing System",是历史遗留的名称。
---
## IPC Namespace
### IPC Namespace
### 作用
#### 作用
隔离 System V IPC POSIX 消息队列
### 隔离的资源
#### 隔离的资源
- 信号量semaphores
- 消息队列message queues
- 共享内存shared memory
### 关键点
#### 关键点
- 同一容器内的进程可以通过 IPC 通信
- 不同容器的进程无法通过 IPC 通信除非显式共享
---
## USER Namespace
### USER Namespace
### 作用
#### 作用
隔离用户和组 ID实现权限隔离
### 效果
#### 效果
```
容器内 宿主机
@@ -175,7 +175,7 @@ UTS = "UNIX Time-sharing System",是历史遗留的名称。
```
### 安全意义
#### 安全意义
容器内的 root 用户可以映射为宿主机上的普通用户即使容器被突破攻击者在宿主机上也只有普通权限
@@ -183,50 +183,50 @@ UTS = "UNIX Time-sharing System",是历史遗留的名称。
---
## 动手实验体验 Namespace
### 动手实验体验 Namespace
使用 `unshare` 命令可以在不使用 Docker 的情况下体验 Namespace
### 实验 1UTS Namespace
#### 实验 1UTS Namespace
```bash
# 创建新的 UTS namespace 并启动 shell
## 创建新的 UTS namespace 并启动 shell
$ sudo unshare --uts /bin/bash
# 修改主机名只影响这个 namespace
## 修改主机名只影响这个 namespace
$ hostname container-test
$ hostname
container-test
# 退出后查看宿主机主机名未改变
## 退出后查看宿主机主机名未改变
$ exit
$ hostname
my-server
```
### 实验 2PID Namespace
#### 实验 2PID Namespace
```bash
# 创建新的 PID MNT namespace
## 创建新的 PID MNT namespace
$ sudo unshare --pid --mount --fork /bin/bash
# 挂载新的 /proc
## 挂载新的 /proc
$ mount -t proc proc /proc
# 查看进程只能看到当前 shell
## 查看进程只能看到当前 shell
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 8960 4516 pts/0 S 10:00 0:00 /bin/bash
root 8 0.0 0.0 10072 3200 pts/0 R+ 10:00 0:00 ps aux
```
### 实验 3NET Namespace
#### 实验 3NET Namespace
```bash
# 创建新的网络 namespace
## 创建新的网络 namespace
$ sudo unshare --net /bin/bash
# 查看网络接口只有 lo
## 查看网络接口只有 lo
$ ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
@@ -234,7 +234,7 @@ $ ip addr
---
## Namespace 的局限性
### Namespace 的局限性
Namespace 提供了隔离但不是安全边界
@@ -248,7 +248,7 @@ Namespace 提供了隔离但不是安全边界:
---
## 本章小结
### 本章小结
| Namespace | 隔离内容 | 一句话说明 |
|-----------|---------|-----------|
@@ -259,9 +259,9 @@ Namespace 提供了隔离但不是安全边界:
| IPC | 进程间通信 | 容器间 IPC 隔离 |
| USER | 用户 ID | 容器 root 宿主机 root |
## 延伸阅读
### 延伸阅读
- [控制组Cgroups](cgroups.md)资源限制机制
- [联合文件系统](ufs.md)分层存储的实现
- [控制组Cgroups](13.3_cgroups.md)资源限制机制
- [联合文件系统](13.4_ufs.md)分层存储的实现
- [安全](../security/README.md)容器安全实践
- [Linux Namespace 官方文档](https://man7.org/linux/man-pages/man7/namespaces.7.html)

View File

@@ -1,6 +1,6 @@
# 控制组
## 控制组
## 什么是控制组
### 什么是控制组
控制组Control Groups简称 cgroups Linux 内核的一个特性用于**限制记录和隔离**进程组的资源使用CPU内存磁盘 I/O网络等
@@ -21,7 +21,7 @@
---
## cgroups 的历史
### cgroups 的历史
| 时间 | 事件 |
|------|------|
@@ -32,7 +32,7 @@
---
## cgroups 可以限制的资源
### cgroups 可以限制的资源
| 资源类型 | 子系统 | 说明 |
|---------|--------|------|
@@ -44,18 +44,18 @@
---
## Docker 中的资源限制
### Docker 中的资源限制
### 内存限制
#### 内存限制
```bash
# 限制容器最多使用 512MB 内存
## 限制容器最多使用 512MB 内存
$ docker run -m 512m myapp
# 限制内存 + swap
## 限制内存 + swap
$ docker run -m 512m --memory-swap 1g myapp
# 软限制超过时警告不会 OOM Kill
## 软限制超过时警告不会 OOM Kill
$ docker run --memory-reservation 256m myapp
```
@@ -66,16 +66,16 @@ $ docker run --memory-reservation 256m myapp
| `--memory-reservation` | 软限制内存竞争时生效 |
| `--oom-kill-disable` | 禁用 OOM Killer谨慎使用 |
### CPU 限制
#### CPU 限制
```bash
# 限制使用 1.5 CPU 核心
## 限制使用 1.5 CPU 核心
$ docker run --cpus=1.5 myapp
# 限制使用 CPU 0 1
## 限制使用 CPU 0 1
$ docker run --cpuset-cpus="0,1" myapp
# 设置 CPU 使用权重相对值默认 1024
## 设置 CPU 使用权重相对值默认 1024
$ docker run --cpu-shares=512 myapp
```
@@ -86,77 +86,77 @@ $ docker run --cpu-shares=512 myapp
| `--cpu-shares` | CPU 时间片权重相对值 |
| `--cpu-period` / `--cpu-quota` | 精细控制 CPU 配额 |
### 磁盘 I/O 限制
#### 磁盘 I/O 限制
```bash
# 限制设备写入速度为 10MB/s
## 限制设备写入速度为 10MB/s
$ docker run --device-write-bps /dev/sda:10mb myapp
# 限制设备读取速度
## 限制设备读取速度
$ docker run --device-read-bps /dev/sda:10mb myapp
# 限制 IOPS
## 限制 IOPS
$ docker run --device-write-iops /dev/sda:100 myapp
```
### 进程数限制
#### 进程数限制
```bash
# 限制最多 100 个进程
## 限制最多 100 个进程
$ docker run --pids-limit=100 myapp
```
---
## 查看容器资源使用
### 查看容器资源使用
```bash
# 实时监控所有容器的资源使用
## 实时监控所有容器的资源使用
$ docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O
abc123 web 0.50% 45.5MiB / 512MiB 8.89% 1.2kB / 0B 0B / 0B
def456 db 2.30% 256MiB / 1GiB 25.00% 5.6kB / 3.2kB 4.1MB / 2.3MB
# 查看特定容器
## 查看特定容器
$ docker stats mycontainer
# 查看容器的 cgroup 配置
## 查看容器的 cgroup 配置
$ docker inspect mycontainer --format '{{json .HostConfig}}' | jq
```
---
## 资源限制的效果
### 资源限制的效果
### 内存超限
#### 内存超限
```bash
# 启动限制 100MB 内存的容器
## 启动限制 100MB 内存的容器
$ docker run -m 100m stress --vm 1 --vm-bytes 200M
# 容器会被 OOM Killer 杀死
## 容器会被 OOM Killer 杀死
$ docker ps -a
CONTAINER ID STATUS NAMES
abc123 Exited (137) 5 seconds ago hopeful_darwin
# 137 = 128 + 9表示被 SIGKILL (9) 杀死
## 137 = 128 + 9表示被 SIGKILL (9) 杀死
```
### CPU 限制验证
#### CPU 限制验证
```bash
# 不限制 CPU
## 不限制 CPU
$ docker run --rm stress --cpu 4
# 占满所有 CPU
## 占满所有 CPU
# 限制为 1 个核心
## 限制为 1 个核心
$ docker run --rm --cpus=1 stress --cpu 4
# 只能使用约 100% CPU1 个核心
## 只能使用约 100% CPU1 个核心
```
---
## cgroups v1 vs v2
### cgroups v1 vs v2
| 特性 | cgroups v1 | cgroups v2 |
|------|-----------|-----------|
@@ -166,15 +166,15 @@ $ docker run --rm --cpus=1 stress --cpu 4
| PSI压力监控 | | |
| rootless 容器 | 部分支持 | 完整支持 |
### 检查系统使用的版本
#### 检查系统使用的版本
```bash
# 查看 cgroup 版本
## 查看 cgroup 版本
$ mount | grep cgroup
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime)
# 如果显示 cgroup2 表示 v2
## 如果显示 cgroup2 表示 v2
# 或者
## 或者
$ cat /proc/filesystems | grep cgroup
nodev cgroup
nodev cgroup2
@@ -182,7 +182,7 @@ nodev cgroup2
---
## Compose 中设置限制
### Compose 中设置限制
```yaml
services:
@@ -200,25 +200,25 @@ services:
---
## 最佳实践
### 最佳实践
### 1. 始终设置内存限制
#### 1. 始终设置内存限制
```bash
# 防止 OOM 影响宿主机
## 防止 OOM 影响宿主机
$ docker run -m 1g myapp
```
### 2. 为关键应用设置 CPU 保证
#### 2. 为关键应用设置 CPU 保证
```bash
$ docker run --cpus=2 --cpu-shares=2048 critical-app
```
### 3. 监控资源使用
#### 3. 监控资源使用
```bash
# 配合 Prometheus + cAdvisor 监控
## 配合 Prometheus + cAdvisor 监控
$ docker run -d --name cadvisor \
-v /:/rootfs:ro \
-v /var/run:/var/run:ro \
@@ -229,7 +229,7 @@ $ docker run -d --name cadvisor \
---
## 本章小结
### 本章小结
| 资源 | 限制参数 | 示例 |
|------|---------|------|
@@ -239,8 +239,8 @@ $ docker run -d --name cadvisor \
| **磁盘 I/O** | `--device-write-bps` | `--device-write-bps /dev/sda:10mb` |
| **进程数** | `--pids-limit` | `--pids-limit=100` |
## 延伸阅读
### 延伸阅读
- [命名空间](namespace.md)资源隔离
- [命名空间](13.2_namespace.md)资源隔离
- [安全](../security/README.md)容器安全概述
- [Docker Stats](../05_container/README.md)监控容器资源

View File

@@ -1,6 +1,6 @@
# 联合文件系统
## 联合文件系统
## 什么是联合文件系统
### 什么是联合文件系统
联合文件系统UnionFS是一种**分层轻量级**的文件系统它将多个目录"联合"挂载到同一个虚拟目录形成一个统一的文件系统视图
@@ -29,9 +29,9 @@
---
## 为什么 Docker 使用联合文件系统
### 为什么 Docker 使用联合文件系统
### 1. 镜像分层复用
#### 1. 镜像分层复用
```
nginx:alpine myapp:latest
@@ -44,7 +44,7 @@ nginx:alpine myapp:latest
多个镜像共享相同的底层节省磁盘空间
### 2. 快速构建
#### 2. 快速构建
每个 Dockerfile 指令创建一层只有变化的层需要重建
@@ -57,7 +57,7 @@ COPY . . # 层4应用代码
代码变化时只需重建层4层1-3 使用缓存
### 3. 容器启动快
#### 3. 容器启动快
容器启动时不需要复制镜像只需
1. 在镜像层上创建一个薄的可写层
@@ -65,7 +65,7 @@ COPY . . # 层4应用代码
---
## Copy-on-Write写时复制
### Copy-on-Write写时复制
当容器修改只读层中的文件时
@@ -88,7 +88,7 @@ COPY . . # 层4应用代码
---
## Docker 支持的存储驱动
### Docker 支持的存储驱动
Docker 可使用多种联合文件系统实现
@@ -101,7 +101,7 @@ Docker 可使用多种联合文件系统实现:
| **devicemapper** | 块设备级存储 | 遗留系统 |
| **vfs** | 不使用 CoW每层完整复制 | 仅测试 |
### 各发行版推荐
#### 各发行版推荐
| Linux 发行版 | 推荐存储驱动 |
|-------------|-------------|
@@ -111,7 +111,7 @@ Docker 可使用多种联合文件系统实现:
| RHEL 8+ | overlay2 |
| Fedora | overlay2 |
### 查看当前存储驱动
#### 查看当前存储驱动
```bash
$ docker info | grep "Storage Driver"
@@ -120,7 +120,7 @@ Storage Driver: overlay2
---
## overlay2 工作原理
### overlay2 工作原理
overlay2 是目前最推荐的存储驱动
@@ -148,7 +148,7 @@ overlay2 是目前最推荐的存储驱动:
- **workdir**OverlayFS 的工作目录
- **merged**联合挂载后的视图
### 文件操作行为
#### 文件操作行为
| 操作 | 行为 |
|------|------|
@@ -159,10 +159,10 @@ overlay2 是目前最推荐的存储驱动:
---
## 查看镜像层
### 查看镜像层
```bash
# 查看镜像的层信息
## 查看镜像的层信息
$ docker history nginx:alpine
IMAGE CREATED CREATED BY SIZE
a6eb2a334a9f 2 weeks ago CMD ["nginx" "-g" "daemon off;"] 0B
@@ -172,7 +172,7 @@ a6eb2a334a9f 2 weeks ago CMD ["nginx" "-g" "daemon off;"] 0B
<missing> 2 weeks ago COPY 30-tune-worker-processes.sh /docker-ent 4.62kB
...
# 查看层的存储位置
## 查看层的存储位置
$ docker inspect nginx:alpine --format '{{json .GraphDriver.Data}}' | jq
{
"LowerDir": "/var/lib/docker/overlay2/.../diff:/var/lib/docker/overlay2/.../diff",
@@ -184,29 +184,29 @@ $ docker inspect nginx:alpine --format '{{json .GraphDriver.Data}}' | jq
---
## 最佳实践
### 最佳实践
### 1. 减少镜像层数
#### 1. 减少镜像层数
```docker
# 每条命令创建一层
## 每条命令创建一层
RUN apt-get update
RUN apt-get install -y nginx
RUN rm -rf /var/lib/apt/lists/*
# 合并为一层
## 合并为一层
RUN apt-get update && \
apt-get install -y nginx && \
rm -rf /var/lib/apt/lists/*
```
### 2. 避免在容器中写入大量数据
#### 2. 避免在容器中写入大量数据
容器层的写入性能低于直接写入大量数据应使用
- 数据卷Volume
- 绑定挂载Bind Mount
### 3. 使用 .dockerignore
#### 3. 使用 .dockerignore
排除不需要的文件可以
- 减小构建上下文
@@ -214,7 +214,7 @@ RUN apt-get update && \
---
## 本章小结
### 本章小结
| 概念 | 说明 |
|------|------|
@@ -223,8 +223,8 @@ RUN apt-get update && \
| **overlay2** | Docker 默认推荐的存储驱动 |
| **分层好处** | 镜像复用快速构建快速启动 |
## 延伸阅读
### 延伸阅读
- [镜像](../02_basic_concept/image.md)理解镜像分层
- [容器](../02_basic_concept/container.md)容器存储层
- [构建镜像](../04_image/build.md)Dockerfile 层的创建
- [镜像](../02_basic_concept/2.1_image.md)理解镜像分层
- [容器](../02_basic_concept/2.2_container.md)容器存储层
- [构建镜像](../04_image/4.5_build.md)Dockerfile 层的创建

View File

@@ -1,3 +1,3 @@
# 容器格式
## 容器格式
最初Docker 采用了 `LXC` 中的容器格式 0.7 版本以后开始去除 LXC转而使用自行开发的 [libcontainer](https://github.com/docker/libcontainer),从 1.11 开始,则进一步演进为使用 [runC](https://github.com/opencontainers/runc) 和 [containerd](https://github.com/containerd/containerd)。

View File

@@ -1,8 +1,8 @@
# Docker 网络实现
## Docker 网络实现
Docker 的网络实现其实就是利用了 Linux 上的网络命名空间和虚拟网络设备特别是 veth pair建议先熟悉了解这两部分的基本概念再阅读本章
## 基本原理
### 基本原理
首先要实现网络通信机器需要至少一个网络接口物理接口或虚拟接口来收发数据包此外如果不同子网之间要进行通信需要路由机制
Docker 中的网络接口默认都是虚拟的接口虚拟接口的优势之一是转发效率较高
@@ -10,7 +10,7 @@ Linux 通过在内核中进行数据复制来实现虚拟接口之间的数据
Docker 容器网络就利用了这项技术它在本地主机和容器内分别创建一个虚拟接口并让它们彼此连通这样的一对接口叫做 `veth pair`
## 创建网络参数
### 创建网络参数
Docker 创建一个容器的时候会执行如下操作
* 创建一对虚拟接口分别放到本地主机和新容器中
* 本地主机一端桥接到默认的 docker0 或指定网桥上并具有一个唯一的名字 veth65f9
@@ -25,7 +25,7 @@ Docker 创建一个容器的时候,会执行如下操作:
* `--net=container:NAME_or_ID` Docker 将新建容器的进程放到一个已存在容器的网络栈中新容器进程有自己的文件系统进程列表和资源限制但会和已存在的容器共享 IP 地址和端口等网络资源两者进程可以直接通过 `lo` 环回接口通信
* `--net=none` Docker 将新容器放到隔离的网络栈中但是不进行网络配置之后用户可以自己进行配置
## 网络配置细节
### 网络配置细节
用户使用 `--net=none` 可以自行配置网络让容器达到跟平常一样具有访问网络的权限通过这个过程可以了解 Docker 配置网络的细节
首先启动一个 `/bin/bash` 容器指定 `--net=none` 参数

View File

@@ -1,4 +1,4 @@
# 底层实现
# 第十三章 底层实现
Docker 底层的核心技术包括 Linux 上的命名空间Namespaces控制组Control groupsUnion 文件系统Union file systems和容器格式Container format