runc
这是一个容器领域里“最底层、最核心、但又最容易被忽略”的组件。
内容:定位 -> 架构 -> 生命周期 -> 内核机制 -> 与 Docker / containerd 的关系 -> 为什么必须理解 runc。
runc 是一个“把 Linux 内核能力真正用起来”的程序:
它负责把 namespaces、cgroups、capabilities、seccomp 等配置,转化为一个真正运行中的 Linux 进程。
- 📌 runc 本身不是守护进程
- 📌 runc 启动完容器就退出
- 📌 容器 = 被 runc 创建并配置的普通 Linux 进程
容器完整技术栈(自上而下)
┌───────────────────────────┐
│ Kubernetes / Docker CLI │
├───────────────────────────┤
│ containerd / cri-o │
├───────────────────────────┤
│ runc │ <-【核心】
├───────────────────────────┤
│ Linux Kernel │
│ namespaces / cgroups / fs │
└───────────────────────────┘
👉 所有 OCI 标准容器,最终都由 runc 创建
不是虚拟机!
runc 的本质
- 一个 CLI 程序
- 用 Go 编写
- 实现 OCI Runtime Specification
功能只有一个:创建 + 启动 一个进程,并套上隔离与限制
OCI 定义了三样东西:
- filesystem layout
- config.json
- runtime behavior
{
"process": {
"args": ["mysql"],
"env": ["PATH=/usr/bin"],
"cwd": "/"
},
"root": {
"path": "rootfs",
"readonly": false
},
"linux": {
"namespaces": [
{ "type": "pid" },
{ "type": "net" },
{ "type": "mnt" }
],
"resources": {
"cpu": {},
"memory": {}
}
}
}
📌 runc 就是 config.json 的“执行器”
runc create mycontainer
runc 做了什么:
- clone() 创建进程
- 设置 namespaces
- 设置 cgroups
- mount rootfs
- 设置 capabilities
- 准备但不执行 ENTRYPOINT
📌 容器进程:Paused 状态
runc start mycontainer
- 执行 config.json 中的进程
- PID 诞生
- 容器“活了”
- 发送信号
- 清理 cgroups
- 清理 namespace 句柄
| Namespace | 隔离什么 |
|---|---|
| pid | 进程 |
| net | 网络 |
| mnt | 文件系统 |
| uts | hostname |
| ipc | 共享内存 |
| user | UID/GID |
📌 runc 调用 clone(CLONE_NEW)
| 资源 | 控制 |
|---|---|
| CPU | quota / shares |
| Memory | limit / swap |
| IO | blkio |
| PIDs | 最大进程数 |
📌 直接写 /sys/fs/cgroup
CAP_SYS_ADMIN ❌
CAP_NET_ADMIN ❌
CAP_CHOWN ✅
👉 容器 root ≠ 宿主机 root
"seccomp": {
"defaultAction": "SCMP_ACT_ERRNO",
"syscalls": [
{ "names": ["read", "write"], "action": "SCMP_ACT_ALLOW" }
]
}
📌 减少攻击面
Docker 实际调用链
docker run
-> dockerd
-> containerd
-> containerd-shim
-> runc
为什么要有 containerd-shim?
- runc 退出后容器仍需存在
- shim 持有 stdio
- shim 处理信号 & exit code
📌 runc 不常驻
| 对比项 | runc / container | 虚拟机 |
|---|---|---|
| 内核 | 宿主机 | 独立 |
| 启动 | ms 级 | 秒级 |
| 隔离 | 进程级 | 硬件级 |
| 安全 | 依赖内核 | 强 |
| 资源开销 | 极低 | 高 |
因为:
- runc 运行在 宿主机 root
- 操作 namespaces / mount
- 任何漏洞 = root 权限
📌 历史漏洞:
- CVE-2019-5736(容器覆盖 runc 二进制)
理解 runc 等于:
- 理解 容器的真实边界
- 理解 容器 ≠ VM
- 理解 数据库为什么绕不过 overlay2
- 理解 cgroups/NUMA/IO 调优的生效点
📌 容器不是魔法,只是进程
runc = 把 “普通 Linux 进程” 包装成 “看起来像独立系统” 的工具。