简介
在虚拟化领域,Linux逐渐增加了Cgroups、Namespace、Seccomp、capability、Apparmor等一些功能。 Docker重度使用这些特性,而且目前风靡大江南北。实际上,容器技术是一系列晦涩难懂甚至有些神秘的系统特性的集合, 因此Docker公司将这些底层的技术合并在一起,开源出了一个项目runC,并托管于OCI组织。
Linux基金会在2015年6月成立了OCI(Open Container Initiative)组织,旨在围绕容器网格定义和运行时的配置指定一个 开放的工业化标准。该组织主要由Docker、Google、IBM、Microsoft、Red Hat和其他许多合作伙伴创立。
runC是一个轻量级的容器运行引擎,包括所有Docker使用的和容器相关的系统调用的代码,其基本功能点如下:
- 完全支持Linux Namespace,包括User Namespace
- 原生支持所有Linux提供的安全特性:Selinux、Apparmor、Seccomp、control groups、capability、
pivot_root等。 只要是Linux能做的,runC都能做 - 在CRIU项目的支持下原生支持容器热迁移
- 一份正式的容器标准,由Open Container Project管理,并挂靠在Linux基金会下,可以说这是真正的业界标准。
可以这么理解,runC的目标就是去构造到处都可以运行的标准容器。
runC创建容器的流程
输入runc run <container-id>就会根据当前路径下面的config.json文件去创建一个容器。
这里主要来介绍一下runC里面的createContainer流程,首先来看一下函数定义
|
|
createContainer函数的参数列表接收上下文和关于容器的描述spec,然后根据spec描述来配置容器需要的信息, 最后把这些配置信息传递给factory的create方法。factory可以结余很多系统实现,比如Linux、Solaris、Windows、 Unix,这里主要看一下基于Linux的实现。
|
|
这里截取了Create函数实现的一部分, 其实主要工作就是检查容器配置,然后根据目录结构初始化一下容器的root file system, 最后把包含所有信息的struct返回。
容器信息创建完毕,就需要真正创建容器进程了,下面列出创建容器进程的newParentProcess函数。
|
|
可以看到,newProcessProcess函数里面最重要的就是创建容器所属的command信息,下面来仔细看一下它的实现
|
|
这段代码就不多做解释了,看过本书前面的章节应该就能明白,这和前面创建容器初始化进程是相似的流程, 只是多加了一些环境变量和参数。容器创建其实就参考了runC实现。
最后,来看一下最终的start是如何实现的
|
|
至此,就完成了容器的初始化进程启动。下面会再次调用runC的init方法完成容器初始化进程的启动。这个参数在factory_linux.go里面有体现
|
|
在New函数中,可以看到熟悉的/proc/self/exe,后面的参数是init,其实架构和mydocker一样,也会重新运行runC的init方法来初始化容器的进程
代码读到这里,应该可以大概理解runC创建容器的整个过程了,如下:
- 读取配置文件
- 设置rootFileSystem
- 使用factory创建容器, 各个系统平台均有不同实现
- 创建容器的初始化进程process
- 设置容器的输出管道,主要是Go的pipes
- 执行Container.Start()启动物理的容器
- 回调init方法重新初始化进程
- runC父进程等待子进程初始化成功后退出
可以看到,具体的执行流程设计3个概念:process、container、factory
factory用来创建容器,process负责进程之间的通信和启动容器。
附录A 单独启动rootfs
|
|
runc在arm64 麒麟系统中,有个bug:
unable to start container process: error adding pid 2047088 to cgroups: failed to write 2047088: open /sys/fs/cgroup/blkio/system.slice/supervisord/cgroup.procs: no such file or directory
需要改runc的代码,并重新编译,改动分支release-1.1,改动如下:
diff --git a/Makefile b/Makefile
index e3af9bc..0c0a2fd 100644
--- a/Makefile
+++ b/Makefile
@@ -68,15 +68,16 @@ recvtty sd-helper seccompagent:
static:
$(GO_BUILD_STATIC) -o runc .
-releaseall: RELEASE_ARGS := "-a arm64 -a armel -a armhf -a ppc64le -a riscv64 -a s390x"
+releaseall: RELEASE_ARGS := "-a arm64"
releaseall: release
-release: runcimage
+release:
$(CONTAINER_ENGINE) run $(CONTAINER_ENGINE_RUN_FLAGS) \
--rm -v $(CURDIR):/go/src/$(PROJECT) \
-e RELEASE_ARGS=$(RELEASE_ARGS) \
+ --pull never \
$(RUNC_IMAGE) make localrelease
- script/release_sign.sh -S $(GPG_KEYID) -r release/$(VERSION) -v $(VERSION)
+ #script/release_sign.sh -S $(GPG_KEYID) -r release/$(VERSION) -v $(VERSION)
localrelease: verify-changelog
script/release_build.sh -r release/$(VERSION) -v $(VERSION) $(RELEASE_ARGS)
diff --git a/libcontainer/cgroups/utils.go b/libcontainer/cgroups/utils.go
index fc4ae44..e84295d 100644
--- a/libcontainer/cgroups/utils.go
+++ b/libcontainer/cgroups/utils.go
@@ -393,6 +393,10 @@ func WriteCgroupProc(dir string, pid int) error {
return nil
}
+ if err := os.MkdirAll(dir, 0o755); err != nil {
+ return err
+ }
+
file, err := OpenFile(dir, CgroupProcesses, os.O_WRONLY)
if err != nil {
return fmt.Errorf("failed to write %v: %w", pid, err)
编译命令:
# runc_dev-main.tar 使用 make runcimage 命令生成,生成时间较长,一般10h
$ docker load -i runc_dev-main.tar
$ docker tag runc_dev:main runc_dev:release-1-1
$ make releaseall
本文发表于 0001-01-01,最后修改于 0001-01-01。
本站永久域名「 jiavvc.top 」,也可搜索「 极客油画 」找到我。

