“容器運(yùn)行時(shí)”是一個(gè)被過度使用的名詞,。 在 Red Hat,,我們樂意這么說,“容器即 Linux,,Linux 即容器”,。下面解釋一下這種說法。傳統(tǒng)的容器是操作系統(tǒng)中的進(jìn)程,,通常具有如下 3 個(gè)特性: 資源限制 當(dāng)你在系統(tǒng)中運(yùn)行多個(gè)容器時(shí),你肯定不希望某個(gè)容器獨(dú)占系統(tǒng)資源,,所以我們需要使用資源約束來控制 CPU、內(nèi)存和網(wǎng)絡(luò)帶寬等資源,。Linux 內(nèi)核提供了 cgroup 特性,,可以通過配置控制容器進(jìn)程的資源使用。 安全性配置 一般而言,,你不希望你的容器可以攻擊其它容器或甚至攻擊宿主機(jī)系統(tǒng),。我們使用了 Linux 內(nèi)核的若干特性建立安全隔離,相關(guān)特性包括 SELinux,、seccomp 和 capabilities,。 (LCTT 譯注:從 2.2 版本內(nèi)核開始,Linux 將特權(quán)從超級用戶中分離,產(chǎn)生了一系列可以單獨(dú)啟用或關(guān)閉的 capabilities) 虛擬隔離 容器外的任何進(jìn)程對于容器而言都應(yīng)該不可見,。容器應(yīng)該使用獨(dú)立的網(wǎng)絡(luò),。不同的容器對應(yīng)的進(jìn)程應(yīng)該都可以綁定 80 端口。每個(gè)容器的內(nèi)核映像image,、根文件系統(tǒng)rootfs(rootfs)都應(yīng)該相互獨(dú)立,。在 Linux 中,我們使用內(nèi)核的名字空間namespace特性提供虛擬隔離virtual separation,。 那么,,具有安全性配置并且在 cgroup 和名字空間下運(yùn)行的進(jìn)程都可以稱為容器。查看一下 Red Hat Enterprise Linux 7 操作系統(tǒng)中的 PID 1 的進(jìn)程 systemd,,你會發(fā)現(xiàn) systemd 運(yùn)行在一個(gè) cgroup 下,。 ps 命令讓我們看到 systemd 進(jìn)程具有 SELinux 標(biāo)簽: 以及 capabilities: 最后,查看 /proc/1/ns 子目錄,,你會發(fā)現(xiàn) systemd 運(yùn)行所在的名字空間,。 如果 PID 1 進(jìn)程(實(shí)際上每個(gè)系統(tǒng)進(jìn)程)具有資源約束、安全性配置和名字空間,,那么我可以說系統(tǒng)上的每一個(gè)進(jìn)程都運(yùn)行在容器中,。 容器運(yùn)行時(shí)工具也不過是修改了資源約束、安全性配置和名字空間,,然后 Linux 內(nèi)核運(yùn)行起進(jìn)程,。容器啟動(dòng)后,容器運(yùn)行時(shí)可以在容器內(nèi)監(jiān)控 PID 1 進(jìn)程,,也可以監(jiān)控容器的標(biāo)準(zhǔn)輸入/輸出,,從而進(jìn)行容器進(jìn)程的生命周期管理。 容器運(yùn)行時(shí) 你可能自言自語道,,“哦,,systemd 看起來很像一個(gè)容器運(yùn)行時(shí)”。經(jīng)過若干次關(guān)于“為何容器運(yùn)行時(shí)不使用 systemd-nspawn 工具來啟動(dòng)容器”的郵件討論后,,我認(rèn)為值得討論一下容器運(yùn)行時(shí)及其發(fā)展史,。 Docker 通常被稱為容器運(yùn)行時(shí),但“容器運(yùn)行時(shí)container runtime”是一個(gè)被過度使用的詞語,。當(dāng)用戶提到“容器運(yùn)行時(shí)”,,他們其實(shí)提到的是為開發(fā)人員提供便利的上層high-level工具,包括 Docker,,CRI-O 和 RKT,。這些工具都是基于 API 的,涉及操作包括從容器倉庫拉取容器鏡像,、配置存儲和啟動(dòng)容器等,。啟動(dòng)容器通常涉及一個(gè)特殊工具,,用于配置內(nèi)核如何運(yùn)行容器,這類工具也被稱為“容器運(yùn)行時(shí)”,,下文中我將稱其為“底層容器運(yùn)行時(shí)”以作區(qū)分,。像 Docker、CRI-O 這樣的守護(hù)進(jìn)程及形如 Podman,、Buildah 的命令行工具,,似乎更應(yīng)該被稱為“容器管理器”。 早期版本的 Docker 使用 lxc 工具集啟動(dòng)容器,,該工具出現(xiàn)在 systemd-nspawn 之前,。Red Hat 最初試圖將 libvirt (libvirt-lxc)集成到 Docker 中替代 lxc 工具,因?yàn)?RHEL 并不支持 lxc,。libvirt-lxc 也沒有使用 systemd-nspawn,,在那時(shí) systemd 團(tuán)隊(duì)僅將 systemd-nspawn 視為測試工具,不適用于生產(chǎn)環(huán)境,。 與此同時(shí),,包括我的 Red Hat 團(tuán)隊(duì)部分成員在內(nèi)的上游upstream Docker 開發(fā)者,,認(rèn)為應(yīng)該采用 golang 原生的方式啟動(dòng)容器,,而不是調(diào)用外部應(yīng)用。他們的工作促成了 libcontainer 這個(gè) golang 原生庫,,用于啟動(dòng)容器,。Red Hat 工程師更看好該庫的發(fā)展前景,放棄了 libvirt-lxc,。 后來成立 開放容器組織Open Container Initiative(OCI)的部分原因就是人們希望用其它方式啟動(dòng)容器,。傳統(tǒng)的基于名字空間隔離的容器已經(jīng)家喻戶曉,但人們也有虛擬機(jī)級別隔離virtual machine-level isolation的需求,。Intel 和 Hyper.sh 正致力于開發(fā)基于 KVM 隔離的容器,,Microsoft 致力于開發(fā)基于 Windows 的容器。OCI 希望有一份定義容器的標(biāo)準(zhǔn)規(guī)范,,因而產(chǎn)生了 OCI 運(yùn)行時(shí)規(guī)范Runtime Specification,。 OCI 運(yùn)行時(shí)規(guī)范定義了一個(gè) JSON 文件格式,用于描述要運(yùn)行的二進(jìn)制,,如何容器化以及容器根文件系統(tǒng)的位置,。一些工具用于生成符合標(biāo)準(zhǔn)規(guī)范的 JSON 文件,另外的工具用于解析 JSON 文件并在該根文件系統(tǒng)(rootfs)上運(yùn)行容器,。Docker 的部分代碼被抽取出來構(gòu)成了 libcontainer 項(xiàng)目,,該項(xiàng)目被貢獻(xiàn)給 OCI。上游 Docker 工程師及我們自己的工程師創(chuàng)建了一個(gè)新的前端工具,,用于解析符合 OCI 運(yùn)行時(shí)規(guī)范的 JSON 文件,,然后與 libcontainer 交互以便啟動(dòng)容器。這個(gè)前端工具就是 runc,也被貢獻(xiàn)給 OCI,。雖然 runc 可以解析 OCI JSON 文件,,但用戶需要自行生成這些文件。此后,,runc 也成為了最流行的底層容器運(yùn)行時(shí),,基本所有的容器管理工具都支持 runc,包括 CRI-O,、Docker,、Buildah、Podman 和 Cloud Foundry Garden 等,。此后,,其它工具的實(shí)現(xiàn)也參照 OCI 運(yùn)行時(shí)規(guī)范,以便可以運(yùn)行 OCI 兼容的容器,。 Clear Containers 和 Hyper.sh 的 runV 工具都是參照 OCI 運(yùn)行時(shí)規(guī)范運(yùn)行基于 KVM 的容器,,二者將其各自工作合并到一個(gè)名為 Kata 的新項(xiàng)目中。在去年,,Oracle 創(chuàng)建了一個(gè)示例版本的 OCI 運(yùn)行時(shí)工具,,名為 RailCar,使用 Rust 語言編寫,。但該 GitHub 項(xiàng)目已經(jīng)兩個(gè)月沒有更新了,,故無法判斷是否仍在開發(fā)。幾年前,,Vincent Batts 試圖創(chuàng)建一個(gè)名為 nspawn-oci 的工具,,用于解析 OCI 運(yùn)行時(shí)規(guī)范文件并啟動(dòng) systemd-nspawn;但似乎沒有引起大家的注意,而且也不是原生的實(shí)現(xiàn),。 如果有開發(fā)者希望實(shí)現(xiàn)一個(gè)原生的 systemd-nspawn --oci OCI-SPEC.json 并讓 systemd 團(tuán)隊(duì)認(rèn)可和提供支持,,那么CRI-O、Docker 和 Podman 等容器管理工具將可以像使用 runc 和 Clear Container/runV (Kata) 那樣使用這個(gè)新的底層運(yùn)行時(shí),。(目前我的團(tuán)隊(duì)沒有人參與這方面的工作,。) 總結(jié)如下,在 3-4 年前,,上游開發(fā)者打算編寫一個(gè)底層的 golang 工具用于啟動(dòng)容器,,最終這個(gè)工具就是 runc。那時(shí)開發(fā)者有一個(gè)使用 C 編寫的 lxc 工具,,在 runc 開發(fā)后,,他們很快轉(zhuǎn)向 runc。我很確信,,當(dāng)決定構(gòu)建 libcontainer 時(shí),,他們對 systemd-nspawn 或其它非原生(即不使用 golang)的運(yùn)行 namespaces 隔離的容器的方式都不感興趣,。 |
|