當前位置:
首頁 > 科技 > 容器技術演化史

容器技術演化史

接收程序員的 8 點技術早餐

作者|Daniel J Walsh

譯者|金靈傑

在過去幾年中,容器技術不僅僅在開發者中成為熱門話題,許多企業也參與其中。這種對容器興趣的日益增加,使得對其安全提升和加固的需求不斷提升,同時也對可擴展性和互操作性有了更高的要求。這些工作都是大工程,本文介紹了紅帽在企業級容器支持上所做的工作。

寫在前面

當我在 2013 年秋季首次遇到 Docker 公司(Docker.io)的代表時,我們還在研究如何在紅帽企業版(Red Hat Enterprise Linux,RHEL)中使用 Docker 容器。(現在 Docker 項目的一部分已經被改名為 Moby。)在將這項技術融入到 RHEL 時,我們遇到了許多問題。首先遇到的最大障礙是找到一個支持寫時複製(Copy On Write,COW)的文件系統來處理容器鏡像分層。紅帽最終為包括 Device Mapper、btrfs 和最初版本的 OverlayFS 貢獻了部分 COW 實現。對於 RHEL,我們默認使用 Device Mapper,雖然當時對 OverlayFS 的支持已經快完成了。

另一個大障礙是啟動容器的工具。當時,上游 docker 使用 LXC 工具來啟動容器,但是我們不希望在 RHEL 中支持 LXC 工具集。在與上游 docker 公司合作之前,我和 libvirt 團隊合作,製作了名為 virt-sandbox 的工具,它能夠通過 libvirt-lxc 來啟動容器。

當時,紅帽公司一些人認為,將 LXC 工具集移除,在 Docker 守護進程和 libvirt 之間通過 libvirt-lxc 來橋接啟動容器是個不錯的想法。但是這種實現方式令人擔憂。使用該方式通過 Docker 客戶端(docker-cli)來啟動容器,客戶端和容器進程(pid1OfContainer)之間的調用層次有:

我不太贊同啟動容器的工具和實際運行容器之間有兩個守護進程。

我的團隊開始和上游 docker 開發者一起,通過原生 Go 語言實現了一個名為容器運行時庫:libcontainer。該庫最終作為 OCI 運行時規範 runc 的初始實現發布。

有許多人誤以為當他們執行一個容器的時候,容器進程是 docker-cli 的子進程。事實上,docker 是一個典型的 C/S 架構,容器進程運行在一個完全獨立的環境中。這樣的 C/S 架構,可能導致不穩定和潛在的安全隱患,另外還會導致一些系統特性無法使用。例如,systemd 有一個稱為套接字激活(socket activation)的特性,用戶可以設置守護進程僅在進程連接到套接字時才運行。這樣可以降低系統內存使用,讓服務按需運行。套接字激活的實現原理是 systemd 監聽一個 TCP 套接字,當該套接字接收到數據包的時候,systemd 激活需要監聽該套接字的服務。當這個服務激活之後,systemd 將套接字轉交給新啟動的守護進程。將這個守護進程放到基於 Docker 的容器中之後,就會產生問題。通過在 systemd 的 unit 文件中使用 Docker 客戶端命令可以啟動容器,但是 systemd 無法簡單的通過 Docker 客戶端命令將套接字轉移到守護進程。

類似這樣的問題,讓我們意識到,有必要換一種運行容器的方式。

容器編排問題

上游 docker 項目致力於讓容器使用變得更加方便,它一直是學習 Linux 容器的好工具。我們可以通過簡單的使用類似 docker run -ti fedora sh 命令來啟動容器並進入容器中。

容器的真正優勢在於同時啟動大量組件,並將它們組裝成一個強大的應用程序。設置一個多容器應用的難點在於其複雜度的指數級增長,以及通過簡單的 docker 命令將分散的各個部分串聯起來。如何在資源有限的集群節點中管理容器布局?如何管理這些容器的生命周期?類似問題還有很多。

在第一屆 DockerCon 上,至少有 7 個公司 / 開源項目展示了容器編排的方式。我們演示的是紅帽 OpenShift 的項目 geard,它鬆散的基於 OpenShift v2 容器。紅帽決定我們需要在容器編排上重新審視,也可能和其他開源社區的人員合作。

Google 演示的是 Kubernetes 容器編排工具,它是基於 Google 內部開發的編排工具所積累的經驗。OpenShift 決定放棄 Gear 項目,開始和 Google 一起合作 Kubernetes。Kubernetes 目前已經稱為 GitHub 上最大的社區項目之一。

Kubernetes

Kubernetes 最初使用 Google 的 lmctfy 作為其容器運行時庫。2014 年夏天,lmctfy 被移植支持 Docker。Kubernetes 會在集群的每個節點運行一個名為 kubelet 的守護進程。這意味著原始的 Kubernetes 和 Docker 1.8 工作流程看上去是這樣的:

這又回到了兩個守護進程的模式。

還有更糟糕的。每次 Docker 發布,Kubernetes 都會被破壞。Docker 1.10 修改了後端存儲,導致所有鏡像都需要重新構建。Docker 1.11 開始通過 runc 來啟動容器:

Docker 1.12 增加了一個容器守護進程來啟動容器。這樣的主要目的是滿足 Docker Swarm(一個 Kubernetes 的競爭對手)的需求:

前面提到了,每次 Docker 發布新版本都會破壞 Kubernetes 的功能,因此對於 Kubernetes 和 OpenShift 等工具都需要適配舊版本 Docker。

現在,已經進化成三守護進程系統,這種架構下守護進程中的任何一部分出錯了,整個系統都會分崩離析。

走向容器標準化

CoreOS、rkt 其他運行時環境

由於 Docker 運行時庫的各種問題,多個組織都在尋求其替代方案。其中一個組織是 CoreOS。CoreOS 為上游 docker 提供了一個替代的容器運行時環境 rkt(rocket)。同時,CoreOS 還引入了一個標準容器規範:appc(App Container)。簡單的說,他們希望大家在處理容器鏡像存儲的時候可以使用統一的標準規範。

這是一個對社區的警示。在剛開始和上游 docker 合作容器項目的時候,我最大的擔憂是最終會有多個標準。我不希望會出現類似 RPM 格式和 Debian 格式(deb)之間的戰爭,這場戰爭影響了後來 20 年的 Linux 軟體分發。appc 提出的一個利好是它說服了上游 docker 和開源社區一起建立一個標準體:Open Container Initiative(OCI)。

OCI 一直在制定兩種規範:

OCI 運行時規範:OCI 運行時規範「旨在定義配置、執行環境和容器的生命周期。」它定義了容器在硬碟上存儲的方式,用於描述容器中應用程序的 JSON 文件和如何創建和運行容器。上游 docker 貢獻了 libcontainer,並且提供了 runc 作為 OCI 運行時規範的默認實現。

OCI 鏡像格式規範:鏡像格式規範基本上基於上游 docker 鏡像格式,同時定義了容器鏡像在容器倉庫中的格式。該標準允許應用程序開發者將他們的應用程序標準化成同樣的格式。appc 規範描述的一些特性已經合併如 OCI 鏡像格式規範。這兩個 OCI 規範都已經接近 1.0 正式版本。(1.0 正式版本已經於 2017 年 7 月 20 日發布,譯者注)上游 docker 已經統一在其正式發布之後支持 OCI 鏡像規範。rkt 現在同時支持 OCI 鏡像和傳統 docker 鏡像格式。

通過提供容器鏡像和運行時的行業規範,OCI 幫助了容器相關工具和編排框架上的革新。

抽象運行時介面

Kubernetes 編排工具是這些標準的受益者之一。作為 Kubernetes 的一大支持者,CoreOS 提交了一系列的補丁為 Kubernetes 增加了通過 rkt 運行容器的支持。Google 和 Kubernetes 社區發現應用了這些補丁之後,將來要為 Kubernetes 增加新的容器運行時支持會使得 Kubernetes 代碼變得複雜和臃腫。因此 Kubernetes 團隊決定實現一套名為容器運行時介面(Container Runtime Interface,CRI)的 API 協議規範。然後他們重構了 Kubernetes,使其直接調用 CRI,而不用調用 Docker 引擎。如果以後需要添加新的容器運行時介面,只需要實現 CRI 的服務端介面即可。同時,Kubernetes 為 CRI 開發者提供了大量測試集用來驗證這些實現是否兼容 Kubernetes。CRI 的抽象,還有一項需要持續進行工作:將原先 Kubernetes 中對 Docker 引擎的直接調用去除,移動到稱為 docker-shim 的適配層中去。

容器工具創新

鏡像倉庫創新——skopeo

幾年前我們在 Atomic 項目中開發 atomic 命令行介面。其中一個功能是,我們希望能夠有一個工具,當鏡像還在鏡像倉庫的時候,就可以驗證其中的內容。當時,唯一的變通方式是通過容器鏡像關聯的 JSON 文件將鏡像拉取到本地,然後再通過 docker inspect 命令來讀取鏡像信息。這些鏡像可能非常大,佔用上 G 空間。有了這項功能,就能夠讓用戶能夠提前檢查鏡像內容,以確定是否需要拉取鏡像,因此我們為 docker inspect 命令增加了一個 --remote 的參數。但是上游 docker 拒絕了這個 pull request,原因是他們不希望將 Docker 命令行介面弄的複雜,並且用戶可以通過自己的小工具來實現同樣的功能。

由 Antonio Murdaca 領導的我們團隊,最終完成了名為 skopeo 的工具。Antonio 將這個工具定位不僅局限於拉取鏡像的配置文件,他還實現了將容器鏡像從鏡像倉庫拉取到本地伺服器,從本地伺服器推送鏡像到鏡像倉庫的雙向協議。

目前,skopeo 已經重度融入在 atomic 命令行介面中,包括檢查容器更新、集成入 atomic scan 命令實現中等。Atomic 也使用 skopeo 來拉取和推送鏡像,而非使用上游的 docker 守護進程。

Containers/image 庫

我們一直在和 CoreOS 溝通關於在 rkt 中使用 skopeo 的可行性,但是對方一直不願意通過一個可以執行的幫助應用程序,而是希望能夠將 skopeo 功能作為依賴庫直接集成進去。因此我們決定將 skopeo 拆分成庫和可執行程序,最終創建了 image 工程。

containers/image 庫和 skopeo 已經被一些上游項目和雲服務基礎設施工具使用。二者已經能夠支持除 Docker 外的其他多種存儲後端,並且它們提供了諸如在鏡像倉庫之間移動鏡像等許多特性。skopeo 的一個優勢在於,它的工作不依賴任何守護進程。containers/image 庫的突破,也讓類似容器鏡像簽名等增強功能的實現成為了可能。

鏡像處理和掃描的創新

前文提到了 atomic 命令行介面,該工具用來實現 docker 命令行介面不兼容的特性和一些我們覺得上游 docker 不會接受的特性。另外我們還希望它能夠可擴展的支持其他容器運行時、工具和存儲。前面提到的 skopeo 就驗證了這點。

其中一個我們想加入到 atomic 的特性是 atomic mount。該命令的需求來源是能夠從 Docker 鏡像存儲(上游 docker 稱之為圖驅動,graph driver)獲取數據,並將其掛載到某個地方,這樣可以使用其他工具來檢查這個鏡像。目前如果使用原生 docker,查看鏡像內容的唯一方式是啟動容器。如果鏡像中包含不受信內容,僅僅為了查看鏡像內容而運行其代碼是非常危險的。啟動容器然後驗證內容的另外一個問題是,用於檢查的工具通常不會包含在容器鏡像中。

大部分容器鏡像掃描工具的工作流程通常如下:它們連接到 Docker 套接字,執行 docker save 命令創建一個壓縮包,然後將其解壓縮到硬碟上,最後檢查這些內容。這個操作流程很慢。

有了 atomic mount 命令,我們可以直接通過 Docker 圖驅動(graph driver)來掛載鏡像。如果 Docker 守護進程使用設備映射器(device mapper),它可以掛載這個設備;如果使用的是 overlay 文件系統,它可以掛載 overlay。這樣可以快速的滿足我們的需求。現在只需要這樣做:

然後就可以開始檢查內容。當檢查完畢之後,執行:

我們將該特性融入到了 atomic scan 命令中,這樣就可以構建一個快速鏡像掃描儀了。

工具協調問題

atomic mount 命令使用過程中一個大問題是它獨立工作。Docker 守護進程無法感知到還有其他進程在使用鏡像。這可能導致一些問題(例如,有人首先用前面提到的命令掛載 Fedora 鏡像,然後其他人執行了 docker rmi fedora 命令,Docker 守護進程在試圖刪除 Fedora 鏡像的時候會因為設備忙而失敗)。此時 Docker 守護進程會進入不可預知的狀態。

Container/storage 庫

為了解決這個問題,我們開始將上游 docker daemon 代碼中圖驅動相關代碼拉取到自己的倉庫。Docker 守護進程的圖驅動將所有鎖相關操作都在自己的內存中完成。我們希望能夠將鎖相關實現移動到文件系統中,這樣不同的進程能夠同時操作容器存儲,而無需都通過守護進程這個單點。

最終該項目被命名為 container/storage,它實現了容器在運行、構建和存儲時所需要的所有寫時複製(COW)特性,而無需使用一個進程來控制和監控(即不需要守護進程)。現在 skopeo 和其他一些工具和項目都能夠更好的使用存儲。還有一些其他開源項目開始使用 containers/storage 庫,在某個時間點我們希望這個項目能夠合併回上游的 docker 項目。

啟航,進行創新

下面讓我們來看下 Kubernetes 在一個節點上通過 Docker 守護進程運行一個容器時,到底發上了什麼。首先 Kubernetes 執行一個命令:

該命令告訴 kubelet 在節點上運行 nginx 應用程序。kubelet 調用容器運行時介面,要求其啟動 nginx 應用程序。此時,容器運行時介面的實現需要完成以下步驟:

檢查本地存儲中名為 nginx 的鏡像。如果本地不存在,容器運行時將會在鏡像倉庫中查找對應的鏡像。

如果鏡像在本地存儲中不存在,將其從鏡像倉庫下載到本地系統中。

將下載的容器鏡像釋放到容器存儲中(通常是一個寫時複製存儲),並掛載到適當位置。

使用標準化的容器運行時來運行容器。

讓我們來看看上述過程中依賴的特性:

OCI 鏡像格式:用於定義鏡像在倉庫中的標準存儲格式。

Containers/image 庫:用於實現從鏡像倉庫拉取鏡像所需要的所有特性。

Containers/storage 庫:提供 OCI 鏡像格式解壓縮到寫時複製存儲上所需要的功能。

OCI 運行時規範和 runc:提供運行容器所需要的工具(和 Docker 守護進程用來運行容器的工具相同)。

這意味著我們可以使用這些工具和庫實現使用容器所需要的能力,而無需依賴一個大型容器守護進程。

在一個中等到大型規模基於 DevOps 的持續集成 / 持續交付環境中,效率、速度和安全性是非常重要的。而一旦相關的工具符合 OCI 規範,那麼開發和運維就能夠在持續集成 / 持續交付管道和生產環境中使用最佳的工具。大部分容器操作工具都隱藏在容器編排框架或者其他上層容器平台技術之下。可以預見到未來容器運行時和鏡像工具的選擇會成為容器平台的一個安裝選項。

系統(獨立)容器

Atomic 項目中,我們引入了 atomic host,這是一個構建操作系統的新方式,其中的軟體可以「原子的」更新,並且大部分應用程序都以容器的方式運行。使用該平台的目的是為了證明大部分軟體能夠被移植到 OCI 鏡像格式,並能夠使用標準的協議從鏡像倉庫下載並安裝到系統中。將軟體以容器鏡像的方式提供,可以讓用戶的操作系統和應用軟體以不同的速度進行更新。傳統的 RPM/yum/DNF 方式分發軟體包會將應用程序版本限定在主機操作系統的生命周期中。

這種將基礎設置作為容器來分發的一個問題是,有的時候應用程序需要在容器運行時守護進程啟動前執行。讓我們來看一個使用 Docker 守護進程的 Kubernetes 例子:Kubernetes 在啟動前需要先完成網路設置,這樣它才能夠將 pods 分配到獨立的網路環境中。該場景下目前我們使用的默認守護進程是 flanneld,它必須在 Docker 守護進程啟動之前運行,以設置 Docker 守護進程的網路介面。同時,flanneld 使用 etcd 作為數據存儲。該守護進程需要在 flanneld 之前啟動。

如果我們將 etcd 和 flanneld 通過鏡像分發,就會遇到先有雞還是先有蛋的問題。在啟動容器化應用程序之前需要一個容器運行時守護進程,但是這些應用程序又需要在容器運行時守護進程之前啟動。為了解決這個問題,我找到了一些 hack 的方式,但是沒有一種方式是徹底的。同時,Docker 守護進程目前還沒有一個像樣的方式來設置容器啟動的優先順序。我看見過這方面的建議,但是目前的實現都是使用類似於老的 SysVInit 方式來啟動服務(當然我也知道這帶來的複雜性。)

systemd

使用 systemd 來替代 SysVInit 的一個原因是用來處理啟動服務的優先順序和順序,為什麼容器不能利用這項技術呢?在 Atomic 項目中,我們決定在主機上運行容器的時候不再需要容器運行時守護進程,尤其是那些啟動早期需要的服務。因此我們增強了 atomic 命令行介面,允許用戶安裝容器鏡像。當用戶執行 atomic install --system etcd 時,atomic 會使用 skopeo 從鏡像倉庫拉取 etcd 的 OCI 鏡像,然後將鏡像釋放到 OSTree 存儲中。因為我們在生產環境中運行 etcd,因此該鏡像是只讀的。下一步 atomic 命令從容器鏡像中抓取 systemd 的 unit 文件模板,並在硬碟上創建 unit 文件用以啟動鏡像。該 unit 文件通常使用 runc 在主機上啟動容器(雖然 runc 不是必須的)。

如果運行 atomic install --system flanneld 命令會發生類似的事情,除了這次生成 flanneld 的 unit 文件將會指定 etcd 需要在它啟動前運行。

當系統啟動時,systemd 確保 etcd 會在 flanneld 之前運行,並且容器運行時會在 flanneld 啟動之後再運行。該特性可以讓用戶將 Docker 守護進程和 Kubernetes 放到系統容器中。這意味著用戶可以啟動一個 atomic host 或者傳統基於 rpm 的操作系統,其中的整個容器編排框架以容器的方式運行。這項功能非常強大,因為我們知道客戶希望能夠獨立於這些組件,持續給他們的容器主機打補丁。此外,它能夠將主機操作系統佔用空間最小化。

當然,關於將傳統應用程序放到容器中使其既能夠作為獨立 / 系統容器運行,又能夠成為可編排容器仍然有所爭論。考慮一個 Apache 容器,我們可以使用 atomic install --system httpd 命令安裝。這個容器鏡像可以和基於 rpm 的 httpd 服務一樣的啟動(systemctl start httpd,除了 httpd 進程將會啟動在容器中)。存儲仍然可以使用本地的,意味著可以將主機的 /var/www 目錄掛載到容器中,這個容器也監聽這本地網路的 80 埠。這表明我們可以在主機的容器中運行傳統工作負載,而無需一個容器運行時守護進程。

構建容器鏡像

在我看來,最近 4 年來容器創新中最令人悲傷的事情,就是構建容器鏡像構建機制缺乏創新。一個容器鏡像是一個由鏡像內容的壓縮包和一些 JSON 文件組成的壓縮包。容器的基礎鏡像是一個完成的根文件系統和一些描述用的 JSON 文件。然後用戶可以在上面增加層,每個增加的層會形成一個壓縮包和記錄變化的 JSON 文件。這些層和基礎鏡像一起打包組合成了容器鏡像。

基本上所有人都通過 docker build 和 Dockerfile 格式文件來構建鏡像。上游 docker 在幾年前就已經停止接受修改和增強 Dockerfile 格式和構建方式的 pull request。在容器進化過程中,Dockerfile 扮演了重要的角色。開發和運維可以簡單直接的方式構建鏡像。然而在我看來,Dockerfile 只是一個簡化的 bash 腳本,到目前為止還有好多問題沒有解決。例如:

為了構建容器鏡像,Dockerfile 必須依賴 Docker 守護進程

目前為止還沒有人創建出標準的工具,可以獨立於 Docker 命令來創建 OCI 格式鏡像。

諸如 ansible-containers 和 OpenShift S2I(Source2Image)等工具,仍然在底層使用了 Docker 引擎。

Dockerfile 中的每一行都會創建一個新的鏡像,這能夠提升開發階段構建鏡像的效率,因為工具可以知道 Dockerfile 中的每一行是否有修改,如果沒有修改,之前構建的鏡像可以被複用而無需重新構建。但是這會導致產生大量的層。

因為這個問題許多人都要求提供一個合併層的方式以限制層的數量。最終上游 docker 已經接受了部分建議滿足了該需求。

為了能夠從安全的站點拉取鏡像內容,通常用戶需要一些安全措施。例如用戶需要有 RHEL 認證和訂閱授權才能夠在鏡像中增加 RHEL 內容。

這些密鑰最終會保存在鏡像的層中,開發者需要在這些層中移除它們。

為了能夠在構建鏡像的時候能夠掛載卷,我們在自己分發的 atomic 項目和 docker 包中增加了 -v 參數,但是上游 docker 沒有接受這些補丁。

構建出來的組件最終被放置在容器鏡像中。因此,雖然 Dockerfile 對於剛開始構建的開發者,可以更好的了解整個構建過程,但是這對於大規模企業環境來說不是一個高效的方式。另外,在使用自動化容器平台之後,用戶不會關注構建 OCI 標準鏡像的方式是否高效。

出發吧 buildah

在 2017 年 DevConf.cz 上,我讓我們團隊的 Nalin Dahyabhai 看看稱之為 containers-coreutils 的構建工具,本質上這是一個使用了 containers/storage 庫和 containers/image 庫的一系列命令行工具,它們可以模仿 Dockerfile 的語法。Nalin 將其命令為 buildah,用來取笑我的波士頓口音。使用一些 buildah 原語,我們就可以構建一個容器鏡像:

關於安全性的一個主要思想是確保操作系統的鏡像儘可能小,以限制不需要的工具。該想法的來源是黑客需要依賴工具來攻破應用程序,如果注入 gcc、make、dnf 等工具不存在,攻擊可能會停止或者受到限制。

另外,由於這些鏡像的拉取和推送都需要經過互聯網,縮小鏡像尺寸總是沒錯的。

Docker 構建鏡像的主要方式是通過命令將軟體安裝或者編譯到容器的 buildroot 中。

執行 run 命令需要這些可執行程序包含在容器鏡像中。例如在鏡像中使用 dnf 命令需要安裝整個 Python 棧,即使最終的應用程序不會使用 Python。

ctr=$(buildah from fedora):

使用 containers/image 庫從鏡像倉庫拉取 Fedora 鏡像

返回一個容器 ID(ctr)

mnt=$(buildah mount $ctr):

掛載剛創建的容器鏡像($ctr)。

返回掛載點路徑。

現在可以使用這個掛載點寫入內容。

dnf install httpd –installroot=$mnt:

我們可以使用主機系統上的命令將內容寫入到容器中,這意味著我們可以把密鑰放在主機上,而不用放入到容器中,另外構建工具也可以保存在主機上。

dnf 等命令也不用事先安裝到容器中,Python 等依賴也是,除非應用程序依賴。

cp foobar $mnt/dir:

我們可以使用 bash 支持的任何命令來填充容器。

buildah commit $ctr:

我們可以在需要的時候創建層。層的創建由用戶控制而不是工具。

buildah config --env container=oci --entrypoint /usr/bin/httpd $ctr:

在 Dockerfile 中支持的命令也可以指定。

buildah run $ctr dnf -y install httpd:

buildah 同樣支持 run 命令,它不依賴容器運行時守護進程,而是通過執行 runc 直接在鎖定的容器中執行命令。

buildah build-using-dockerfile -f Dockerfile .:

buildah 同樣支持使用 Dockerfile 來構建鏡像。

我們希望將類似 ansible-containers 和 OpenShift S2I 這樣的工具改成 buildah,而非依賴一個容器運行時守護進程。

構建容器鏡像和運行容器採用相同的運行時環境,對於生產環境還會遇到一個大問題,既針對容器安全需要同時滿足二者的許可權需求。通常構建容器鏡像的時候需要遠多於運行容器所需要的許可權。例如,默認情況下我們會允許 mknod 能力。mknod 能力允許進程能夠創建設備節點,但是在生產環境幾乎沒有應用程序需要這個能力。在生產環境中移除 mknod 能力能夠讓系統變得更加安全。

另一個例子是我們會默認給容器鏡像以讀寫許可權,因為安裝進程需要將軟體包安裝到 /usr 目錄中。但是在生產環境中,我個人建議應該將所有容器運行在只讀模式。容器中的進程應該只能允許寫入 tmpfs 或者掛載到容器內的卷上。通過將構建鏡像和運行容器分離,我們可以修改這些默認設置,讓運行環境更加安全。

Kubernetes 的運行時抽象:CRI-O

Kubernetes 增加了一套用於插拔任何 pods 運行時的 API,稱為容器運行時介面(Container Runtime Interface,CRI)。我不願意在我的系統上運行太多的守護進程,但是我們基於此增加了一種。由我們團隊中 Mrunal Patel 領導的小組,在 2016 年晚些時候開始實現 CRI-O 守護進程。這是一個用於運行基於 OCI 應用程序的容器運行時介面守護進程。理論上,未來我們可以將 CRI-O 代碼直接編譯如 kubelet,以減少一個守護進程。

和其他容器運行時不同的是,CRI-O 的唯一目的是滿足 Kubernetes 的需求。回憶一下前面提到的關於 Kubernetes 運行一個容器所需要的步驟。

Kubernetes 向 kubelet 發送一個消息,通知它需要啟動一個 nginx 服務:

kubelet 調用 CRI-O 告知其運行 nginx。

CRI-O 響應 CRI 請求。

CRI-O 在鏡像倉庫中找到對應的 OCI 鏡像。

CRI-O 使用 containers/image 將鏡像從倉庫拉取到本地。

CRI-O 使用 containers/storage 將鏡像解壓到本地存儲中。

CRI-O 使用 OCI 運行時規範啟動容器,通常使用 runc。我前面提到過,這和 Docker 守護進程使用 runc 來啟動容器的方式相同。

如果需要,kubelet 也可以使用其他的運行時來啟動容器,例如 Clear Containers 的 runv

CRI-O 旨在成為運行 Kubernetes 的穩定平台,我們只會在通過了所有 Kubernetes 的測試集之後才會發布新的版本。所有提交到 https://github.com/Kubernetes-incubator/cri-o 上的 pull request 都需要運行整個 Kubernetes 測試集。開發者不能提交一個無法通過這些測試的 pull request。CRI-O 是一個完全開放的項目,目前已經有來自 Intel、SUSE、IBM、Google、Hyper.sh 等公司的貢獻者。只要 CRI-O 的大多數維護者同意,pull request 就會被接受,即使這些補丁功能不是紅帽公司需要的。

寫在最後

我希望本文的深入探討能夠幫助讀者了解 Linux 容器的演化史。Linux 一度處於每個廠商有自己標準的情形。Docker 公司專註於建立鏡像構建的事實標準,並且簡化了容器的相關工具。Open Container Initiative 的建立意味著行業正在圍繞著核心鏡像格式和運行時制定規範,圍繞著讓工具更有效率、更安全、高可擴展性和可用性進行創新。容器允許我們能夠用新穎的方式驗證軟體安裝,無論其是運行在主機上的傳統軟體還是運行在雲端經過編排的微服務。在很多方面,這還僅僅是個開始。

https://opensource.com/article/17/7/how-linux-containers-evolved

今日薦文


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 InfoQ 的精彩文章:

一張圖解讀小米直達服務能力
30+前沿案例,300位同行者,直擊2018互聯網出海新攻略

TAG:InfoQ |