强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

03 - 架构原理

03 - 架构原理

深入理解 Docker 的分层架构:daemon、containerd、runc,以及存储驱动与 cgroup/namespace。


3.1 Docker 整体架构

Docker 采用经典的 客户端-服务器(Client-Server) 架构:

┌─────────────────────────────────────────────────────────┐
│                       Docker Client                      │
│   docker CLI  │  Docker Compose  │  Docker SDK  │  API  │
└───────┬────────────────┬────────────────┬───────────────┘
        │  REST API       │                │
        ▼                 ▼                ▼
┌─────────────────────────────────────────────────────────┐
│                    Docker Engine (dockerd)                │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌─────────┐ │
│  │ 镜像管理  │  │ 容器管理  │  │ 网络管理  │  │ 卷管理  │ │
│  └──────────┘  └──────────┘  └──────────┘  └─────────┘ │
│  ┌─────────────────────────────────────────────────────┐ │
│  │              Docker API Server                       │ │
│  └─────────────────────────────────────────────────────┘ │
└───────┬─────────────────────────────────────────────────┘
        │  gRPC
┌─────────────────────────────────────────────────────────┐
│                     containerd                           │
│  ┌──────────┐  ┌──────────┐  ┌──────────────────────┐  │
│  │ 镜像服务  │  │ 内容存储  │  │  容器运行时管理       │  │
│  └──────────┘  └──────────┘  └──────────────────────┘  │
└───────┬─────────────────────────────────────────────────┘
        │  OCI Runtime Spec
┌─────────────────────────────────────────────────────────┐
│                        runc                              │
│           (OCI 标准容器运行时)                            │
│  ┌────────────────────────────────────────────────────┐ │
│  │  Linux Kernel: namespaces + cgroups + seccomp      │ │
│  └────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘

组件职责表

组件角色说明
docker CLI客户端用户交互界面,发送 REST API 请求
dockerdDocker 守护进程镜像管理、构建、网络、卷、API 服务
containerd高级容器运行时容器生命周期管理、镜像分发
containerd-shim容器垫片进程每个容器一个,使容器独立于 containerd
runcOCI 运行时实际创建和运行容器
Linux Kernel内核namespaces、cgroups、seccomp 等

3.2 Docker daemon (dockerd)

职责

dockerd 职责:
  ├── API 服务: 暴露 REST API (默认 unix:///var/run/docker.sock)
  ├── 镜像管理: 构建、拉取、推送镜像
  ├── 容器管理: 创建、启动、停止容器(委托给 containerd)
  ├── 网络管理: 创建和管理 Docker 网络
  ├── 卷管理: 创建和管理数据卷
  ├── 日志管理: 容器日志收集和驱动
  └── 事件系统: 发布容器、镜像等事件

配置文件

# Docker daemon 配置文件
cat /etc/docker/daemon.json
{
  "storage-driver": "overlay2",
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "exec-opts": ["native.cgroupdriver=systemd"],
  "registry-mirrors": ["https://docker.1ms.run"],
  "default-address-pools": [
    { "base": "172.17.0.0/12", "size": 24 }
  ],
  "live-restore": true,
  "max-concurrent-downloads": 10,
  "max-concurrent-uploads": 5
}

常用 daemon 参数

参数默认值说明
storage-driveroverlay2存储驱动
log-driverjson-file默认日志驱动
exec-opts[]执行选项,如 cgroup 驱动
live-restorefalsedaemon 重启时保持容器运行
max-concurrent-downloads3并发下载层数
default-runtimerunc默认 OCI 运行时
iptablestrue是否管理 iptables 规则
ipv6false是否启用 IPv6

3.3 containerd

containerd 是一个行业标准的高级容器运行时,由 Docker 捐赠给 CNCF,现为毕业项目。

containerd 架构

┌──────────────────────────────────────────┐
│                  containerd               │
│                                          │
│  ┌──────────┐  ┌───────────────────────┐ │
│  │ Content   │  │   Snapshot Service    │ │
│  │ Store     │  │   (快照管理)          │ │
│  │ (内容存储)│  │                       │ │
│  └──────────┘  └───────────────────────┘ │
│                                          │
│  ┌──────────┐  ┌───────────────────────┐ │
│  │ Image     │  │   Container Service   │ │
│  │ Service   │  │   (容器生命周期)      │ │
│  │ (镜像服务)│  │                       │ │
│  └──────────┘  └───────────────────────┘ │
│                                          │
│  ┌──────────┐  ┌───────────────────────┐ │
│  │ Namespace │  │   Task Service        │ │
│  │ Service   │  │   (进程管理)          │ │
│  │ (命名空间)│  │                       │ │
│  └──────────┘  └───────────────────────┘ │
│                                          │
│  ┌──────────────────────────────────────┐ │
│  │         Runtime (runc/crun/kata)     │ │
│  └──────────────────────────────────────┘ │
└──────────────────────────────────────────┘

直接使用 containerd

# 使用 ctr 工具(containerd 的 CLI)
sudo ctr images pull docker.io/library/nginx:alpine

sudo ctr containers create docker.io/library/nginx:alpine my-nginx

sudo ctr tasks start my-nginx

sudo ctr tasks ls

使用 nerdctl(containerd 的现代 CLI)

# 安装 nerdctl(兼容 Docker CLI 语法)
# https://github.com/containerd/nerdctl
nerdctl run -d --name my-nginx -p 8080:80 nginx:alpine
nerdctl ps
nerdctl images

3.4 containerd-shim

每个容器运行时都有一个 shim 进程,它的作用是:

containerd ──启动──> containerd-shim ──启动──> runc ──创建──> 容器进程
                           │                          │
                           │                      runc 退出
                           └─── 保持运行,管理容器进程
功能说明
解耦容器与 containerdcontainerd 重启不影响运行中的容器
保持 stdin 开启容器的输入输出流不中断
退出状态报告将容器退出码报告给 containerd
资源监控收集容器资源使用数据
# 查看 shim 进程
ps aux | grep containerd-shim
# containerd-shim-runc-v2 -namespace moby -id <container_id> ...

3.5 runc — OCI 标准运行时

runc 是最底层的容器运行时,直接调用 Linux 内核功能创建容器。

runc 的工作原理

runc 创建容器的过程:

1. 创建容器目录结构
2. 设置 rootfs(根文件系统)
3. 挂载 /proc, /sys, /dev 等
4. 配置 Linux namespaces:
   - PID namespace: 进程隔离
   - Network namespace: 网络隔离
   - Mount namespace: 文件系统隔离
   - UTS namespace: 主机名隔离
   - IPC namespace: 进程间通信隔离
   - User namespace: 用户隔离
5. 配置 cgroups:
   - CPU 限制
   - 内存限制
   - I/O 限制
6. 设置 seccomp / AppArmor / SELinux 安全策略
7. 切换到容器的 rootfs
8. 执行用户指定的进程

直接使用 runc

# 创建 OCI bundle
mkdir -p /mycontainer/rootfs

# 导出 Ubuntu 镜像的文件系统
docker export $(docker create ubuntu:22.04) | tar -C /mycontainer/rootfs -xf -

# 生成默认配置
cd /mycontainer
runc spec

# 运行容器
sudo runc run mycontainer

# 清理
sudo runc delete mycontainer
rm -rf /mycontainer

其他 OCI 运行时

运行时特点适用场景
runc参考实现,最广泛通用场景
crunC 语言实现,更快性能敏感场景
kata-runtime轻量 VM 级隔离强安全隔离
gVisor (runsc)用户态内核安全沙箱

3.6 Linux Namespaces(命名空间)

Namespace 是 Linux 内核实现容器隔离的核心机制。

六种 Namespace

Namespace隔离内容标志说明
PID进程 IDCLONE_NEWPID容器内 PID 从 1 开始
Network网络栈CLONE_NEWNET独立的网卡、IP、端口
Mount文件系统挂载CLONE_NEWNS独立的挂载点
UTS主机名CLONE_NEWUTS独立的 hostname
IPC进程间通信CLONE_NEWIPC独立的信号量、消息队列
User用户/组 IDCLONE_NEWUSERUID 映射

验证 Namespace

# 查看容器的 namespace
PID=$(docker inspect --format '{{.State.Pid}}' my-nginx)
ls -la /proc/$PID/ns/

# 输出:
# lrwxrwxrwx 1 root root 0 ... cgroup -> 'cgroup:[4026532583]'
# lrwxrwxrwx 1 root root 0 ... ipc -> 'ipc:[4026532582]'
# lrwxrwxrwx 1 root root 0 ... mnt -> 'mnt:[4026532580]'
# lrwxrwxrwx 1 root root 0 ... net -> 'net:[4026532584]'
# lrwxrwxrwx 1 root root 0 ... pid -> 'pid:[4026532581]'
# lrwxrwxrwx 1 root root 0 ... uts -> 'uts:[4026532579]'

Namespace 操作示例

# 使用 unshare 创建新的 namespace
sudo unshare --pid --mount --net --uts --ipc --fork bash

# 在新 namespace 中
hostname my-container
echo "当前 hostname: $(hostname)"
ps aux  # 只能看到当前 namespace 中的进程

3.7 Cgroups(控制组)

Cgroups 用于限制、控制和统计进程组的资源使用。

资源控制类型

控制器功能示例
cpuCPU 时间分配--cpus=1.5
memory内存使用限制--memory=512m
blkio块设备 I/O--blkio-weight=500
pids进程数限制--pids-limit=100
cpusetCPU 核心绑定--cpuset-cpus=0,1
devices设备访问控制--device-read-bps

容器资源限制示例

# CPU 限制
docker run -d --name cpu-test --cpus=1.5 nginx:alpine

# 内存限制
docker run -d --name mem-test --memory=256m --memory-swap=512m nginx:alpine

# 进程数限制
docker run -d --name pid-test --pids-limit=50 nginx:alpine

# 查看容器的 cgroup 信息
docker stats cpu-test mem-test pid-test --no-stream

cgroup v1 vs v2

特性cgroup v1cgroup v2
层级多层级(每个控制器独立)统一层级
挂载点/sys/fs/cgroup/<controller>//sys/fs/cgroup/
资源压力通知不支持支持 PSI
线程级控制不支持支持
安全性较弱更强
Docker 默认旧版本默认Docker 20.10+ 默认
# 检查当前 cgroup 版本
stat -fc %T /sys/fs/cgroup/
# cgroup2fs = v2, tmpfs = v1

# 查看容器的 cgroup 路径
docker inspect --format '{{.HostConfig.CgroupParent}}' <container>

3.8 存储驱动(Storage Driver)

Docker 使用分层文件系统来存储镜像和容器数据。

分层原理

镜像层 (只读):
  ┌────────────────────────────────┐
  │ Layer 3: COPY app.py /app/     │  ← 应用代码 (10KB)
  ├────────────────────────────────┤
  │ Layer 2: RUN pip install flask │  ← 依赖安装 (50MB)
  ├────────────────────────────────┤
  │ Layer 1: ubuntu:22.04 base     │  ← 基础镜像 (77MB)
  └────────────────────────────────┘

容器层 (可写):
  ┌────────────────────────────────┐
  │ Writable Layer                │  ← 容器运行时写入
  │ (修改的文件、日志、临时文件)     │
  ├────────────────────────────────┤
  │ 镜像层 (只读,通过 CoW 复制)    │
  └────────────────────────────────┘

Copy-on-Write (CoW) 机制

读取文件:
  容器层 → 未找到 → 向下查找镜像层 → 找到并读取
  容器层 → 找到 → 直接读取

写入文件:
  首次写入 → 从镜像层复制到容器层 (Copy-on-Write) → 在容器层修改
  后续写入 → 直接在容器层修改

存储驱动对比

驱动性能稳定性适用场景说明
overlay2⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐推荐默认Linux 4.0+ 内核
fuse-overlayfs⭐⭐⭐⭐⭐⭐⭐Rootless 模式需要 FUSE 支持
btrfs⭐⭐⭐⭐⭐⭐⭐⭐Btrfs 文件系统原生快照支持
zfs⭐⭐⭐⭐⭐⭐⭐⭐ZFS 文件系统适合大容量存储
vfs⭐⭐⭐⭐⭐测试/不支持 CoW无 CoW,全量复制
# 查看当前存储驱动
docker info | grep "Storage Driver"

# 查看镜像层信息
docker inspect nginx:alpine | jq '.[0].RootFS.Layers'

# 查看存储占用
docker system df -v

3.9 容器创建的完整流程

用户执行: docker run -d --name web -p 8080:80 nginx:alpine

完整流程:
  1. Docker CLI → dockerd (REST API)
  2. dockerd 解析参数,检查镜像是否存在
  3. 如果镜像不存在 → 从 Registry 拉取
  4. dockerd 调用 containerd 创建容器
  5. containerd 创建容器配置
  6. containerd 启动 containerd-shim
  7. containerd-shim 调用 runc
  8. runc 创建 namespaces、cgroups
  9. runc 配置 rootfs、挂载点
  10. runc 执行容器进程 (nginx)
  11. runc 退出,containerd-shim 接管
  12. 容器进程独立运行
  13. dockerd 配置网络(bridge + port mapping)
  14. 返回容器 ID 给 CLI
# 使用 nsenter 进入容器的 namespace 查看
CONTAINER_PID=$(docker inspect --format '{{.State.Pid}}' web)
sudo nsenter -t $CONTAINER_PID -p -n -m ps aux

3.10 Docker 数据目录结构

/var/lib/docker/
  ├── overlay2/          # 镜像和容器的文件系统层
  │   ├── <layer-id>/    # 每层的目录
  │   │   ├── diff/      # 该层的文件差异
  │   │   ├── link      # 短标识符链接
  │   │   └── merged/   # 联合挂载后的完整文件系统
  │   └── l/             # 符号链接目录
  ├── containers/        # 容器元数据和日志
  │   └── <container-id>/
  │       ├── config.v2.json   # 容器配置
  │       ├── hostconfig.json  # 主机配置
  │       └── <id>-json.log    # 容器日志
  ├── image/             # 镜像元数据
  │   └── overlay2/
  │       ├── distribution/    # 分发元数据
  │       ├── imagedb/         # 镜像数据库
  │       ├── layerdb/         # 层数据库
  │       └── repositories.json # 仓库索引
  ├── volumes/           # 数据卷
  │   └── <volume-name>/
  │       └── _data/     # 卷数据
  ├── network/           # 网络配置
  ├── tmp/               # 临时文件
  └── swarm/             # Swarm 数据
# 查看 Docker 数据目录大小
sudo du -sh /var/lib/docker/
sudo du -sh /var/lib/docker/overlay2/
sudo du -sh /var/lib/docker/containers/

# 查看容器配置
sudo cat /var/lib/docker/containers/<container-id>/config.v2.json | jq .

要点回顾

要点核心内容
分层架构CLI → dockerd → containerd → runc → Kernel
containerd高级容器运行时,管理容器生命周期
runcOCI 标准运行时,实际创建容器
Namespace6 种命名空间实现进程、网络、文件系统隔离
Cgroups限制 CPU、内存、I/O 等资源使用
存储驱动overlay2 是推荐的默认存储驱动

注意事项

不要手动修改 /var/lib/docker: Docker 数据目录由 Docker 管理,手动修改可能导致数据损坏。

cgroup 版本一致性: Kubernetes 要求 kubelet 和容器运行时使用相同的 cgroup 版本。Docker 20.10+ 默认 cgroup v2。

overlay2 内核要求: overlay2 需要 Linux 4.0+ 内核,且需要 overlay 文件系统支持。使用 modprobe overlay 确认模块已加载。


下一步

04 - 镜像管理:学习 Docker 镜像的拉取、标记、多架构构建等操作。