如果你在用 Docker 或者 Kubernetes 想必你对 容器运行时 这个概念应该不会太陌生。

Docker 中,当你使用 docker info 即可查看当前所使用的 runtime。

➜  ~ docker info
...
Server Version: 18.06.1-ce
Storage Driver: overlay2
 Backing Filesystem: extfs
 Supports d_type: true
 Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
...
Swarm: inactive
Runtimes: nvidia runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 468a545b9edcd5932818eb9de8e72413e616e86e
runc version: 69663f0bd4b60df09991c08812a60108003fa340
init version: fec3683
Security Options:
 seccomp
  Profile: default
...

同时,你还可以自己在 /etc/docker/daemon.json 中增加支持的 runtime , 以及在 docker run 的时候,通过 --runtime 参数配置所使用的 runtime 。

什么是 runc

简单来说,它是 OCI 标准的一种实现。 OCI 标准包含 运行时标准镜像标准 两个部分,而 OCI 这个组织则是由 Docker, CoreOS 和其他的一些公司共同发起创建的,致力于将容器运行时和格式标准化。

即:凡是遵守此标准的实现,无论是 Docker 还是 rkt 或者其他的运行时实现,均可以通过标准的镜像启动容器。

runc 则是在 OCI 成立后,Docker 将其容器运行时 libcontainer 贡献出来后,并加以改造而成的。而 libcontainer 也是在 Docker 0.9 版本开始加入,我也是从这个版本开始使用它。

当然 libcontainer 出现的本意不仅是替换当时的 LXC 依赖,同时也希望能以此作为规范(让其他的项目使用)最终,目标达成。

runc 如何使用

runc 的使用本不是本篇的重点,稍微带过。

➜  ~ docker export -o debian.tar  `docker create debian`
➜  ~ ls
debian.tar
➜  ~ tar -C rootfs -xf debian.tar
➜  ~ ls
debian.tar   rootfs
➜  ~ tree -L 1 -a rootfs
rootfs
├── bin
├── boot
├── dev
├── .dockerenv
├── etc
├── home
├── lib
├── lib64
├── media
├── mnt
├── opt
├── proc
├── root
├── run
├── sbin
├── srv
├── sys
├── tmp
├── usr
└── var

19 directories, 1 file

通过以上操作,得到了运行一个容器所必须的 rootfs,当然,上面看到我是通过 docker 来获得这个文件的,但其实这只是为了方便罢了,docker 并不是必须的。

➜  ~ runc spec
➜  ~ ls
config.json  debian.tar  rootfs

通过上面的操作,会得到一个基本的 config.json 的配置文件,这里面包含着运行一个容器所需要的一些配置。其中会包含着一些例如:

"ociVersion": "1.0.1-dev"

这种用于标识规范版本号的信息。

接下来稍微对 config.json 文件进行查看,便可以看到未通过 user Namespace 进行隔离,所以我们需要以 root 权限运行我们的容器。

➜  ~ sudo runc run debian
# ls                                      
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
# hostname    
runc               
# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 9 (stretch)"
NAME="Debian GNU/Linux"
VERSION_ID="9"
VERSION="9 (stretch)"
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

可以看到容器已经运行成功。当然,我们也可以不通过 root 权限运行容器,只要简单的通过 user Namespace 进行隔离,并添加 usergroup 的映射之类的便可以了。此处不做赘述。

为何有 runc 1.0-rc6 存在

我们知道,OCI 在 2015 年成立,在 2017 年 7 月的时候正式宣布 OCI v1.0.0 release 。其实在 2017 年的 11 月还 release 了 v1.0.1 版本。

前面已经提过 runcOCI 的官方实现,那为何时过一年还未正式 release 1.0 呢?这里面原因其实说来也复杂,这也是这篇文章想聊的部分。

首先:runc 1.0 我们想正式 relase 么? 答案是 想。其实早在 2017 年 8 月份的时候 runc 1.0-rc4 就已经支持 OCI v1.0.0 了。但当时并没有进行正式版发布,转而三个月后, OCI 稍做了更新。

到 2018 年 2 月份的时候,发布了 runc 1.0-rc5 -- "The Final Stretch" 这个版本取名其实已经很明确了 The Final Stretch 已经将这个版本作为正式版本前的最后一个版本进行发布了。

但是,提笔前又发了新版本 runc 1.0-rc6 -- "For Real This Time" ,这个版本在预期中其实是想发布 1.0 的。我们讨论后总结的结论主要有以下几个:

  • 不够规范。一方面是 runc 在持续的迭代改进,另一方面是目前很多其他的运行时实现的一些 hooks 依赖于当前的一些实现,而这些实现,并不完全符合规范。这就造成了一旦修正了这些 “错误” 势必造成其他运行时的不稳定和错误。

  • 发布周期不明确。目前容器相关生态中较为核心的项目,估计就 runc 的发布周期比较 “佛系” 了,我们甚至没有一个明确的发布周期。这次的总结中,我以 Kubernetes 的发布做了例子:

Kubernetes Release Date Cadence
Christening of 1.0 10th July 2015 ~one year from inception
From 1.0 to 1.1 9th November 2015 122 days
From 1.1 to 1.2 16th March 2016 128 days
From 1.2 to 1.3 1st July 2016 107 days
From 1.3 to 1.4 26th September 2016 87 days
From 1.4 to 1.5 12th December 2016 77 days
From 1.5 to 1.6 28th March 2017 106 days
From 1.6 to 1.7 30th June 2017 94 days
From 1.7 to 1.8 28th September 2017 90 days
From 1.8 to 1.9 15th December 2017 78 days
From 1.9 to 1.10 28th March 2018 103 days
From 1.10 to 1.11 3rd July 2018 97 days
From 1.11 to 1.12 ETA 25th September 2018 84 days

以这个发布记录来看的话,每三个月作为以此发布相对合适,也比较通用。

至于这次,runc 1.0-rc6 的发布,将作为特性冻结发布,直到下次发布前,重点都将放在 “符合规范” 上面,同时也给其他的运行时实现充足的时间,用于做好兼容之类的。

总结

以上就是本次关于 runc 1.0-rc6 发布时的一些碎碎念,对这种情况颇有感慨,“符合规范” 并没有那么好做,尤其是做基础支撑的时候。


可以通过下面二维码订阅我的文章公众号【MoeLove】

TheMoeLove