咱們都知道runc是容器runtime的一個實現,那到底什麼是runtime?包含了哪些內容?linux
容器的runtime和image同樣,也有標準,也由OCI (Open Containers Initiative)負責維護,地址爲Runtime Specification,瞭解runtime標準有利於咱們更好的理解docker和runc的關係,本文將對該標準作一個簡單的解釋。git
在Linux平臺上,跟runtime有關的規範主要有四個,分別是Runtime and Lifecycle (runtime.md)、Container Configuration file (config.md)、Linux Container Configuration (config-linux.md)和Linux Runtime (runtime-linux.md) .github
除了這四個以外,還有一個Filesystem Bundle (bundle.md)。redis
下面分別介紹這些規範。docker
在上一篇中,已經用過bundle了,hello-world的bundle看起來是這個樣子的:json
dev@debian:~/images$ tree hello-world-bundle hello-world-bundle ├── config.json └── rootfs └── hello 1 directory, 2 files
bundle中包含了運行容器所須要的全部信息,有了這個bundle後,符合runtime標準的程序(好比runc)就能夠根據bundle啓動容器了。segmentfault
bundle包含一個config.json文件和容器的根文件系統目錄,config.json就是後面要介紹的Container Configuration file
,標準要求該配置文件必須叫這個名字,不過對容器的根文件系統目錄沒有要求,只要在config.json裏面將路徑配置正確就能夠了,不過通常約定俗成都叫rootfs。安全
實際使用過程當中,根文件系統目錄可能在其它的地方,只要config.json裏面配置正確的路徑就能夠了,但若是bundle須要打包和其它人分享的話,必須將根文件系統和config.json打包在一塊兒,而且不包含外層的文件夾。bash
該規範定義了上面介紹的config.json裏面應該包含哪些內容,字段不少,這裏不一一詳細介紹,只簡單說明一下,完整的示例請參考這裏:網絡
ociVersion(必須):對應的OCI標準版本
root(必須):根文件系統的位置
mounts:須要掛載哪些目錄到容器裏面。若是是Linux平臺,這裏面必需要包含/proc、/sys,/dev/pts,/dev/shm這四個目錄
process:容器啓動後執行什麼命令
hostname:容器的主機名,相關原理可參考UTS namespace (CLONE_NEWUTS)
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概述
resources:Cgroup中具體子項的配置,包括device whitelist, memory, cpu, blockIO, hugepageLimits, network(net_cls cgroup和net_prio cgroup), pids
intelRdt:和Intel Resource Director Technology有關
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裏面有合適的設備來和外界通訊。