从零造容器
容器不是魔法,它是一堆 Linux 内核特性的组合拳:namespace 做隔离、cgroup 做资源限制、overlayfs 做文件系统分层。本系列用 C 和 Go 从零实现一个能跑的容器运行时,每篇拆解一个内核机制,最终组装成 OCI 兼容的迷你运行时。
为什么要自己造? Docker/containerd/runc 加起来几十万行代码,但核心机制其实就那么几个系统调用。造一遍,你就再也不会觉得容器是黑盒了。
适合谁看
本系列假设你熟悉 Linux 基础(进程、文件系统、网络),能读 C 和 Go 代码。如果你用过 Docker 但不知道它底层在干什么,这个系列就是为你写的。
如果你对下面这些还不熟,先补一下会更顺:
ip、mount、unshare、nsenter这些基础命令- 一个默认启用 cgroups v2 的发行版(Ubuntu 24.04 / Debian 12 / Fedora 40 都可以)
- 能接受
sudo make run这类需要 root 的实验
篇目依赖关系: - 01-04 是地基;05 依赖 03;06 依赖 01-05;07 依赖 06 - 08/09 最好在看完 01-07 后读;11 建议先读 02;12 建议先读 06/07
推荐阅读顺序: - 只想懂容器本质 → 01(namespace)→ 03(rootfs)→ 04(cgroup)→ 05(overlay) - 想自己写 runtime → 按顺序 01-07 - 关注安全 → 先看 01,然后跳到 08(seccomp)→ 09(rootless) - 想做性能优化 → 先看 02(网络基础),再看 11(性能实测)→ 10(microVM 对比)
目录
第一阶段:内核积木
用 C 逐个拆解容器依赖的内核机制。每篇一个系统调用家族,附可编译的示例代码。
- clone() + CLONE_NEWPID / CLONE_NEWUTS / CLONE_NEWNS
- PID 1 在容器里的特殊角色
- veth pair + bridge + NAT
- 用 tcpdump 验证数据流向
- chroot 的安全缺陷 vs pivot_root
- 手工组装 Alpine minirootfs
- CPU / Memory / IO 资源限制
- OOM killer 行为与 CFS bandwidth 尾延迟
第二阶段:存储与组装
有了内核积木,现在用 Go 把它们拼成一个真正能跑的运行时。
- copy-on-write 的实际性能代价
- 手工构建分层”镜像”
- 容器生命周期:create → start → exec → kill → delete
- init 进程与 reexec 技巧
- OCI Runtime Spec 关键字段拆解
- 用 ctr 调用自己的运行时
第三阶段:安全加固
容器能跑了,但还不安全。这一阶段给容器加上”不能做什么”的约束。
- seccomp 过滤器编写与 capability 裁剪
- Docker 默认 seccomp profile 拆解
- UID/GID 映射与 rootless 限制
- Podman rootless 实现分析
第四阶段:性能与对比
跳出容器看全局:和 microVM 比安全性,用数据说话看网络性能,最后回到 runc 源码看工业级实现。
- KVM API 最小化实现
- 启动时间与资源开销拆解
- 各网络方案多维度实测
- Cilium eBPF 替代 iptables 的收益
- libcontainer 核心流程
- 对比迷你运行时与 runc 的差距
相关阅读
本系列与博客其他文章形成交叉引用网络,按主题分组:
内核机制 - eBPF:Linux 内核的隐藏武器 — Seccomp-BPF 与 eBPF 同源 - eBPF + io_uring:高性能网络栈的终极形态 — 容器网络的 eBPF 数据面 - io_uring 系列 — 容器/VM 中的 I/O 路径差异
Go Runtime - Go 调度器深度拆解 — Go runtime fork 的陷阱
性能优化 - 内存分配器擂台 — Cgroups 下分配器行为变化
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
io_uring 系列文章
探索 Linux 下一代高性能异步 I/O 接口 io_uring,从核心原理到性能对比,再到与现有网络库的集成。
【从零造容器】Linux Namespaces:用 50 行 C 隔离一个进程
容器不是魔法。它就是几个系统调用。本文用 C 从 clone() 开始,逐个开启 PID/UTS/Mount/IPC namespace,看隔离到底是怎么回事。50 行代码,你就拥有了一个'容器'的雏形。
【从零造容器】Network Namespace:给你的进程接上虚拟网线
上一篇我们用 clone() 隔离了 PID、主机名和挂载点,但那个'容器'连 lo 都 ping 不通。本文从 CLONE_NEWNET 出发,用 veth pair + bridge + iptables MASQUERADE,一步步给容器接上网。
【从零造容器】Mount Namespace 与 pivot_root:构建容器文件系统
chroot 不是安全边界——10 行 C 就能逃出去。本文用 pivot_root 构建真正隔离的容器根文件系统:从 Alpine minirootfs 到设备节点,从 mount propagation 到只读根,一步步把容器的'地基'打牢。