runc是OCI标准的命令行参考实现,仅负责按config.json启动进程隔离环境,不处理镜像、网络或存储;手动运行需构造含config.json和rootfs的合法bundle,注意process.args为数组、rootfs含对应/bin/sh;runc run为原子操作,create+start可精细控制;排查需查日志、关SELinux、验证user_namespace。

runc 是什么,不是什么
runc 不是 Docker、不是 containerd、更不是 Kubernetes。它是 OCI(Open Container Initiative)标准的参考实现,一个纯粹的命令行工具,只做一件事:根据 config.json 启动一个符合 OCI 规范的进程隔离环境。它不处理镜像拉取、网络配置、存储卷挂载——那些都该由上层运行时(比如 containerd)完成。
你直接调用 runc,通常只在调试容器启动失败、验证 OCI bundle 结构、或嵌入轻量级容器逻辑时才有意义。生产环境几乎不会裸用 runc run。
怎么手动跑通一个最简 runc 容器
核心是构造合法的 OCI bundle 目录,关键文件只有两个:config.json 和 rootfs。漏掉任何一项,runc spec 生成的模板也得人工核对。
-
runc spec生成默认模板后,必须检查root.path是否指向真实存在的目录(不能是空路径或符号链接未解引用) -
process.args必须是非空数组,比如["/bin/sh"];写成"/bin/sh"(字符串而非数组)会导致invalid config: process.args must be an array - rootfs 下必须包含对应架构的
/bin/sh(例如用busybox静态编译版),否则启动报错container_linux.go:380: starting container process caused: exec: "/bin/sh": stat /bin/sh: no such file or directory - 运行前需用
runc checkpoint或runc state确认当前无残留状态,避免failed to create container: container with id exists
runc run 和 runc create + runc start 的区别在哪
这是最容易混淆的操作模式。两者最终都启动容器,但生命周期控制粒度不同:
-
runc run是原子操作:创建 + 启动 + 前台阻塞。适合调试,但无法在启动前注入 namespace、cgroup 或 seccomp 策略 -
runc create只做初始化(分配 cgroup、挂载、设置 namespace),容器处于 “created” 状态;此时可手动修改/sys/fs/cgroup/.../cgroup.procs或注入 seccomp profile;再runc start才真正执行process.args - 如果用
runc run --detach,它其实内部仍走 create + start 流程,但会 fork 出后台进程——此时若父进程退出太快,子进程可能被 init 收养,导致runc ps查不到 PID
常见 panic 和权限问题怎么定位
runc 报错信息往往藏在 Go runtime 层,直接看 stderr 很难判断根源。真正有效的排查路径是加日志和降权验证:
- 启动时加
--log /tmp/runc.log --log-format json,重点关注"msg":"creating container"后的 error 字段,而不是开头的 warning - 报
operation not permitted大概率是没关 SELinux(setenforce 0临时验证)或没开user_namespace.enable=1(Ubuntu 22.04+ 默认关闭) - 用
runc list看不到容器?先确认是否用了--root指定了非默认路径(默认是/run/runc),否则runc list默认查不到 - seccomp 规则拒绝系统调用时,错误是静默的(进程直接 exit 1),必须用
strace -f runc run ...捕获最后被 kill 的 syscall
OCI bundle 的合法性比功能更重要——runc check 不校验 rootfs 内容,只验 JSON schema;真正出问题,永远在 config 和文件系统之间那层映射没对齐。










