linux下runtime文件夹什么用,走进docker(04):什么是容器的runtime?

  • Post author:
  • Post category:linux


我们都知道runc是容器runtime的一个实现,那到底什么是runtime?包含了哪些内容?

容器的runtime和image一样,也有标准,也由OCI (Open Containers Initiative)负责维护,地址为Runtime Specification,了解runtime标准有利于我们更好的理解docker和runc的关系,本文将对该标准做一个简单的解释。

规范内容

下面分别介绍这些规范。

在上一篇中,已经用过bundle了,hello-world的bundle看起来是这个样子的:

dev@debian:~/images$ tree hello-world-bundle

hello-world-bundle

├── config.json

└── rootfs

└── hello

1 directory, 2 files

bundle中包含了运行容器所需要的所有信息,有了这个bundle后,符合runtime标准的程序(比如runc)就可以根据bundle启动容器了。

bundle包含一个config.json文件和容器的根文件系统目录,config.json就是后面要介绍的Container Configuration file,标准要求该配置文件必须叫这个名字,不过对容器的根文件系统目录没有要求,只要在config.json里面将路径配置正确就可以了,不过一般约定俗成都叫rootfs。

实际使用过程中,根文件系统目录可能在其它的地方,只要config.json里面配置正确的路径就可以了,但如果bundle需要打包和其它人分享的话,必须将根文件系统和config.json打包在一起,并且不包含外层的文件夹。

该规范定义了上面介绍的config.json里面应该包含哪些内容,字段很多,这里不一一详细介绍,只简单说明一下,完整的示例请参考这里:

ociVersion(必须):对应的OCI标准版本

root(必须):根文件系统的位置

mounts:需要挂载哪些目录到容器里面。如果是Linux平台,这里面必须要包含/proc、/sys,/dev/pts,/dev/shm这四个目录

process:容器启动后执行什么命令

platform(必须):平台信息,如 amd64 + Linux

linux:Linux平台的特殊配置,这里包含下面要介绍的Linux Container Configuration里面的内容

hooks:配置容器运行生命周期中会调用的hooks,包括prestart、poststart和poststop,容器的生命周期见后面Runtime and Lifecycle介绍。

annotations:容器的注释,相当于容器标签,key:value格式

该规范是Linux平台上对Container Configuration file的扩展,这部分的内容也包含在上面的config.json文件中。

namespaces: namespace相关的配置,相关原理可参考Namespace概述及这些namespace(UTS、IPC、mount、pid、network、user 1、user 2)。

uidMappings,gidMappings:配置主机和容器用户/组之间的对应关系,原理可参考user namespace

devices:设置哪些设备可以在容器内被访问到。除了这里指定的设备外,/dev/null、/dev/zero、/dev/full、/dev/random、/dev/urandom、/dev/tty、/dev/console(如果在process的配置里面启动terminal的话)和/dev/ptmx这些设备默认就能在容器内访问到,即runtime的实现需要默认将这些设备bind到容器内,dev/tty和/dev/ptmx的原理可以参考TTY/PTS概述

cgroupsPath:cgroup的路径,可参考Cgroup概述

sysctl:调整容器运行时的kernel参数,主要是一些网络参数,因为每个network namespace都有自己的协议栈,所以可以修改自己协议栈的参数而不影响别人

seccomp:和安全相关的配置,见Seccomp

rootfsPropagation:设置Propagation类型。可以参考Shared subtrees

maskedPaths:设置容器内的哪些目录对用户不可见

readonlyPaths:设置容器内的哪些目录是只读的

mountLabel:和Selinux有关。

该规范主要定义了跟容器运行时相关的三部分内容,容器的状态、容器相关的操作以及容器的生命周期。

容器的状态

当查询容器的状态时,返回的状态里面至少包含如下信息:

{

“ociVersion”: “0.2.0”,

“id”: “oci-container1”,

“status”: “running”,

“pid”: 4422,

“bundle”: “/containers/redis”,

“annotations”: {

“myKey”: “myValue”

}

}

ociVersion (必须): 创建该容器时使用的OCI runtime的版本

id (必须): 容器ID,本机全局唯一

status (必须): 容器的运行时状态,包含如下状态:

creating: 创建中

created: 创建完成

running: 运行中

stopped: 运行结束

实现runtime时可以包含更多的状态,但不能改变这几个状态的含义

pid (容器是running状态时必须): 容器内第一个进程在系统初始pid namespace中的pid,即在容器外面看到的pid

bundle (REQUIRED): bundle所在位置的绝对路径。bundle里面包含了容器的配置文件和根文件系统。

annotations: 容器的注释,相当于容器标签,来自于容器的配置文件,key:value格式。

容器相关的操作

该部分定义了一个符合runtime标准的实现(如runc)至少需要实现下面这些命令:

state: 返回容器的状态,包含上面介绍的那些内容.

create: 创建容器,这一步执行完成后,容器创建完成,修改bundle中的config.json将不再对已创建的容器产生影响

start: 启动容器,执行config.json中process部分指定的进程

kill: 通过给容器发送信号来停止容器,信号的内容由kill命令的参数指定

delete: 删除容器,如果容器正在运行中,则删除失败。删除操作会删除掉create操作时创建的所有内容。

容器的生命周期

这里以runc为例,说明容器的生命周期

执行命令runc create创建容器,参数中指定bundle的位置以及容器的ID,容器的状态变为creating

runc根据bundle中的config.json,准备好容器运行时需要的环境和资源,但不运行process中指定的进程,这步执行完成之后,表示容器创建成功,修改config.json将不再对创建的容器产生影响,这时容器的状态变成created。

执行命令runc start启动容器

runc执行config.json中配置的prestart钩子

runc执行config.json中process指定的程序,这时容器状态变成了running

runc执行poststart钩子。

容器由于某些原因退出,比如容器中的第一个进程主动退出,挂掉或者被kill掉等。这时容器状态变成了stoped

执行命令runc delete删除容器,这时runc就会删除掉上面第2步所做的所有工作。

runc执行poststop钩子

该规范是Linux平台上对Runtime and Lifecycle的补充,目前该规范很简单,只要求容器运行起来后,里面必须建立下面这些软连接:

# ls -l /dev/fd /dev/std*

lrwxrwxrwx 1 root root 13 May 4 12:32 /dev/fd -> /proc/self/fd

lrwxrwxrwx 1 root root 15 May 4 12:32 /dev/stderr -> /proc/self/fd/2

lrwxrwxrwx 1 root root 15 May 4 12:32 /dev/stdin -> /proc/self/fd/0

lrwxrwxrwx 1 root root 15 May 4 12:32 /dev/stdout -> /proc/self/fd/1

结束语

简单点说,docker负责准备runtime的bundle,而runc负责运行该bundle,并管理容器的整个生命周期。

但对于docker来说,并不是只要准备好根文件系统和配置文件就可以了,比如对于网络,runtime没有做任何要求,只要在config.json中指定network namespace就行了(不指定就新建一个),而至于这个network namespace里面有哪些东西则完全由docker负责,docker需要保证新network namespace里面有合适的设备来和外界通信。

参考