Docker 是一个开源的容器化平台,它基于 Linux 内核的多种技术实现了轻量级的虚拟化。理解 Docker 的底层实现对于深入掌握容器技术至关重要。
核心组件
Docker 的底层实现主要依赖以下 Linux 内核特性:
Namespace(命名空间):实现资源隔离
Cgroups(控制组):实现资源限制
Union File System(联合文件系统):实现镜像分层和存储
网络:实现容器网络通信
容器运行时:管理容器的生命周期
graph TB
A[Docker 容器] --> B[Namespace 隔离]
A --> C[Cgroups 限制]
A --> D[UnionFS 存储]
A --> E[网络虚拟化]
A --> F[容器运行时]
B --> B1[PID Namespace]
B --> B2[Network Namespace]
B --> B3[Mount Namespace]
B --> B4[UTS Namespace]
B --> B5[IPC Namespace]
B --> B6[User Namespace]
style A fill:#ffcccc
style B fill:#ccffcc
style C fill:#ccccff
style D fill:#ffffcc
style E fill:#ffccff
style F fill:#ccffff
Docker 架构
graph TB
A[Docker Client] --> B[Docker Daemon]
B --> C[Containerd]
C --> D[containerd-shim]
D --> E[runc]
E --> F[Linux Kernel]
F --> G[Namespaces]
F --> H[Cgroups]
F --> I[UnionFS]
F --> J[网络栈]
style A fill:#ffcccc
style B fill:#ccffcc
style C fill:#ccccff
style D fill:#ffffcc
style E fill:#ffccff
style F fill:#ccffff
Namespace(命名空间)
Namespace 是 Linux 内核提供的资源隔离机制,Docker 使用 Namespace 来实现容器的隔离。
Namespace 类型
Docker 使用以下 6 种 Namespace:
1. PID Namespace(进程隔离)
PID Namespace 隔离进程 ID 空间,容器内的进程只能看到容器内的进程。
1 2 3 4 5 6
# 查看容器的 PID Namespace docker run -d --name test nginx docker exectest ps aux
# 在宿主机查看 ps aux | grep nginx
graph LR
A[宿主机 PID 1] --> B[容器1 PID 1]
A --> C[容器2 PID 1]
B --> D[容器1 进程树]
C --> E[容器2 进程树]
style A fill:#ffcccc
style B fill:#ccffcc
style C fill:#ccccff
2. Network Namespace(网络隔离)
Network Namespace 隔离网络设备、IP 地址、端口等网络资源。
1 2 3 4 5 6
# 查看容器的网络命名空间 docker run -d --name test nginx ip netns list
# 查看容器网络配置 docker inspect test | grep -A 20 NetworkSettings
graph TB
A[宿主机网络栈] --> B[容器1 网络栈]
A --> C[容器2 网络栈]
B --> D[veth pair]
C --> E[veth pair]
D --> F[bridge]
E --> F
style A fill:#ffcccc
style B fill:#ccffcc
style C fill:#ccccff
style F fill:#ffffcc
3. Mount Namespace(文件系统隔离)
Mount Namespace 隔离文件系统挂载点,每个容器有独立的文件系统视图。
1 2 3
# 查看容器的挂载点 docker run -d --name test nginx docker exectest mount
4. UTS Namespace(主机名隔离)
UTS Namespace 隔离主机名和域名,每个容器可以有独立的主机名。
1 2 3
# 设置容器主机名 docker run -d --name test --hostname mycontainer nginx docker exectest hostname
5. IPC Namespace(进程间通信隔离)
IPC Namespace 隔离 System V IPC 和 POSIX 消息队列。
1 2 3
# 查看 IPC 资源 docker run -d --name test nginx docker exectest ipcs
6. User Namespace(用户隔离)
User Namespace 隔离用户和组 ID,容器内的 root 用户可以映射到宿主机的非 root 用户。
1 2
# 使用 User Namespace docker run -d --name test --userns=host nginx
# 限制容器 CPU 使用 docker run -d --name test --cpus="1.5" nginx
# 限制 CPU 份额(相对权重) docker run -d --name test --cpu-shares=512 nginx
# 绑定到特定 CPU 核心 docker run -d --name test --cpuset-cpus="0,1" nginx
内存限制
1 2 3 4 5 6 7 8
# 限制容器内存使用 docker run -d --name test --memory="512m" nginx
# 限制内存 + Swap docker run -d --name test --memory="512m" --memory-swap="1g" nginx
# OOM Killer 优先级 docker run -d --name test --oom-kill-disable nginx
I/O 限制
1 2 3 4 5
# 限制块设备 I/O docker run -d --name test \ --device-read-bps /dev/sda:1mb \ --device-write-bps /dev/sda:1mb \ nginx
Cgroups 实现原理
Cgroups v1 结构
graph TB
A[Cgroups 层级] --> B[CPU 子系统]
A --> C[Memory 子系统]
A --> D[BlkIO 子系统]
B --> E[CPU 限制配置]
C --> F[Memory 限制配置]
D --> G[I/O 限制配置]
E --> H[进程组]
F --> H
G --> H
style A fill:#ffcccc
style H fill:#ccffcc
graph TB
A[容器层] --> B[Upper Dir]
A --> C[Lower Dir 1]
A --> D[Lower Dir 2]
A --> E[Lower Dir N]
C --> F[镜像层 1]
D --> G[镜像层 2]
E --> H[镜像层 N]
style A fill:#ffcccc
style B fill:#ccffcc
style F fill:#ccccff
style G fill:#ffffcc
style H fill:#ffccff
# 查看容器的 Overlay 挂载 docker run -d --name test nginx mount | grep overlay
# 输出示例: # overlay on /var/lib/docker/overlay2/.../merged type overlay # (rw,relatime,lowerdir=...,upperdir=...,workdir=...)
写时复制(Copy-on-Write)
sequenceDiagram
participant C as 容器
participant U as Upper Dir
participant L as Lower Dir
participant M as Merged View
C->>M: 读取文件
M->>L: 从底层读取
C->>M: 修改文件
M->>U: 复制到上层(CoW)
M->>U: 修改上层文件
C->>M: 读取修改后的文件
M->>U: 从上层读取
graph TB
A[宿主机] --> B[docker0 网桥]
B --> C[veth0]
B --> D[veth1]
C --> E[容器1 eth0]
D --> F[容器2 eth0]
B --> G[宿主机网络]
style A fill:#ffcccc
style B fill:#ccffcc
style E fill:#ccccff
style F fill:#ffffcc
2. Host 模式
Host 模式直接使用宿主机的网络栈,没有网络隔离。
1
docker run -d --name test --network host nginx
3. None 模式
None 模式不配置网络,容器只有 loopback 接口。
1
docker run -d --name test --network none nginx
4. Container 模式
Container 模式共享其他容器的网络命名空间。
1 2
docker run -d --name test1 nginx docker run -d --name test2 --network container:test1 nginx
# 端口映射规则 docker run -d --name test -p 8080:80 nginx # Docker 会创建 DNAT 规则将宿主机 8080 端口映射到容器 80 端口
网络数据流
sequenceDiagram
participant C as 客户端
participant H as 宿主机
participant B as docker0
participant V as veth
participant D as 容器
C->>H: 请求 8080 端口
H->>B: iptables DNAT
B->>V: 转发数据包
V->>D: 到达容器 80 端口
D->>V: 响应数据包
V->>B: 返回数据
B->>H: SNAT
H->>C: 响应客户端
容器运行时
容器运行时负责容器的创建、启动、停止和删除。
运行时架构
Docker 运行时演进
graph LR
A[Docker 1.11 之前] --> B[直接使用 libcontainer]
B --> C[Docker 1.11+]
C --> D[containerd]
D --> E[containerd-shim]
E --> F[runc]
style A fill:#ffcccc
style C fill:#ccffcc
style D fill:#ccccff
style F fill:#ffffcc
组件说明
containerd:容器运行时守护进程
containerd-shim:每个容器的运行时代理
runc:OCI 兼容的容器运行时
runc 实现
runc 是 OCI(Open Container Initiative)标准的参考实现。
容器创建流程
sequenceDiagram
participant D as Docker
participant C as containerd
participant S as shim
participant R as runc
participant K as Kernel
D->>C: 创建容器请求
C->>S: 启动 shim
S->>R: runc create
R->>K: 创建 Namespace
R->>K: 配置 Cgroups
R->>K: 挂载文件系统
R->>S: 容器已创建
S->>C: 返回容器 ID
C->>D: 容器创建成功
D->>C: 启动容器
C->>S: start
S->>R: runc start
R->>K: 执行容器进程
runc 命令
1 2 3 4 5 6 7 8 9 10 11
# 创建容器 runc create <container-id>
# 启动容器 runc start <container-id>
# 停止容器 runc kill <container-id> SIGTERM
# 删除容器 runc delete <container-id>
镜像构建
Docker 镜像由多个只读层组成,通过 Dockerfile 构建。
镜像结构
graph TB
A[Docker 镜像] --> B[层 1: 基础镜像]
A --> C[层 2: 安装软件]
A --> D[层 3: 复制文件]
A --> E[层 4: 配置]
A --> F[层 5: 启动命令]
style A fill:#ffcccc
style B fill:#ccffcc
style F fill:#ccccff
sequenceDiagram
participant U as 用户
participant D as Docker Daemon
participant B as BuildKit
participant L as 层缓存
U->>D: docker build
D->>B: 解析 Dockerfile
B->>B: 创建构建上下文
B->>L: 检查层缓存
alt 缓存命中
L->>B: 使用缓存层
else 缓存未命中
B->>B: 执行构建步骤
B->>L: 创建新层
end
B->>D: 生成镜像
D->>U: 构建完成
sequenceDiagram
participant C as Client
participant D as Docker Daemon
participant R as Runtime
participant K as Kernel
C->>D: docker run
D->>D: 检查镜像
D->>D: 创建容器配置
D->>R: 创建容器
R->>K: 创建 Namespace
R->>K: 配置 Cgroups
R->>K: 挂载文件系统
R->>K: 设置网络
R->>K: 启动进程
K->>R: 进程运行中
R->>D: 容器已启动
D->>C: 返回容器 ID
安全机制
安全特性
1. Capabilities(能力)
Linux Capabilities 提供细粒度的权限控制。
1 2
# 移除所有 capabilities,只保留必要的 docker run -d --name test --cap-drop ALL --cap-add NET_BIND_SERVICE nginx
2. Seccomp(安全计算)
Seccomp 限制容器可以使用的系统调用。
1 2
# 使用自定义 seccomp 配置 docker run -d --name test --security-opt seccomp=profile.json nginx
3. AppArmor/SELinux
使用 AppArmor 或 SELinux 提供额外的安全层。
1 2
# 使用 AppArmor docker run -d --name test --security-opt apparmor=docker-default nginx
4. User Namespace
使用 User Namespace 隔离用户 ID。
1 2
# 启用 User Namespace docker run -d --name test --userns=host nginx
安全最佳实践
✅ 使用非 root 用户运行容器
✅ 限制容器的 capabilities
✅ 使用只读文件系统
✅ 限制资源使用(Cgroups)
✅ 定期更新镜像
✅ 扫描镜像漏洞
性能优化
存储优化
1. 使用多阶段构建
1 2 3 4 5 6 7 8 9 10
# 多阶段构建示例 FROM golang:1.19 AS builder WORKDIR /app COPY . . RUN go build -o app .
FROM alpine:latest WORKDIR /app COPY --from=builder /app/app . CMD ["./app"]
2. 优化层缓存
1 2 3 4 5
# 好的做法:先复制依赖文件 COPY go.mod go.sum ./ RUN go mod download COPY . . RUN go build