Open Container Initiative(OCI)目前有2个标准:runtime-spec以及image-spec。前者规定了如何运行解压过的filesystem bundle。OCI规定了如何下载OCI镜像并解压到OCI filesystem bundle,这样OCI runtime就可以运行OCI bundle了。OCI(当前)相当于规定了容器的images和runtime的协议,只要实现了OCI的容器就可以实现其兼容性和可移植性。implements中列出了部分OCI标准的实现。本文不讨论windows下的实现,具体参见Open Container Initiative Runtime Specification

system bundle是个目录,用于给runtime提供启动容器必备的配置文件和文件系统。标准的容器bundle包含以下内容:
config.json:该文件包含了容器运行的配置信息,该文件必须存在bundle的根目录,且名字必须为config.json
容器的根目录,可以由config.json中的root.path指定

下面使用runc来运行一个容器,

runc是根据OCI标准生成的一个cli工具。前面两个命令用于提取filesystem,最后一个用于生成config.json,两者组织在一起就是一个filesystem bundle
# mkdir rootfs
# docker export $(docker create busybox) | tar -C rootfs -xvf -
# runc spec

使用runc来运行这个bundle,可以使用state查看该容器的状态

runc run busybox
# runc state busybox
{
“ociVersion”: “1.0.0”,
“id”: “busybox”,
“pid”: 41732,
“status”: “running”,
“bundle”: “/home/test”,
“rootfs”: “/home/test/rootfs”,
“created”: “2018-12-25T14:41:58.82202891Z”,
“owner”: “”
}
# runc delete busybox
cannot delete container busybox that is not stopped: running

OCI runtime包含runtimeruntime-linuxconfigconfig-linux

runtime规定了如下内容

state.json

  1. ociVersion:创建容器时的OCI版本
  2. id:容器唯一的ID
  3. status:容器的runtime状态,可以为如下值
    1. creating:容器正在被创建(lifecycle的第2步)
    2. created:容器完成创建,但没有返回错误且没有执行用户程序(lifecycle的第2步之后)
    3. running:容器正在执行用户程序且没有返回错误(lifecycle的第5步之后)
    4. stoped:容器进程退出(lifecycle的第7步)
  4. pid:host上看到的容器进程
  5. bundle:host上容器bundle目录的绝对路径
  6. annotation:容器相关的标注,可选

    lifecycle 描述了容器从创建到退出的事件触发点

  7. OCI runtime的create调用与bundle的路径和id相关

  8. OCI runtime的必须依据config.json中的设置来创建环境,如果无法创建config.json中指定的环境,则返回错误。此阶段主要创建config.json中的资源,并没有执行用户程序。该步骤之后任何多config.json的修改都不会影响容器
  9. runtime使用容器的唯一id来执行start容器命令
  10. runtine必须执行 prestart hooks,如果 prestart hooks执行失败,则返回错误,并停止容器,执行第9条操作
  11. runtime必须执行用户程序
  12. runtime必须执行poststart hooks,如果poststart hooks执行失败,则必须记录warning日志,而poststart hooks和lifecycle继续运行
  13. 容器进程退出,可能由错误退出,人为退出,程序崩溃或runtime 执行kill命令引起
  14. runtime使用容器的唯一id来执行delete容器操作
  15. 如果在容器创建阶段(第2步)没有完成某些步骤,则容器必须被销毁
  16. runtime必须执行poststop hooks,如果poststop hooks执行失败,则必须记录warning日志,而poststop hooks和lifecycle继续运行

    operation runtime必须支持如下操作

  17. query state:state ,参见上述state描述

  18. create:create ,runtime应该提供检测id唯一性的功能。该操作中会用到config.json除process之外的配置属性(因为process实在start阶段用到的)。实现中可能会与本规范不一致,如在create操作之前实现了pre-create
  19. start:start ,执行config.json的process中定义的程序,如果process没有设定,则返回错误
  20. kill:kill ,向一个非running状态的容器发送的信号会被忽略。此操作用于向容器进程发送信号
  21. delete:delete ,尝试删除一个非stopped的容器会返回错误。容器删除后其id可能会被后续的容器使用

configuration定义了进程运行,环境变量等配置。现有jsongo版本的配置,其中go中定义了与平台(linux,solaris,windows相关的tag),如下:

OCI runtime - 图1
// Linux is platform-specific configuration for Linux based containers.
Linux Linux json:"linux,omitempty" platform:"linux"
// Solaris is platform-specific configuration for Solaris based containers.
Solaris
Solaris json:"solaris,omitempty" platform:"solaris"
// Windows is platform-specific configuration for Windows based containers.
Windows Windows json:"windows,omitempty" platform:"windows"
// VM specifies configuration for virtual-machine-based containers.
VM
VM json:"vm,omitempty" platform:"vm"

Specification version:必选,指定了bundle使用的OCI的版本

root:

  • path:容器的bundle路径,可以是相对路径和绝对路径,该值通常为rootfs
  • readonly:当设置为true时,容器的根文件为只读,默认false

    mount:按照配置的顺序进行挂载

  • destination:容器中的挂载点,必须是绝对路径

  • source:挂载的设备名称,文件或目录名称(bind mount时),当option中有bind或rbind时改mount类型为bind mount
  • option:mount的选项,参见mount

    process:定义了容器的进程信息

  • terminal:默认false,为true时,linux系统会为该进程分配一个pseudoterminal(pts),并使用标准输入输出流

  • consoleSize:指定terminal的长宽规格,width和height
  • cwd:执行命令的绝对路径
  • env:环境变量
  • args:命令参数,至少需要指定一个参数,首参数即被execvp执行的文件

process根据平台不同支持如下配置
POSIX process 支持设置POSIX和Linux平台

  • rlimits:设置进程的资源,如cpu,内存,文件大小等,参见getrlimit。docker里面使用—ulimit来设置单个进程的资源
    • type:linux和Solaris
    • soft:内核分配给该进程的资源
    • hard;可配置的资源的最大值,即soft的最大值。unprivileged进程(没有CAP_SYS_RESOURCE capability)可以将soft设置为0-hard之间的值

Linux process:

  • apparmorProfile:指定进程的apparmor文件
  • capabilities:指定进程的capabilities
  • noNewPrivileges:设置为true后可以防止进程获取额外的权限(如使得suid和文件capabilities失效),该标记位在内核4.10版本之后可以在/proc/$pid/status中查看NoNewPrivs的设置值。更多参见no_new_privs
  • oomScoreAdj :给进程设置oom_score_adj值,进程的oom涉及以下3个文件,oom_adj和oom_score_adj功能类似,oom_adj主要用于兼容老版本,oomScoreAdj的功能就是设置/proc/$PID/oom_score_adj中的值(范围-1000~1000),系统通过该值和oom_score来决定kill进程的优先级。oom_score为只读文件,oom通过对系统所有进程的oom_score进行排序,值越大,越可能在内存不足时被kill掉。

/proc/$PID/oom_adj
/proc/$PID/oom_score
/proc/$PID/oom_score_adj
可以通过如下命令查看系统所有进程的oom_score
ps -eo pid,comm,pmem —sort -rss | awk ‘{“cat /proc/“$1”/oom_score” | getline oom; print $0”\t”oom}’

  • selinuxLabel :设置进程的SELinux 标签,即MAC值
  • user 用于控制运行进程的用户
    • uid:指定容器命名空间的user id
    • gid:指定容器命名空间的group id
    • additionalGids:指定容器命名空间中附加的group id

hostname:指定容器进程看到的hostname

Platform-specific configuration:包含在linux,Windows,solaris,vm等host平台上使用namespaces,cgroup等。下面以linux为例

  • Default Filesystems:如下路径需要正确挂载到容器中,以便容器进程的正确执行

Path Type
/proc proc
/sys sysfs
/dev/pts devpts
/dev/shm tmpfs

  • namespaces,为包含如下参数的数组
    • type:指定namespace类型,为ipc,mount,user,network,uts,pid,cgroup,如果没有指定namespace type,则继承父namespace的属性
    • path:namespace的文件,如果没有指定,则生成一个新的namespace
  • User namespace mappings,uidMappings和gidMappings指定了user和group从host到容器的映射关系,为结构图数组,包含containerid,hostid和size这3个属性
  • device:列出了必须在容器中存在的设备,为结构体数组,有如下属性
    • type:设备的类型
    • path:容器中的全路径
    • major, minor:设备的主设备号和次设备号,主设备号表示类型,次设备号表示分区,可以使用”ls -al /dev”查看主次设备号。设置可以参见device
    • fileMode:文件ADC访问权限
    • uid:容器中设备的uid
    • gid:容器中设备的gid
  • cgroup:用于控制容器的资源以及设备接入等。
    • Cgroups Path:cgroup的路径,该路径可以是绝对路径,也可以是相对路径。如果没有设置该值,cgroup会使用默认的cgroup路径,可以使用resources字段来配置cgroup,注意:只有在需要更新cgroup的时候才配置该字段内容

“cgroupsPath”: “/myRuntime/myContainer”,
“resources”: {
“memory”: {
“limit”: 100000,
“reservation”: 200000
},
“devices”: [
{
“allow”: false,
“access”: “rwm”
}
]
}

  • Rootfs Mount Propagatio。rootfsPropagation:设置rootfs的mount Propagation类型,slave,private或shared
  • Linux Runtime:该规范规定了容器文件描述符相关的内容。默认下runtime只会打开stdin, stdout和stderr这3个文件描述符
  • Masked Paths:maskedPaths:容器无法读取该设置的路径

    “maskedPaths”: [
    “/proc/kcore”
    ]

  • Readonly Paths:readonlyPaths:容器只读该设置的路径