runc 源码分析-1 概述

runc 是什么

最近在协助其他部门开发基于Android的容器方案,有机会深入接触了下kerneldocker等项目,发现容器的现状比起我14年刚毕业时,已经大不一样了

  • 容器的工业级标准化组织OCI(Open Container Initiative)出炉,这是业界大佬为避免容器生态和docker耦合过紧做的努力,也是docker做出的妥协
  • 随着docker等容器引擎自身功能越来越丰富,其逐渐呈现出组件化的趋势(将底层交给OCI,自己则专注网络,配置管理,集群,编排,安全等方面)
  • 内核中关于容器的开发也如火如荼,包括 capabilities, fs, net, uevent等和容器相关的子系统,代码增长都迎来了第二春

如上所说,随着libcontainer从docker引擎中解耦并贡献给OCI后,runc实际上已经成为了第一个OCI runtime spec的实现。
可以说,任何OCI runtime spec的实现,都能通过自己是提供的关于容器的接口实现容器的起停,资源管理等功能。

runc 在容器引擎中的位置

docker为了兼容OCI runtime spec,在1.11版本后,将runc与docker daemon独立开来,作为实际的管理、操作容器的底层组件。

下图就是runc出现前后,docker架构的变化。

runc-in-docker-engine-process

如下图所示,runc在容器引擎中,仅负责容器生命周期的管理和配置(在docker中,受containerd调用);由于runc本身已经具备了容器基本的管理能力,因而docker,rkt等则能更加关注于对容器的网络管理,编排等领域。

runc-in-docker-arch

runc 的意义

runc符合OCI runtime spec,同时目前也一直在社区中进行着维护。
根据OCI的文档,任何实现了OCI runtime spec的组件,都可以替换runc在容器引擎中的位置,因此,这也可以说是给业界提供了一个Production-Grade的DEMO。(类似的,k8s最近终于引入了一个兼容层,开始将自己和docker/rkt解耦合;另:当前docker版本(1.12)中,也已经可以通过参数替换oci runtime了)。

runc的代码结构

如果看一下 runc目录中.go文件的名称和runc help中 command的名称,可以发现runc代码的目录还是很清晰的,基本每一个.go文件,就对应了一个command

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
% tree -L 1 
.
├── checkpoint.go
├── contrib
├── CONTRIBUTING.md
├── create.go
├── delete.go
├── Dockerfile
├── events.go
├── exec.go
├── init.go
├── kill.go
├── libcontainer
├── LICENSE
├── list.go
├── main.go
├── MAINTAINERS
├── MAINTAINERS_GUIDE.md
├── Makefile
├── man
├── NOTICE
├── notify_socket.go
├── pause.go
├── PRINCIPLES.md
├── ps.go
├── README.md
├── restore.go
├── rlimit_linux.go
├── run.go
├── script
├── signals.go
├── spec.go
├── start.go
├── state.go
├── tests
├── tty.go
├── update.go
├── utils.go
├── utils_linux.go
├── vendor
├── vendor.conf
└── VERSION

后续本文将会逐步进入runc的代码,摸清runc运行时的各个方面。