
第02章Docker 架构原理本章目标深入理解 Docker 的 C/S 架构、核心组件及其交互方式掌握镜像分层的底层实现原理。2.1 Docker 整体架构2.1.1 C/S客户端-服务端架构Docker 采用经典的 C/S 架构分为三个核心部分┌──────────────────────────────────────────────────────────┐ │ Docker 架构总览 │ │ │ │ ┌─────────────┐ │ │ │ Docker │ docker build / docker pull │ │ │ Client │ docker run / docker ps │ │ │ (CLI 工具) │ │ │ └──────┬──────┘ │ │ │ REST API (Unix Socket / TCP) │ │ ▼ │ │ ┌─────────────────────────────────────────────┐ │ │ │ Docker Daemon (dockerd) │ │ │ │ │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ │ │ 镜像管理 │ │ 网络管理 │ │ 卷管理 │ │ │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ ┌──────────────────────────────────────┐ │ │ │ │ │ containerd │ │ │ │ │ │ ┌────────────┐ ┌─────────────┐ │ │ │ │ │ │ │ containerd │ │ containerd │ │ │ │ │ │ │ │ -shim │ │ -shim │ │ │ │ │ │ │ └──────┬─────┘ └──────┬──────┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌────┴───┐ ┌─────┴────┐ │ │ │ │ │ │ │容器 A │ │容器 B │ │ │ │ │ │ │ └────────┘ └──────────┘ │ │ │ │ │ └──────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────┘2.1.2 三大核心组件详解① Docker Client客户端Docker 客户端是你与 Docker 交互的主要方式# 你输入的所有 docker 命令都由客户端接收和解析dockerrun-d-p80:80 nginxdockerps-adockerimages客户端的职责解析命令行参数构造 REST API 请求将请求发送给 Docker Daemon格式化并展示返回结果客户端可以通过以下方式连接 DaemonUnix Socket默认仅本机unix:///var/run/docker.sockTCP远程管理tcp://remote-host:2375SSH安全远程通过 SSH 隧道连接② Docker Daemon守护进程 / dockerdDocker Daemon 是 Docker 的核心服务负责监听 API 请求管理 Docker 对象镜像、容器、网络、卷与其他 Daemon 通信集群模式# 查看 dockerd 进程psaux|grepdockerd# 查看 Docker 系统信息dockerinfo# 查看 Docker 版本dockerversion③ containerd runc容器运行时Docker 的容器运行时分为两层dockerd └── containerd容器运行时管理器 └── containerd-shim容器进程的父进程 └── runcOCI 标准容器运行时 └── 容器进程组件职责containerd容器生命周期管理、镜像分发、存储管理containerd-shim作为容器进程的父进程允许 dockerd 重启而不影响容器runc实际创建和运行容器调用 Namespace、Cgroups 等内核接口# 查看 containerd 状态systemctl status containerd# 查看 runc 版本runc--version2.2 Docker 镜像的分层原理2.2.1 镜像 只读层的叠加Docker 镜像不是一整块文件而是由多个只读层Layer堆叠而成Docker 镜像的层级结构 ┌─────────────────────────────────┐ │ Layer 4: COPY app.py /app/ │ ← 3KB (hash: sha256:abc123...) ├─────────────────────────────────┤ │ Layer 3: RUN pip install ... │ ← 15MB (hash: sha256:def456...) ├─────────────────────────────────┤ │ Layer 2: RUN apt-get install │ ← 80MB (hash: sha256:789ghi...) ├─────────────────────────────────┤ │ Layer 1: FROM ubuntu:22.04 │ ← 77MB (hash: sha256:jkl012...) └─────────────────────────────────┘ 每层只存储与上一层的差异delta2.2.2 分层的好处传统方式无分层 项目A: [OS Python 3.8 App A] 800MB 项目B: [OS Python 3.8 App B] 800MB 项目C: [OS Python 3.11 App C] 850MB 总占用: 2450MB Docker 分层方式 基础层: [OS (Ubuntu)] 77MB ← 三个项目共享 Python层: [Python 3.8] 120MB ← A和B共享 Python层: [Python 3.11] 130MB ← C使用 App A层: [App A 代码] 5MB App B层: [App B 代码] 8MB App C层: [App C 代码] 6MB 总占用: ~346MB去重后实际更少关键优势存储效率相同层只存储一份多个镜像共享基础层传输效率docker pull时只下载本地不存在的层构建效率docker build时未修改的层直接使用缓存安全审计每层都有唯一哈希标识可追溯变更来源2.2.3 写时复制Copy-on-Write容器启动时Docker 在镜像层之上添加一个可写层Container Layer运行中的容器 ┌─────────────────────────────────┐ │ Container Layer (可写) │ ← 修改、新增的文件在这里 │ - 修改了 /etc/config │ │ - 新增了 /tmp/data.log │ ├─────────────────────────────────┤ │ Layer 4: COPY app.py /app/ │ ← 只读镜像层 ├─────────────────────────────────┤ │ Layer 3: RUN pip install ... │ ← 只读镜像层 ├─────────────────────────────────┤ │ Layer 2: RUN apt-get install │ ← 只读镜像层 ├─────────────────────────────────┤ │ Layer 1: FROM ubuntu:22.04 │ ← 只读镜像层 └─────────────────────────────────┘ 读取文件时的查找顺序 1. 先在 Container Layer 查找 2. 没找到 → 在 Layer 4 查找 3. 没找到 → 在 Layer 3 查找 4. ... 依次向下查找 修改文件时写时复制 1. 从镜像层将文件复制到 Container Layer 2. 在 Container Layer 中修改副本 3. 原始镜像层文件不受影响2.2.4 镜像的唯一标识每个层都通过SHA256 哈希作为唯一标识# 查看镜像的分层信息dockerimage inspect nginx:latest# 输出示例简化{RootFS:{Type:layers,Layers:[sha256:2edcec35b5139b8d5c4d7cf15d1f5b5e2...,sha256:e2f5911f58b28f5d0f5b3a8f23d8b3e1b...,sha256:12d84a1c9e0e8f23a4b5c6d7e8f9a0b1c...]}}# 查看镜像的完整历史每一层的构建命令dockerhistorynginx:latest2.3 Docker 的存储驱动2.3.1 什么是存储驱动存储驱动负责管理镜像层和容器可写层的具体存储实现。不同的存储驱动在性能、稳定性和资源使用上有差异。2.3.2 常见存储驱动存储驱动底层技术适用场景性能特点overlay2OverlayFSLinux 默认推荐读写性能优秀内存效率高btrfsBtrfs 文件系统Btrfs 分区支持快照适合特定场景zfsZFS 文件系统ZFS 分区数据完整性高资源消耗大vfs直接复制调试用性能最差每层都完整复制fuse-overlayfsFUSE 实现的 OverlayFSRootless 模式非 root 用户可用# 查看当前使用的存储驱动dockerinfo|grepStorage Driver# 输出Storage Driver: overlay22.3.3 overlay2 详解overlay2 是目前最推荐的存储驱动它将层叠加为一个统一视图overlay2 工作原理 lowerdir (只读层可以有多个) ↓ ┌─────┐ │ L3 │ ← 最底层基础镜像 ├─────┤ │ L2 │ ├─────┤ │ L1 │ └──┬──┘ │ merged (联合挂载点 - 容器看到的视图) ↓ ┌──────────────┐ │ 统一文件系统 │ └──────┬───────┘ │ upperdir (可写层 - 容器的修改) ↓ ┌─────┐ │ Lw │ ← 容器运行时的修改 └─────┘ workdir (OverlayFS 内部使用的工作目录)2.4 Docker 的网络架构2.4.1 libnetworkDocker 使用libnetwork库来管理容器网络它抽象了多种网络实现┌──────────────────────────────────────────┐ │ Docker 网络架构 │ │ │ │ ┌──────────┐ │ │ │ daemon │ │ │ └────┬─────┘ │ │ │ │ │ ┌────┴─────────────────────┐ │ │ │ libnetwork │ │ │ │ ┌─────────┐ ┌────────┐ │ │ │ │ │ CNM │ │ drivers │ │ │ │ │ │ 模型 │ │ │ │ │ │ │ └─────────┘ │- bridge│ │ │ │ │ │- host │ │ │ │ │ │- overlay│ │ │ │ │ │- macvlan│ │ │ │ │ │- none │ │ │ │ └──────────────┴────────┘ │ │ └──────────────────────────────────────────┘2.4.2 网络模型CNMDocker 网络遵循容器网络模型Container Network ModelCNMCNM 三大组件 Sandbox沙盒 Endpoint端点 Network网络 ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ - 网络栈 │ │ - veth pair │ │ - 网络名称 │ │ - IP地址 │◄────►│ - 连接点 │◄────►│ - IP子网 │ │ - 路由表 │ │ │ │ - 网关 │ │ - DNS │ │ │ │ - 驱动 │ └─────────────┘ └─────────────┘ └─────────────┘ 每个容器 1个 Sandbox 每个容器连接到网络 1个 Endpoint 每个网络可以连接多个容器2.5 Docker 的镜像分发机制2.5.1 镜像分发协议Docker 使用OCI Distribution Spec来分发镜像推送/拉取镜像的流程 docker push myapp:v1.0 │ ▼ ┌──────────────────┐ │ Docker Client │ └────────┬─────────┘ │ HTTPS API ▼ ┌──────────────────┐ │ Registry Server │ (如 Docker Hub, Harbor) │ │ │ ┌────────────┐ │ │ │ Manifest │ │ ← 清单文件描述镜像由哪些层组成 │ ├────────────┤ │ │ │ Layer 1 │ │ ← 每层是独立的 blob 对象 │ │ Layer 2 │ │ │ │ Layer 3 │ │ │ └────────────┘ │ └──────────────────┘2.5.2 镜像清单ManifestManifest 是一个 JSON 文件描述了镜像的完整信息{schemaVersion:2,mediaType:application/vnd.docker.distribution.manifest.v2json,config:{mediaType:application/vnd.docker.container.image.v1json,size:7023,digest:sha256:b5b2b2c54748232...},layers:[{mediaType:application/vnd.docker.image.rootfs.diff.tar.gzip,size:32654,digest:sha256:e692418e4cb...},{mediaType:application/vnd.docker.image.rootfs.diff.tar.gzip,size:16724,digest:sha256:3c3dda4e7a...}]}2.6 Docker 的完整工作流程2.6.1 docker run 的完整链路docker run -d -p 8080:80 nginx:latest 执行步骤 1. Docker Client 解析命令 └── 构造 API 请求POST /containers/create 2. Docker Daemon 接收请求 ├── 检查本地是否有 nginx:latest 镜像 │ ├── 有 → 直接使用 │ └── 没有 → 调用 Registry 拉取 │ ├── 拉取 manifest │ ├── 对比本地层下载缺失的层 │ └── 组装镜像 │ ├── 创建容器配置 │ ├── 设置网络端口映射 8080→80 │ ├── 设置存储可写层 │ └── 设置资源限制 │ └── 调用 containerd 创建容器 ├── containerd 调用 containerd-shim ├── shim 调用 runc 创建容器 │ ├── 创建 namespace隔离进程、网络等 │ ├── 设置 cgroups限制 CPU、内存等 │ ├── 挂载文件系统overlay2 层叠 │ └── 启动容器进程nginx -g daemon off; └── 容器进程运行在独立的隔离环境中 3. 返回容器 ID └── Docker Client 显示容器 ID2.6.2 docker build 的完整链路docker build -t myapp:v1.0 . 执行步骤 1. 发送构建上下文Build Context └── 将当前目录.打包发送给 Docker Daemon 2. 解析 Dockerfile └── 逐条执行指令 3. 每条指令生成一个新的镜像层 FROM ubuntu:22.04 → 使用已有层或拉取 RUN apt-get update → 创建新层安装包 COPY . /app → 创建新层复制文件 CMD [python, app.py] → 仅修改元数据不创建新层 4. 构建缓存机制 ├── 检查每一层是否有缓存 │ ├── 有缓存 → 使用缓存层⚡ 快速 │ └── 无缓存 → 执行指令创建新层 └── 一旦某层无缓存后续所有层都重新构建2.7 Docker Daemon 的配置2.7.1 配置文件位置操作系统配置文件路径Linux/etc/docker/daemon.jsonWindowsC:\ProgramData\docker\config\daemon.jsonmacOSDocker Desktop → Settings → Docker Engine2.7.2 常用配置项{data-root:/data/docker,storage-driver:overlay2,log-driver:json-file,log-opts:{max-size:100m,max-file:3},registry-mirrors:[https://mirror.ccs.tencentyun.com],insecure-registries:[],max-concurrent-downloads:10,max-concurrent-uploads:5,default-address-pools:[{base:172.17.0.0/16,size:24}],live-restore:true,userland-proxy:false}配置项说明data-rootDocker 数据存储目录镜像、容器、卷等storage-driver存储驱动类型log-driver容器日志驱动log-opts日志驱动选项大小限制registry-mirrors镜像加速器live-restoredockerd 重启时保持容器运行userland-proxy禁用用户态代理提高性能# 重新加载 Docker 配置sudosystemctl daemon-reloadsudosystemctl restartdocker# 验证配置dockerinfo2.8 Docker 的命名空间详解2.8.1 六大命名空间# 进入一个运行中的容器dockerrun-it--rmubuntu:22.04 /bin/bash# 在容器内查看隔离效果# 1. PID Namespace - 只能看到容器自己的进程psaux# USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND# root 1 0.0 0.0 2488 1320 pts/0 Ss 10:00 0:00 /bin/bash# 2. NET Namespace - 独立的网络栈ipaddr# 1: lo: LOOPBACK,UP,LOWER_UP ...# 15: eth0if16: BROADCAST,MULTICAST,UP,LOWER_UP ...# inet 172.17.0.2/16 ...# 3. MNT Namespace - 独立的文件系统ls/# bin boot dev etc home lib media mnt opt proc root run sbin srv sys tmp usr var# 4. UTS Namespace - 独立的主机名hostname# a1b2c3d4e5f6 (容器ID的前12位)2.8.2 Namespace 的系统调用// Linux 提供的 namespace 系统调用// clone() - 创建新进程并放入新的 namespaceintclone(int(*fn)(void*),void*stack,intflags,void*arg);// 常用 flagsCLONE_NEWPID// 新的 PID namespaceCLONE_NEWNET// 新的 NET namespaceCLONE_NEWNS// 新的 MNT namespaceCLONE_NEWUTS// 新的 UTS namespaceCLONE_NEWIPC// 新的 IPC namespaceCLONE_NEWUSER// 新的 USER namespace// unshare() - 将当前进程放入新的 namespaceintunshare(intflags);// setns() - 将当前进程加入已有的 namespaceintsetns(intfd,intnstype);2.9 Docker 的 Cgroups 详解2.9.1 Cgroups v1 vs v2特性Cgroups v1Cgroups v2层级结构每个控制器独立层级统一层级资源分配更灵活但复杂更直观、统一内存限制memory.limit_in_bytesmemory.maxCPU 限制cpu.cfs_quota_uscpu.max默认版本CentOS 7/8Ubuntu 20.04、Fedora2.9.2 查看容器的 Cgroups 配置# 启动一个限制资源的容器dockerrun-d--nametest-container\--cpus1.5\--memory512m\--memory-swap512m\nginx:latest# 查看容器的 cgroup 限制# Cgroups v2 方式cat/sys/fs/cgroup/system.slice/docker-container-id.scope/memory.maxcat/sys/fs/cgroup/system.slice/docker-container-id.scope/cpu.max2.10 动手实验实验 2.1观察镜像的分层结构# 拉取一个镜像dockerpull python:3.11-slim# 查看镜像的每一层dockerhistorypython:3.11-slim# 查看详细的层信息dockerinspect python:3.11-slim实验 2.2观察容器的隔离性# 终端1在宿主机上查看进程psaux|grepnginx# 终端2启动 Nginx 容器dockerrun-d--namenginx-test-p8080:80 nginx:latest# 终端3在宿主机查看进程能看见容器进程psaux|grepnginx# root 1234 ... nginx: master process nginx -g daemon off;# system 1235 ... nginx: worker process# 终端4进入容器查看进程只看到自己的进程dockerexec-itnginx-testpsaux# PID TTY STAT TIME COMMAND# 1 ? Ss 0:00 nginx: master process nginx -g daemon off;# 21 ? S 0:00 ps aux实验 2.3验证写时复制# 启动一个 Ubuntu 容器dockerrun-it--namecopy-test ubuntu:22.04 /bin/bash# 在容器内创建文件echoHello Container/tmp/test.txtcat/tmp/test.txt# Hello Container# 退出容器exit# 查看容器的可写层大小dockerinspect copy-test--format{{.GraphDriver.Data}}# 再次启动容器文件仍然存在在可写层中dockerstart-aicopy-testcat/tmp/test.txt# Hello Containerexit2.11 本章小结要点内容架构模式C/S 架构ClientCLI→ Daemondockerd→ containerd → runc镜像分层每条 Dockerfile 指令生成一层层通过 SHA256 哈希唯一标识写时复制容器启动时添加可写层修改操作通过 CoW 实现存储驱动推荐 overlay2管理层的叠加和读写命名空间PID/NET/MNT/UTS/IPC/USER 六种隔离机制Cgroups限制 CPU、内存、磁盘 I/O、网络带宽等资源2.12 课后练习理解题解释 Docker 镜像的分层存储如何提高存储效率和构建速度。实操题使用docker history查看 3 个不同镜像的层结构分析每层的内容。进阶题配置 Docker 使用远程 DaemonTCP 模式体验 C/S 架构的网络通信。 下一章Docker 安装部署 —— 在不同操作系统上搭建 Docker 环境