mirror of
https://github.com/yeasy/docker_practice.git
synced 2026-03-11 04:14:38 +00:00
style: apply global formatting fixes (struct, spacing, zhlint)
This commit is contained in:
@@ -27,31 +27,31 @@ graph LR
|
||||
|
||||
Docker 的内部架构如同洋葱一样分层,每一层专注解决特定问题:
|
||||
|
||||
#### 1. Docker CLI(客户端)
|
||||
#### 1。Docker CLI (客户端)
|
||||
|
||||
用户与 Docker 交互的主要方式。它将用户命令(如 `docker run`)转换为 API 请求发送给 dockerd。
|
||||
用户与 Docker 交互的主要方式。它将用户命令 (如 `docker run`) 转换为 API 请求发送给 dockerd。
|
||||
|
||||
#### 2. Dockerd(守护进程)
|
||||
#### 2。Dockerd (守护进程)
|
||||
|
||||
Docker 的大脑。
|
||||
- 监听 API 请求
|
||||
- 管理 Docker 对象(镜像、容器、网络、卷)
|
||||
- 管理 Docker 对象 (镜像、容器、网络、卷)
|
||||
- 编排下层组件完成工作
|
||||
|
||||
#### 3. Containerd(高级运行时)
|
||||
#### 3。Containerd (高级运行时)
|
||||
|
||||
行业标准的容器运行时(CNCF 毕业项目)。
|
||||
- 管理容器的完整生命周期(启动、停止)
|
||||
行业标准的容器运行时 (CNCF 毕业项目)。
|
||||
- 管理容器的完整生命周期 (启动、停止)
|
||||
- 镜像拉取与存储
|
||||
- **不包含** 复杂的与容器无关的功能(如构建、API)
|
||||
- Kubernetes 也可以直接使用 containerd(跳过 Docker)
|
||||
- **不包含**复杂的与容器无关的功能 (如构建、API)
|
||||
- Kubernetes 也可以直接使用 containerd (跳过 Docker)
|
||||
|
||||
#### 4. Runc(低级运行时)
|
||||
#### 4。Runc (低级运行时)
|
||||
|
||||
用于创建和运行容器的 CLI 工具。
|
||||
- 直接与内核交互(Namespaces, Cgroups)
|
||||
- 直接与内核交互 (Namespaces,Cgroups)
|
||||
- 遵循 OCI (Open Container Initiative) 规范
|
||||
- **主要职责**:根据配置启动一个容器,然后退出(将控制权交给容器进程)
|
||||
- **主要职责**:根据配置启动一个容器,然后退出 (将控制权交给容器进程)
|
||||
|
||||
#### 5. Shim
|
||||
|
||||
@@ -95,9 +95,9 @@ flowchart TD
|
||||
Shim -.-> |8. Monitor IO/Exit| Container
|
||||
```
|
||||
|
||||
1. **CLI**发送请求给**Dockerd**2.**Dockerd**解析请求,调用**Containerd**3.**Containerd** 准备镜像,转换为 OCI Bundle
|
||||
4. **Containerd**创建**Shim** 进程
|
||||
5. **Shim**调用**Runc**6.**Runc** 与系统内核交互,创建 Namespaces 和 Cgroups
|
||||
1. **CLI** 发送请求给 **Dockerd**2。**Dockerd** 解析请求,调用 **Containerd**3。**Containerd** 准备镜像,转换为 OCI Bundle
|
||||
4. **Containerd** 创建 **Shim** 进程
|
||||
5. **Shim** 调用 **Runc**6。**Runc** 与系统内核交互,创建 Namespaces 和 Cgroups
|
||||
7. **Runc** 启动 nginx 进程后退出
|
||||
8. **Shim** 接管容器 IO 和生命周期监控
|
||||
|
||||
@@ -108,7 +108,7 @@ flowchart TD
|
||||
从 Docker Engine v29 (2025/2026) 开始,架构进一步简化和标准化:
|
||||
|
||||
- **Containerd 镜像存储 (Image Store)**:默认启用。Docker 直接使用 Containerd 的镜像管理能力,不再维护自己的一套 graphdriver。
|
||||
- **优势**:多平台镜像支持更好、镜像拉取更快(lazy pulling)、与 K8s 共享镜像。
|
||||
- **优势**:多平台镜像支持更好、镜像拉取更快 (lazy pulling)、与 K8s 共享镜像。
|
||||
|
||||
---
|
||||
|
||||
@@ -127,8 +127,8 @@ flowchart TD
|
||||
end
|
||||
```
|
||||
|
||||
- 使用轻量级虚拟机(Apple Virtualization / WSL 2)运行 Linux 内核
|
||||
- 文件挂载(Bind Mount)需要跨越 VM 边界(这也是文件 I/O 慢的原因)
|
||||
- 使用轻量级虚拟机 (Apple Virtualization / WSL 2) 运行 Linux 内核
|
||||
- 文件挂载 (Bind Mount) 需要跨越 VM 边界 (这也是文件 I/O 慢的原因)
|
||||
- 网络端口需要从宿主机转发到 VM
|
||||
|
||||
---
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
命名空间(Namespace)是 Linux 内核的一个强大特性,为容器提供了隔离的运行环境。
|
||||
命名空间 (Namespace) 是 Linux 内核的一个强大特性,为容器提供了隔离的运行环境。
|
||||
|
||||
### 什么是 Namespace
|
||||
## 什么是 Namespace
|
||||
|
||||
> **Namespace 是 Linux 内核提供的资源隔离机制,它让容器内的进程仿佛运行在独立的操作系统中。**Namespace 是容器技术的核心基础之一。它回答了一个关键问题:**如何让一个进程"以为"自己独占整个系统?**
|
||||
> **Namespace 是 Linux 内核提供的资源隔离机制,它让容器内的进程仿佛运行在独立的操作系统中。**Namespace 是容器技术的核心基础之一。它回答了一个关键问题:**如何让一个进程 “以为” 自己独占整个系统?**
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
@@ -71,7 +71,7 @@ PID USER COMMAND
|
||||
|
||||
- 容器内的 PID 1 进程特殊重要——它是容器的主进程,退出则容器停止
|
||||
- 容器内无法看到宿主机或其他容器的进程
|
||||
- 宿主机可以看到所有容器内的进程(但 PID 不同)
|
||||
- 宿主机可以看到所有容器内的进程 (但 PID 不同)
|
||||
|
||||
---
|
||||
|
||||
@@ -85,6 +85,8 @@ NET Namespace 负责网络栈的隔离,包括网卡、路由表和 iptables
|
||||
|
||||
#### NET 隔离效果
|
||||
|
||||
如下代码块所示,展示了相关示例:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Host ["宿主机"]
|
||||
@@ -105,7 +107,7 @@ flowchart LR
|
||||
#### NET 关键点
|
||||
|
||||
- 每个容器有独立的网卡、IP、路由表、iptables 规则
|
||||
- 多个容器可以监听相同端口(如都监听 80)
|
||||
- 多个容器可以监听相同端口 (如都监听 80)
|
||||
- Docker 使用 veth pair 连接容器网络和宿主机网桥
|
||||
|
||||
---
|
||||
@@ -120,6 +122,8 @@ MNT Namespace 负责文件系统挂载点的隔离,确保容器看到独立的
|
||||
|
||||
#### MNT 隔离效果
|
||||
|
||||
如下代码块所示,展示了相关示例:
|
||||
|
||||
```
|
||||
宿主机文件系统: 容器内看到的:
|
||||
/ / ← 容器的根目录
|
||||
@@ -167,7 +171,7 @@ $ docker run --hostname mycontainer ubuntu hostname
|
||||
mycontainer
|
||||
```
|
||||
|
||||
UTS = "UNIX Time-sharing System",是历史遗留的名称。
|
||||
UTS = “UNIX Time-sharing System”,是历史遗留的名称。
|
||||
|
||||
---
|
||||
|
||||
@@ -181,14 +185,14 @@ IPC Namespace 用于隔离进程间通信资源,如 System V IPC 和 POSIX 消
|
||||
|
||||
#### 隔离的资源
|
||||
|
||||
- 信号量(semaphores)
|
||||
- 消息队列(message queues)
|
||||
- 共享内存(shared memory)
|
||||
- 信号量 (semaphores)
|
||||
- 消息队列 (message queues)
|
||||
- 共享内存 (shared memory)
|
||||
|
||||
#### IPC 关键点
|
||||
|
||||
- 同一容器内的进程可以通过 IPC 通信
|
||||
- 不同容器的进程无法通过 IPC 通信(除非显式共享)
|
||||
- 不同容器的进程无法通过 IPC 通信 (除非显式共享)
|
||||
|
||||
---
|
||||
|
||||
@@ -202,6 +206,8 @@ USER Namespace 允许将容器内的用户 ID 映射到宿主机的不同用户
|
||||
|
||||
#### USER 隔离效果
|
||||
|
||||
如下代码块所示,展示了相关示例:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Container ["容器内"]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
## 14.3 控制组
|
||||
|
||||
控制组(Cgroups)是 Linux 内核提供的另一种关键机制,主要用于资源的限制和审计。
|
||||
控制组 (Cgroups) 是 Linux 内核提供的另一种关键机制,主要用于资源的限制和审计。
|
||||
|
||||
### 什么是控制组
|
||||
|
||||
控制组(Control Groups,简称 cgroups)是 Linux 内核的一个特性,用于**限制、记录和隔离**进程组的资源使用(CPU、内存、磁盘 I/O、网络等)。
|
||||
控制组 (Control Groups,简称 cgroups) 是 Linux 内核的一个特性,用于**限制、记录和隔离**进程组的资源使用 (CPU、内存、磁盘 I/O、网络等)。
|
||||
|
||||
> **核心作用**:让多个容器公平共享宿主机资源,防止单个容器耗尽系统资源。
|
||||
|
||||
@@ -163,6 +163,8 @@ $ docker inspect mycontainer --format '{{json .HostConfig}}' | jq
|
||||
|
||||
### 资源限制的效果
|
||||
|
||||
本节涵盖了相关内容与详细描述,主要探讨以下几个方面:
|
||||
|
||||
#### 内存超限
|
||||
|
||||
运行以下命令:
|
||||
@@ -213,6 +215,10 @@ $ docker run --rm --cpus=1 stress --cpu 4
|
||||
| PSI(压力监控) | ❌ | ✅ |
|
||||
| rootless 容器 | 部分支持 | 完整支持 |
|
||||
|
||||
#### 概述
|
||||
|
||||
总体概述了以下内容。
|
||||
|
||||
#### 检查系统使用的版本
|
||||
|
||||
运行以下命令:
|
||||
@@ -235,7 +241,7 @@ nodev cgroup2
|
||||
|
||||
### 在 Compose 中设置限制
|
||||
|
||||
在 Compose 中设置限制 配置如下:
|
||||
在 Compose 中设置限制配置如下:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
@@ -257,7 +263,7 @@ services:
|
||||
|
||||
在使用 Cgroups 限制资源时,遵循一些最佳实践可以避免潜在的问题。
|
||||
|
||||
#### 1. 始终设置内存限制
|
||||
#### 1。始终设置内存限制
|
||||
|
||||
运行以下命令:
|
||||
|
||||
@@ -267,7 +273,7 @@ services:
|
||||
$ docker run -m 1g myapp
|
||||
```
|
||||
|
||||
#### 2. 为关键应用设置 CPU 保证
|
||||
#### 2。为关键应用设置 CPU 保证
|
||||
|
||||
运行以下命令:
|
||||
|
||||
@@ -275,7 +281,7 @@ $ docker run -m 1g myapp
|
||||
$ docker run --cpus=2 --cpu-shares=2048 critical-app
|
||||
```
|
||||
|
||||
#### 3. 监控资源使用
|
||||
#### 3。监控资源使用
|
||||
|
||||
运行以下命令:
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
## 14.4 联合文件系统
|
||||
|
||||
联合文件系统(UnionFS)是 Docker 镜像分层存储的基础,它允许将多个目录挂载为同一个虚拟文件系统。
|
||||
联合文件系统 (UnionFS) 是 Docker 镜像分层存储的基础,它允许将多个目录挂载为同一个虚拟文件系统。
|
||||
|
||||
### 什么是联合文件系统
|
||||
|
||||
联合文件系统(UnionFS)是一种**分层、轻量级**的文件系统,它将多个目录"联合"挂载到同一个虚拟目录,形成一个统一的文件系统视图。
|
||||
联合文件系统 (UnionFS) 是一种**分层、轻量级**的文件系统,它将多个目录 “联合” 挂载到同一个虚拟目录,形成一个统一的文件系统视图。
|
||||
|
||||
> **核心思想**:将多个只读层叠加,最上层可写,形成完整的文件系统。
|
||||
|
||||
@@ -28,7 +28,9 @@ flowchart TD
|
||||
|
||||
Docker 选择联合文件系统作为其存储驱动,主要基于以下几个核心优势。
|
||||
|
||||
#### 1. 镜像分层复用
|
||||
#### 1。镜像分层复用
|
||||
|
||||
如下代码块所示,展示了相关示例:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
@@ -38,7 +40,7 @@ flowchart TD
|
||||
|
||||
多个镜像共享相同的底层,节省磁盘空间。
|
||||
|
||||
#### 2. 快速构建
|
||||
#### 2。快速构建
|
||||
|
||||
每个 Dockerfile 指令创建一层,只有变化的层需要重建:
|
||||
|
||||
@@ -49,9 +51,9 @@ RUN npm install # 层3:安装依赖
|
||||
COPY . . # 层4:应用代码
|
||||
```
|
||||
|
||||
代码变化时,只需重建层4,层1-3 使用缓存。
|
||||
代码变化时,只需重建层 4,层 1-3 使用缓存。
|
||||
|
||||
#### 3. 容器启动快
|
||||
#### 3。容器启动快
|
||||
|
||||
容器启动时不需要复制镜像,只需:
|
||||
1. 在镜像层上创建一个薄的可写层
|
||||
@@ -59,7 +61,7 @@ COPY . . # 层4:应用代码
|
||||
|
||||
---
|
||||
|
||||
### Copy-on-Write(写时复制)
|
||||
### Copy-on-Write (写时复制)
|
||||
|
||||
当容器修改只读层中的文件时:
|
||||
|
||||
@@ -141,11 +143,15 @@ flowchart TD
|
||||
OverlayFS --> Lower1
|
||||
```
|
||||
|
||||
- **lowerdir**:只读的镜像层(可以有多个)
|
||||
- **lowerdir**:只读的镜像层 (可以有多个)
|
||||
- **upperdir**:可写的容器层
|
||||
- **workdir**:OverlayFS 的工作目录
|
||||
- **merged**:联合挂载后的视图
|
||||
|
||||
#### 概述
|
||||
|
||||
总体概述了以下内容。
|
||||
|
||||
#### 文件操作行为
|
||||
|
||||
| 操作 | 行为 |
|
||||
@@ -190,7 +196,9 @@ $ docker inspect nginx:alpine --format '{{json .GraphDriver.Data}}' | jq
|
||||
|
||||
为了构建高效、轻量的镜像,我们在使用联合文件系统时应注意以下几点。
|
||||
|
||||
#### 1. 减少镜像层数
|
||||
#### 1。减少镜像层数
|
||||
|
||||
如下代码块所示,展示了相关示例:
|
||||
|
||||
```docker
|
||||
## ❌ 每条命令创建一层
|
||||
@@ -206,13 +214,13 @@ RUN apt-get update && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
#### 2. 避免在容器中写入大量数据
|
||||
#### 2。避免在容器中写入大量数据
|
||||
|
||||
容器层的写入性能低于直接写入。大量数据应使用:
|
||||
- 数据卷(Volume)
|
||||
- 绑定挂载(Bind Mount)
|
||||
- 数据卷 (Volume)
|
||||
- 绑定挂载 (Bind Mount)
|
||||
|
||||
#### 3. 使用 .dockerignore
|
||||
#### 3。使用。dockerignore
|
||||
|
||||
排除不需要的文件可以:
|
||||
- 减小构建上下文
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
## 14.6 Docker 网络实现
|
||||
|
||||
Docker 的网络实现其实就是利用了 Linux 上的网络命名空间和虚拟网络设备(特别是 veth pair)。建议先熟悉了解这两部分的基本概念再阅读本章。
|
||||
Docker 的网络实现其实就是利用了 Linux 上的网络命名空间和虚拟网络设备 (特别是 veth pair)。建议先熟悉了解这两部分的基本概念再阅读本章。
|
||||
|
||||
### 基本原理
|
||||
|
||||
首先,要实现网络通信,机器需要至少一个网络接口(物理接口或虚拟接口)来收发数据包;此外,如果不同子网之间要进行通信,需要路由机制。
|
||||
首先,要实现网络通信,机器需要至少一个网络接口 (物理接口或虚拟接口) 来收发数据包;此外,如果不同子网之间要进行通信,需要路由机制。
|
||||
|
||||
Docker 中的网络接口默认都是虚拟的接口。虚拟接口的优势之一是转发效率较高。
|
||||
Linux 通过在内核中进行数据复制来实现虚拟接口之间的数据转发,发送接口的发送缓存中的数据包被直接复制到接收接口的接收缓存中。对于本地系统和容器内系统看来就像是一个正常的以太网卡,只是它不需要真正同外部网络设备通信,速度要快很多。
|
||||
|
||||
Docker 容器网络就利用了这项技术。它在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通(这样的一对接口叫做 `veth pair`)。
|
||||
Docker 容器网络就利用了这项技术。它在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通 (这样的一对接口叫做 `veth pair`)。
|
||||
|
||||
### 创建网络参数
|
||||
|
||||
@@ -21,7 +21,7 @@ Docker 创建一个容器的时候,会执行如下操作:
|
||||
|
||||
完成这些之后,容器就可以使用 eth0 虚拟网卡来连接其他容器和其他网络。
|
||||
|
||||
可以在 `docker run` 的时候通过 `--net` 参数来指定容器的网络配置,有4个可选值:
|
||||
可以在 `docker run` 的时候通过 `--net` 参数来指定容器的网络配置,有 4 个可选值:
|
||||
* `--net=bridge` 这个是默认值,连接到默认的网桥。
|
||||
* `--net=host` 告诉 Docker 不要将容器网络放到隔离的命名空间中,即不要容器化容器内的网络。此时容器使用本地主机的网络,它拥有完全的本地主机接口访问权限。容器进程可以跟主机其它 root 进程一样可以打开低范围的端口,可以访问本地网络服务比如 D-bus,还可以让容器做一些影响整个主机系统的事情,比如重启主机。因此使用这个选项的时候要非常小心。如果进一步的使用 `--privileged=true`,容器会被允许直接配置主机的网络堆栈。
|
||||
* `--net=container:NAME_or_ID` 让 Docker 将新建容器的进程放到一个已存在容器的网络栈中,新容器进程有自己的文件系统、进程列表和资源限制,但会和已存在的容器共享 IP 地址和端口等网络资源,两者进程可以直接通过 `lo` 环回接口通信。
|
||||
@@ -57,7 +57,7 @@ $ sudo ip link add A type veth peer name B
|
||||
$ sudo brctl addif docker0 A
|
||||
$ sudo ip link set A up
|
||||
```
|
||||
将B放到容器的网络命名空间,命名为 eth0,启动它并配置一个可用 IP(桥接网段)和默认网关。
|
||||
将 B 放到容器的网络命名空间,命名为 eth0,启动它并配置一个可用 IP (桥接网段) 和默认网关。
|
||||
```bash
|
||||
$ sudo ip link set B netns $pid
|
||||
$ sudo ip netns exec $pid ip link set dev B name eth0
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# 第十四章 底层实现
|
||||
# 第十四章底层实现
|
||||
|
||||
Docker 底层的核心技术包括 Linux 上的命名空间(Namespaces)、控制组(Control groups)、Union 文件系统(Union file systems)和容器格式(Container format)。
|
||||
Docker 底层的核心技术包括 Linux 上的命名空间 (Namespaces)、控制组 (Control groups)、Union 文件系统 (Union file systems) 和容器格式 (Container format)。
|
||||
|
||||
我们知道,传统的虚拟机通过在宿主主机中运行 hypervisor 来模拟一整套完整的硬件环境提供给虚拟机的操作系统。虚拟机系统看到的环境是可限制的,也是彼此隔离的。
|
||||
这种直接的做法实现了对资源最完整的封装,但很多时候往往意味着系统资源的浪费。
|
||||
例如,以宿主机和虚拟机系统都为 Linux 系统为例,虚拟机中运行的应用其实可以利用宿主机系统中的运行环境。
|
||||
|
||||
我们知道,在操作系统中,包括内核、文件系统、网络、PID、UID、IPC、内存、硬盘、CPU 等等,所有的资源都是应用进程直接共享的。
|
||||
要想实现虚拟化,除了要实现对内存、CPU、网络IO、硬盘IO、存储空间等的限制外,还要实现文件系统、网络、PID、UID、IPC等等的相互隔离。
|
||||
要想实现虚拟化,除了要实现对内存、CPU、网络 IO、硬盘 IO、存储空间等的限制外,还要实现文件系统、网络、PID、UID、IPC 等等的相互隔离。
|
||||
前者相对容易实现一些,后者则需要宿主机系统的深入支持。
|
||||
|
||||
随着 Linux 系统对于命名空间功能的完善实现,程序员已经可以实现上面的所有需求,让某些进程在彼此隔离的命名空间中运行。大家虽然都共用一个内核和某些运行时环境(例如一些系统命令和系统库),但是彼此却看不到,都以为系统中只有自己的存在。这种机制就是容器(Container),利用命名空间来做权限的隔离控制,利用 cgroups 来做资源分配。
|
||||
随着 Linux 系统对于命名空间功能的完善实现,程序员已经可以实现上面的所有需求,让某些进程在彼此隔离的命名空间中运行。大家虽然都共用一个内核和某些运行时环境 (例如一些系统命令和系统库),但是彼此却看不到,都以为系统中只有自己的存在。这种机制就是容器 (Container),利用命名空间来做权限的隔离控制,利用 cgroups 来做资源分配。
|
||||
|
||||
* [基本架构](14.1_arch.md)
|
||||
* [命名空间](14.2_namespace.md)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
### 延伸阅读
|
||||
|
||||
- [控制组(Cgroups)](14.3_cgroups.md):资源限制机制
|
||||
- [控制组 (Cgroups)](14.3_cgroups.md):资源限制机制
|
||||
- [联合文件系统](14.4_ufs.md):分层存储的实现
|
||||
- [安全](../11_ops/security/README.md):容器安全实践
|
||||
- [Linux Namespace 官方文档](https://man7.org/linux/man-pages/man7/namespaces.7.html)
|
||||
|
||||
Reference in New Issue
Block a user