Docker 背后的核心原理,这是理解现代容器技术的关键。
一、Docker 架构概览
Docker 的架构分为三个主要层次:
用户空间
└── Docker Client (docker CLI)
└── Docker Daemon (dockerd)
└── Containerd (容器运行时管理)
└── Containerd-shim (容器进程管理)
└── Runc (实际创建容器的工具)
二、核心技术原理
1. 命名空间(Namespaces) - 隔离的基石
Docker 使用 Linux 命名空间实现资源隔离:
# 查看容器的命名空间
$ ls -la /proc/<容器PID>/ns/
lrwxrwxrwx 1 root root 0 Jan 1 12:00 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 Jan 1 12:00 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 root root 0 Jan 1 12:00 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx 1 root root 0 Jan 1 12:00 net -> 'net:[4026531993]'
lrwxrwxrwx 1 root root 0 Jan 1 12:00 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Jan 1 12:00 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Jan 1 12:00 uts -> 'uts:[4026531838]'
六大命名空间的作用:
- PID:进程隔离,容器内 PID 1 是 init 进程
- NET:网络隔离,容器有自己的网络设备、IP、端口
- MNT:文件系统挂载点隔离
- IPC:进程间通信隔离
- UTS:主机名和域名隔离
- User:用户和用户组隔离
2. 控制组(cgroups) - 资源限制
cgroups 限制和监控容器资源使用:
# 查看容器的 cgroup 配置
$ cat /sys/fs/cgroup/cpu/docker/<容器ID>/cpu.shares
1024
$ cat /sys/fs/cgroup/memory/docker/<容器ID>/memory.limit_in_bytes
536870912 # 512MB 限制
cgroups 子系统:
- cpu/cpuacct:CPU 使用率和统计
- memory:内存限制
- blkio:块设备 I/O
- devices:设备访问控制
- freezer:暂停/恢复进程
- net_cls:网络流量分类
3. 联合文件系统(UnionFS) - 分层的秘密
Docker 镜像使用分层存储:
# 查看镜像的分层结构
$ docker history nginx:latest
IMAGE CREATED CREATED BY SIZE
c316d5a335a5 2 weeks ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon…"] 0B
<missing> 2 weeks ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B
<missing> 2 weeks ago /bin/sh -c #(nop) EXPOSE 80 0B
<missing> 2 weeks ago /bin/sh -c ln -sf /dev/stdout /var/log/nginx… 0B
写时复制(Copy-on-Write)机制:
基础镜像层 (只读)
└── 添加层1 (只读)
└── 添加层2 (只读)
└── 容器可写层 (读写)
4. 容器网络原理
Docker 网络模型:
# 查看 Docker 网络
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
bridge bridge bridge local
host host host local
none null null local
网络命名空间创建流程:
# 创建网络命名空间
$ ip netns add mynet
# 创建 veth pair(虚拟以太网设备对)
$ ip link add veth0 type veth peer name veth1
# 将 veth1 移动到容器的网络命名空间
$ ip link set veth1 netns <容器PID>
三、Docker 启动容器完整流程
1. 命令执行流程
1. 用户执行: docker run -d nginx:latest
2. Docker Client 发送请求到 Docker Daemon
3. Daemon 检查本地是否有 nginx:latest 镜像
4. 如果没有,从 Registry 拉取镜像
5. 调用 Containerd 创建容器
6. Containerd 调用 Runc
7. Runc 创建容器:
- 创建新的命名空间
- 设置 cgroups 限制
- 挂载 rootfs(使用 UnionFS)
- 设置容器网络
- 启动容器进程
8. Containerd-shim 接管容器进程
2. 容器创建的核心代码逻辑(简化版)
// Runc 创建容器的关键步骤
func createContainer(config ContainerConfig) {
// 1. 创建新的命名空间
unix.Unshare(unix.CLONE_NEWNS |
unix.CLONE_NEWUTS |
unix.CLONE_NEWIPC |
unix.CLONE_NEWPID |
unix.CLONE_NEWNET |
unix.CLONE_NEWUSER)
// 2. 设置 cgroups
cgroupPath := filepath.Join("/sys/fs/cgroup/memory", config.ID)
os.MkdirAll(cgroupPath, 0755)
ioutil.WriteFile(filepath.Join(cgroupPath, "memory.limit_in_bytes"),
[]byte(config.MemoryLimit), 0644)
// 3. 设置 rootfs
mount("overlay", rootfs, "overlay", 0,
"lowerdir="+lowerDir+",upperdir="+upperDir+",workdir="+workDir)
// 4. 设置网络
setupNetwork(config.NetworkConfig)
// 5. 执行容器进程
syscall.Exec(config.Path, config.Args, os.Environ())
}
四、高级特性原理
1. Docker Overlay 网络
物理主机 eth0 (192.168.1.100)
└── docker0 网桥 (172.17.0.1)
├── veth0 <---> veth1 (容器1: 172.17.0.2)
└── veth2 <---> veth3 (容器2: 172.17.0.3)
# 跨主机通信通过 VXLAN
物理网络: 192.168.1.0/24
├── 主机A: 192.168.1.100
│ └── 容器: 10.0.0.2
└── 主机B: 192.168.1.101
└── 容器: 10.0.0.3
2. 存储驱动比较
- overlay2(默认):性能好,支持最多层
- aufs:早期默认,兼容性好
- devicemapper:直接使用块设备
- btrfs/zfs:支持快照功能
3. 容器与虚拟机对比
传统虚拟机:
┌─────────────────────────────────────┐
│ App1 App2 App3 │
│ ┌──────┬──────┬──────┐ │
│ │Guest │Guest │Guest │ │
│ │ OS │ OS │ OS │ │
│ └──────┴──────┴──────┘ │
│ Hypervisor (KVM, VMware) │
│ Host Operating System │
│ Hardware │
└─────────────────────────────────────┘
Docker 容器:
┌─────────────────────────────────────┐
│ App1 App2 App3 │
│ ┌──────┬──────┬──────┐ │
│ │Bins/Libs│Bins/Libs│Bins/Libs│ │
│ └──────┴──────┴──────┘ │
│ Docker Engine (daemon) │
│ Host Operating System │
│ Hardware │
└─────────────────────────────────────┘
五、安全机制
1. 安全特性
- Capabilities 能力集:细粒度权限控制
- Seccomp 系统调用过滤:限制可用的系统调用
- AppArmor/SELinux:强制访问控制
- User Namespace:用户权限隔离
- 只读文件系统:防止恶意写入
2. 安全配置示例
# 运行容器时指定安全选项
$ docker run --security-opt seccomp=profile.json \
--cap-drop=ALL --cap-add=NET_BIND_SERVICE \
--read-only \
nginx:latest
六、实际调试示例
# 1. 查看容器底层信息
$ docker inspect <容器ID> | grep -A 10 "State"
$ ps aux | grep <容器PID>
# 2. 查看容器的 cgroup 限制
$ cat /sys/fs/cgroup/memory/docker/<容器ID>/memory.usage_in_bytes
# 3. 进入容器的命名空间
$ nsenter -t <容器PID> -n ip addr # 查看容器的网络
$ nsenter -t <容器PID> -m -p chroot /proc/<容器PID>/root /bin/bash
# 4. 使用 runc 直接管理容器
$ runc list # 查看 runc 管理的容器
七、总结要点
轻量级:共享主机内核,无需完整操作系统
快速启动:直接运行应用,无需启动完整 OS
高效资源利用:多个容器共享系统资源
一致性环境:开发、测试、生产环境一致
微服务友好:天然支持微服务架构
关键理解
- 容器不是虚拟机:没有 Hypervisor,直接运行在主机内核上
- 隔离不彻底:共享内核存在安全风险
- 性能接近原生:几乎没有性能损耗
- 镜像即环境:镜像包含了运行所需的一切
理解这些底层原理,能帮助你更好地使用 Docker,排查问题,设计更合理的容器化方案。