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": ""
OCI runtime包含runtime,runtime-linux,config,config-linux
- runtime規定了如下內容
- state
ociVersion:創建容器時的OCI版本
- id:容器唯一的ID
- status:容器的runtime狀態,可以為如下值
- creating:容器正在被創建(lifecycle的第2步)
- created:容器完成創建,但沒有返回錯誤且沒有執行用戶程序(lifecycle的第2步之后)
- running:容器正在執行用戶程序且沒有返回錯誤(lifecycle的第5步之后)
- stoped:容器進程退出(lifecycle的第7步)
- pid:host上看到的容器進程
- bundle:host上容器bundle目錄的絕對路徑
- annotation:容器相關的標注,可選
- state
由於runc實現了OCI runtime,使用runc state查看上述busybox可以得到state相關的信息
{ "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": "" }
-
- lifecycle 描述了容器從創建到退出的事件觸發點
- OCI runtime的create調用與bundle的路徑和id相關
- OCI runtime的必須依據config.json中的設置來創建環境,如果無法創建config.json中指定的環境,則返回錯誤。此階段主要創建config.json中的資源,並沒有執行用戶程序。該步驟之后任何多config.json的修改都不會影響容器
- runtime使用容器的唯一id來執行start容器命令
- runtine必須執行 prestart hooks,如果 prestart hooks執行失敗,則返回錯誤,並停止容器,執行第9條操作
- runtime必須執行用戶程序
- runtime必須執行poststart hooks,如果poststart hooks執行失敗,則必須記錄warning日志,而poststart hooks和lifecycle繼續運行
- 容器進程退出,可能由錯誤退出,人為退出,程序崩潰或runtime 執行kill命令引起
- runtime使用容器的唯一id來執行delete容器操作
- 如果在容器創建階段(第2步)沒有完成某些步驟,則容器必須被銷毀
- runtime必須執行poststop hooks,如果poststop hooks執行失敗,則必須記錄warning日志,而poststop hooks和lifecycle繼續運行
- operation runtime必須支持如下操作
- query state:
state <container-id>,參見上述state描述
- create:
create <container-id> <path-to-bundle>,runtime應該提供檢測id唯一性的功能。該操作中會用到config.json除process之外的配置屬性(因為process實在start階段用到的)。實現中可能會與本規范不一致,如在create操作之前實現了pre-create
- start:
start <container-id>,執行config.json的process中定義的程序,如果process沒有設定,則返回錯誤
- kill:
kill <container-id> <signal>,向一個非running狀態的容器發送的信號會被忽略。此操作用於向容器進程發送信號
- delete:
delete <container-id>,嘗試刪除一個非stopped的容器會返回錯誤。容器刪除后其id可能會被后續的容器使用
- query state:
- lifecycle 描述了容器從創建到退出的事件觸發點
# runc delete busybox
cannot delete container busybox that is not stopped: running
-
- hooks:定義了每個操作前后的動作,參見runtime configuration for hooks
- configuration定義了進程運行,環境變量等配置。現有json和go版本的配置,其中go中定義了與平台(linux,solaris,windows相關的tag),如下:
// 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執行的文件
根據平台不同支持如下配置
POSIX process 支持設置POSIX和Linux平台
-
- rlimits:設置進程的資源,如cpu,內存,文件大小等,參見getrlimit。docker里面使用--ulimit來設置單個進程的資源
- type:linux和Solaris
- soft:內核分配給該進程的資源
- hard;可配置的資源的最大值,即soft的最大值。unprivileged進程(沒有CAP_SYS_RESOURCE capability)可以將soft設置為0-hard之間的值
- rlimits:設置進程的資源,如cpu,內存,文件大小等,參見getrlimit。docker里面使用--ulimit來設置單個進程的資源
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掉。(參見linux oom機制分析和oom介紹)
/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路徑
- namespaces,為包含如下參數的數組
-
可以使用resources字段來配置cgroup,注意:只有在需要更新cgroup的時候才配置該字段內容
"cgroupsPath": "/myRuntime/myContainer", "resources": { "memory": { "limit": 100000, "reservation": 200000 }, "devices": [ { "allow": false, "access": "rwm" } ] }
Device whitelist:用於配置設備白名單
- allow (boolean, REQUIRED) -允許
- type (string, OPTIONAL) - 設備類似: a (all), c (char), or b (block). 默認為all
- major, minor (int64, OPTIONAL) - 設備的主次號. 默認all
- access (string, OPTIONAL) - 設備的cgroup權限.r (read), w (write), 和m (mknod).
Memory:具體可以參見cgroup memory
- limit (int64, OPTIONAL) - 設置內存使用limit
- reservation (int64, OPTIONAL) - 設置內存的soft limit
- swap (int64, OPTIONAL) - 設置memory+Swap使用limit
- kernel (int64, OPTIONAL) - 設置內存的hard limit
- kernelTCP (int64, OPTIONAL) - 設置內核TCP buffer的hard limit
- swapness:設置swap的使用比例
- disableOOMKiller:是否開啟oomkiller
CPU:具體可以參見cgroup CPU
- shares (uint64, OPTIONAL) - cgroup中task使用的cpu的相對比例
- quota (int64, OPTIONAL) - 一個period中使用的cpu時間
- period (uint64, OPTIONAL) - 以毫秒為單位的cpu周期 (CFS scheduler only)
- realtimeRuntime (int64, OPTIONAL) - 以毫秒為單位的 cgroup tasks連續使用cpu資源的最長周期
- realtimePeriod (uint64, OPTIONAL) - 實時調度的 period
- cpus (string, OPTIONAL) - CPU列表
- mems (string, OPTIONAL) - memory nodes列表
Block IO:
Huge page limits:
pageSize
:大頁大小- limit:bytes為單位限制的大頁的使用上限
Network:
- classID:cgroup網絡報文的標簽
- priorities
name:網卡名稱
priority:網卡優先級
PIDs:
limit:cgroup限制的pid的數目
RDMA
Sysctl:允許在容器運行過程中修改內核參數
Seccomp:在linux內核中為應用提供了一種沙盒機制。更多參見seccomp
seccomp
defaultAction:seccomp的默認動作,允許值類型為syscalls[].action
architectures:系統調用的平台,如下
SCMP_ARCH_X86
SCMP_ARCH_X86_64
SCMP_ARCH_X32
SCMP_ARCH_ARM
SCMP_ARCH_AARCH64
SCMP_ARCH_MIPS
SCMP_ARCH_MIPS64
SCMP_ARCH_MIPS64N32
SCMP_ARCH_MIPSEL
SCMP_ARCH_MIPSEL64
SCMP_ARCH_MIPSEL64N32
SCMP_ARCH_PPC
SCMP_ARCH_PPC64
SCMP_ARCH_PPC64LE
SCMP_ARCH_S390
SCMP_ARCH_S390X
SCMP_ARCH_PARISC
SCMP_ARCH_PARISC6
syscalls:匹配seccomp的系統調用,該屬性可選
name:系統調用的名稱,至少有一個
action:seccomp的動作規則。libseccomp v2.3.2中如下:
SCMP_ACT_KILL
SCMP_ACT_TRAP
SCMP_ACT_ERRNO
SCMP_ACT_TRACE
SCMP_ACT_ALLOW
args:
index (uint, REQUIRED) - 系統調用的index
value (uint64, REQUIRED) - 系統調用參數的值
valueTwo (uint64, OPTIONAL) - 系統調用參數的值
op (string, REQUIRED) - 系統調用參數的動作。 libseccomp v2.3.2如下
SCMP_CMP_NE
SCMP_CMP_LT
SCMP_CMP_LE
SCMP_CMP_EQ
SCMP_CMP_GE
SCMP_CMP_GT
SCMP_CMP_MASKED_EQ
Rootfs Mount Propagation
rootfsPropagation:設置rootfs的mount Propagation類型,slave,private或shared
-
-
- Linux Runtime:該規范規定了容器文件描述符相關的內容。默認下runtime只會打開stdin, stdout和stderr這3個文件描述符
-
Masked Paths
maskedPaths:容器無法讀取該設置的路徑
"maskedPaths": [
"/proc/kcore" ]
Readonly Paths
readonlyPaths:容器只讀該設置的路徑
-
- POSIX-platform Hooks:支持使用hooks來設置lifecycle中用戶自定義動作
- hooks
- prestart:在用戶程序運行前以及容器命名空間創建后執行,包含如下屬性的數組:
- path:類似execv的路徑,為均對路徑
- args:類似execv的參數
- env:環境變量
- timeout:終端hooks的超時時間
- poststart:在用戶程序執行之后且在start步驟返回前執行,同 prestart的數組一樣
- poststop:在容器刪除之后且在delete步驟返回前執行,同prestart的數組一樣
- prestart:在用戶程序運行前以及容器命名空間創建后執行,包含如下屬性的數組:
- hooks
- Annotations:為key-value類型的任意字符串,如果沒有annotation,該字段可以為空,也可以不存在
- extensibility:遇到無法識別的字段需要返回錯誤
- POSIX-platform Hooks:支持使用hooks來設置lifecycle中用戶自定義動作
TIPS:
- openshift 3.11版本的runc采用的是其自己實現的runtime,位於/usr/libexec/docker/docker-runc-current,實際與runc類似。
參考:
https://cizixs.com/2017/11/05/oci-and-runc/
https://github.com/opencontainers/runtime-spec/blob/master/config.md
https://github.com/opencontainers/runtime-spec/blob/master/specs-go/config.go