实操|如何将 Containerd 用作 Kubernetes runtime
日前專為開發(fā)者提供技術(shù)分享的又拍云 OpenTalk 公開課邀請了網(wǎng)易有道資深運維開發(fā)工程師張晉濤,直播分享《Containerd 上手實踐 》,詳細介紹 Containerd 的發(fā)展歷程、主要特性,以及如何將其作為 Kubernetes runtime 的上手實踐。以下是直播內(nèi)容整理
關(guān)于作者:張晉濤,現(xiàn)就職于網(wǎng)易有道, 對 Docker、Kubernetes 及相關(guān)生態(tài)有大量實踐及深入源碼的研究,《Docker 核心知識必知必會》專欄作者。PS 講師長期堅持更新 K8S 生態(tài)周報,如有興趣可訂閱其公眾號【MoeLove】。
大家好,今天分享的內(nèi)容將會從 Kubernetes 宣布棄用 dockershim 說起,介紹 Containerd 相關(guān)特性,分享如何將 Containerd 用作 Kubernetes 的 runtime。
Kubernetes 宣布棄用 dockershim
很多媒體將該事件宣稱為 Kubernetes 宣布棄用 Docker,其實這是一種誤導(dǎo)。那么應(yīng)該如何正確的去看待呢?首先是了解整個事情的前因后果,得需要知道 dockershim 是什么。
dockershim
dockershim 是 Kubernetes 的一個組件,主要目的是為了通過 CRI 操作 Docker。Docker在 2013 年就出現(xiàn)了,2014 年 Kubernetes 發(fā)布并默認使用 Docker 作為容器運行時,而 dockershim首次正式出現(xiàn)是在 2016 年。Docker 在創(chuàng)建之初并沒有考慮到容器編排或者是考慮 Kubernetes,但 Kubernetes 在創(chuàng)建之初便采用Docker 作為它的默認容器進行時,后續(xù)代碼當(dāng)中包含了很多對 Docker 相關(guān)的操作邏輯。后期 Kubernetes 為了能夠做解耦,兼容更多的容器進行時,將操作 Docker 相關(guān)邏輯整體獨立起來組成了 dockershim。
Container Runtime Interface
再說 CRI(Container Runtime Interface)即容器運行時接口,該概念是由Kubernetes 提出并在 2016 年底開始應(yīng)用,其主要目標(biāo)是增強 Kubernetes 的可擴展性,可以不固定、不捆綁某一個容器運行時,實現(xiàn)可插拔式的容器進行時。比如可以使用 Docker 為容器運行時也可以使用其他的例如 rkt,并且希望通過開放 CRI 這個統(tǒng)一的接口來提高代碼的可維護性,而不是需要支持 Docker 時就對 Docker 進行適配,需要支持另一個運行時就得對其做相關(guān)的適配。它希望是任何一個成為 Kubernetes 的容器運行時都遵守 CRI 統(tǒng)一的接口與規(guī)范,實現(xiàn)了 CRI 就可以作為 Kubernetes 的運行時,并不需要關(guān)注具體是什么。
為什么要棄用 dockershim
dockershim 的目的是為了 Kubernetes 通過 CRI 操作 Docker,所以Kubernetes 任何的功能變動或 Docker 有任何的功能特性變更,dockershim 代碼必須加以改動保證能夠支持變更。另外一個原因是隨著容器技術(shù)的推進,容器運行時已經(jīng)多種多樣了,比如本次分享的主角 Containerd,還有 cri-o 以及 rkt 的容器運行時,不過這個 rkt 容器運行時的項目已經(jīng)不維護了。
此外,原先 Kubernetes 需要去調(diào)用 dockershim 跟 Docker 做溝通,Docker 的底層運行時是 containerd,可以發(fā)現(xiàn)最終都是要調(diào)用 containerd,并且 containerd 自身也是可以支持 CRI 的。那為什么要先繞過一層 Docker 呢?是不是可以直接通過 CRI 跟 Containerd 進行交互呢?這也就造成了現(xiàn)在 Kubernetes 社區(qū)希望棄用 dockershim。
棄用 dockershim 的影響
終端???任何影響。這里指的是使用來自云廠商/使用別人提供的Kubernetes 集群的終端用戶,他們不需要關(guān)注集群本身的容器運行時到底是什么,因為和你交互的都是 Kubernetes 自身的 CRI,而任何一個可以作為 Kubernetes 底層容器運行時的東西都必須是兼容 CRI 接口的,上層就已經(jīng)屏蔽掉這個細節(jié)了。
對負責(zé)維護 Kubernetes 集群的工作人員有一定影響。當(dāng)升級 Kubernetes 集群版本時,需要考慮是否切換容器進行時。如果目前在用最新版本 Kubernetes V1.20,并且使用 Docker 作為容器運行時,要考慮它能否正常工作。其實是可以正常工作的,只是在啟動 Kubernetes 的時候會發(fā)現(xiàn)一條日志,提醒你當(dāng)前使用的容器運行時 Docker 已經(jīng)不再被 Kubernetes 支持,因為已經(jīng)準(zhǔn)備棄用dockershim,因此會有這個提醒。
Kubernetes 社區(qū)計劃在 2021 年將dockershim 正式移除。換個角度考慮,既然社區(qū)不想在 Kubernetes 源代碼當(dāng)中維護 dockershim 了,那是不是可以把 dockershim 組件給單獨的拿出來呢?答案是可以的,現(xiàn)在 Mirantis 和 Docker 已經(jīng)決定之后共同合作維護 dockershim 組件。此外,還可以通過樹外的 dockershim 獨立組件,繼續(xù)使用 Docker 作為容器運行時,并且使用這種方式只需要做一些簡單的配置,把原先使用內(nèi)置的Kubernetes 自身攜帶的 dockershim 組件,改成使用一個獨立的 dockershim 組件,本身變動很小。
那么 Docker 到底還能否使用呢?在我看來,毋庸置疑,Docker 仍然是現(xiàn)階段容器構(gòu)建和運行的最佳選擇。
快速了解 Containerd
Containerd 是中間層的容器運行時。它構(gòu)建在平臺之下,作為平臺下層的一個容器運行時,但又比最底層的容器運行時像 runc、gVisor 要高一點,所以被稱為中間層的容器運行時。除此之外也可稱作為資源管理器,可以用來管理容器的進程、鏡像以及管理文件系統(tǒng)的快照,還有元數(shù)據(jù)和依賴的管理。既然它可以作為一個資源管理器來使用,如果想要在此之上構(gòu)建一個屬于自己的容器平臺就會很方便。
Containerd 是由 Docker 公司創(chuàng)建,并且在 2017年捐贈給了 CNCF,2019 年 Containerd 從 CNCF 正式畢業(yè)。Containerd 項目一開始的目標(biāo)是用來管理容器的進程,之后逐步變更成為一個完整的容器運行時,是 Docker 的底層容器運行時。需要說明的是,containerd 是可以拋開 Docker 與 Kubernetes 自身獨立工作的。
Containerd 與 CRI
Containerd 在之前的版本中考慮到了 CRI,但它是將CRI 作為獨立的進程存在的。在上圖中看到,CRI-Containerd 其實是一個獨立組件,Kubernetes 通過 CRI 接口調(diào)用 CRI-Containerd,再由這個組件去調(diào)用 containerd。在 Containerd1.1 版本之后對該特性做了重新的設(shè)計,它將 CRI 的支持通過插件化的方式來實現(xiàn),Kubernetes 通過 CRI 接口調(diào)用的其實是 Containerd 當(dāng)中 CNI 的插件,以此來達到通信的目的,調(diào)用鏈更少更短了。
Containerd 的特性
-
支持 OCI 鏡像規(guī)范,即前文所提到的 runc
-
支持 OCI 運行時規(guī)范。Docker 引導(dǎo)了 OCI 組織的成立,該組織主要有兩個規(guī)范:鏡像規(guī)范與運行時規(guī)范。這兩個規(guī)范在 Docker 成立時把 Docker 鏡像規(guī)范與底層容器運行時規(guī)范都給捐贈出來作為它的初始工作
-
支持鏡像的 push/pull 作用
-
支持容器網(wǎng)絡(luò)管理。因為可以啟動和運行容器,容器啟動后支持相互之間的訪問,或彼此之間網(wǎng)絡(luò)的隔離,所以需要支持容器網(wǎng)絡(luò)的管理
-
存儲支持多租戶。Containerd 的相關(guān)操作有通過 namespace 來做隔離的,可以指定不同的 namespace 來實現(xiàn),它默認的 namespace 叫 default,在 default 的 namespace下面下載多個鏡像。但是在其他的 namespace 下看不到這些鏡像,也用不到,以此來達到多租戶的隔離
-
支持容器運行時和容器的生命周期管理
-
支持管理網(wǎng)絡(luò)名稱空間容器以加入現(xiàn)有名稱空間,可以讓某一個容器加入到現(xiàn)有的 namespace 當(dāng)中
Containerd 的整體架構(gòu)
上圖是 Containerd 整體的架構(gòu)。由下往上,Containerd支持的操作系統(tǒng)和架構(gòu)有 Linux、Windows 以及像 ARM 的一些平臺。在這些底層的操作系統(tǒng)之上運行的就是底層容器運行時,其中有上文提到的runc、gVisor 等。在底層容器運行時之上的是Containerd 相關(guān)的組件,比如 Containerd 的 runtime、core、API、backend、store 還有metadata 等等。構(gòu)筑在 Containerd 組件之上以及跟這些組件做交互的都是 Containerd 的 client,Kubernetes 跟 Containerd 通過 CRI 做交互時,本身也作為 Containerd 的一個 client。Containerd 本身有提供了一個 CRI,叫 ctr,不過這個命令行工具并不是很好用。
在這些組件之上就是真正的平臺,Google Cloud、Docker、IBM、阿里云、微軟云還有RANCHER等等都是,這些平臺目前都已經(jīng)支持 containerd, 并且有些已經(jīng)作為自己的默認容器運行時了。
Containerd 主要功能與上手實踐
鏡像管理
首先是上文中頻繁提到的鏡像管理。具體操作是需要通過一個 client 去跟 Containerd 做交互。如圖中所示,這里選擇了ctr 的命令行工具。ctr指定一個address 參數(shù)跟 Containerd交互,它是在后臺持續(xù)運行的一個服務(wù),需要指定它的地址。圖中是通過 pull 一個 redis alpine linux 的鏡像,接下來通過 image ls就可以看到已經(jīng)成功 pull 下來的鏡像。
容器管理
作為一個容器運行時對容器進行管理是必不可少的功能。同樣的通過 -a 參數(shù)來指定 address 與Containerd 進行通信,通過 container create+鏡像名稱+容器名稱來創(chuàng)建一個容器,通過 container ls 可以看到剛才創(chuàng)建的容器。需要注意的是,最后一列叫做 runtime,它的 runtime 叫做 io.containerd.runc.v2,表明是 v2 版本的 Containerd API。
在 Containerd 中通過 container create 創(chuàng)建出來的容器其實并不管用,還需要讓其運行起來。這時通常會把它當(dāng)作一個 task,對它執(zhí)行 task start,就可以把剛才創(chuàng)建的鏡像跑起來了。通過 task ls 就可以看到名叫 redis 的 Containerd,其中有一個正在運行的進程,并且展現(xiàn)出了進程號。
命名空間
需要說明的是,可以通過 -n 來指定一個默認的叫做 default 的命名空間,而后通過 task ls 就看到剛才啟動容器的進程,它其實是在運行中的。如果把 namespace 換一個,比如圖中的 moby 就是 Docker 項目當(dāng)前使用的 namespace 名稱,Docker 在使用 Containerd 作為容器運行時的時候,會默認使用它。
繼續(xù)往下看,通過 ctr -n 指定使用 moby 命名空間,-a 參數(shù)指定containerd 的地址,然后 task ls 來看看moby 項目當(dāng)中到底運行著什么。可以看到有一條記錄是正在運行當(dāng)中的。這條記錄如何和 Docker 當(dāng)中的容器或任務(wù)做匹配比較呢?一個簡單的辦法就是通過 docker ps --no-trunc-- format 跟容器完整的 ID,然后 grep 就可以看到剛才通過 ctr 命令得到的ID 了。
需要注意的是,如果使用 Containerd 作為 Kubernetes 的容器運行時,那么它的 namespace 叫 k8s.io。到這里可能有些人已經(jīng)發(fā)現(xiàn),Containerd 作為 Docker 的運行時可以使用不同的命名空間,比如 moby。用作 Kubernetes 容器運行時也可以使用不同的命名空間,比如 k8s.io。那是否存在一種辦法可以讓平臺當(dāng)中既有 Kubernetes 又有 Docker,還有 Containerd 呢?答案是肯定的,直接將其全部裝到一起,但不配置 Docker 作為容器運行時,先觀察一段時間看看,這也是一種辦法。
Containerd 用作 Kubernetes 的 runtime
上圖是 Containerd 用作 Kubernetes 的 runtime 整個流程圖。Kubernetes 通過 CRI 接口,調(diào)用到 CRI plugin,plugin 是Containerd 一個內(nèi)置的插件,其中包含了最主要的兩部分:一是 image service,包含了鏡像服務(wù)相關(guān)的;二是 runtime service,即運行時的服務(wù)。如果在 containerd 當(dāng)中部署了一個項目或服務(wù),它首先會調(diào)度到某一臺機器,這臺機器上的 Kubernetes 就會開始工作,它會查詢服務(wù)、需要哪些鏡像,而后把相關(guān)的鏡像讓運行時給拉下來,再去啟動對應(yīng)的 pod 或者相關(guān)的容器。
其次它還會跟 CNI 做交互。CNI 即 Container Network Interface,是Kubernetes 提供的一個容器網(wǎng)絡(luò)接口)。主要注意的是,交互過程中可能會提前創(chuàng)建出來 pause的 container,是一個占位的過程,這里先不對此做更深入的介紹。
當(dāng) Containerd 作為 Kubernetes 的容器運行時,配置相對很簡單:通過 containerd config default命令可以直接查詢到 Containerd 完整的默認配置,下圖中可以看到主要是配置 CRI。所以在這里的配置文件當(dāng)中,通過 plugins.io.containerd.grpc.vi.cri 對其進行配置,首先是 default-runtime,其次配置一個 runtime.runc。
這里簡單介紹配置 runc 需要注意的參數(shù)。比如 io.containerd.runc.v2 需要配置runtime.type;涉及配置與 runc 相關(guān)的一些配置會包含一些 CNI 的配置、目錄之類的,具體的配置上圖中已經(jīng)展示了。總而言之如果想要提起來一個服務(wù)、一個 pod 或是 container,要注意都是需要配置的。它都會有一個 pause的鏡像,即 sandbox_image,可以從中指定一個默認的鏡像。當(dāng)然也可以通過此處換源,加快國內(nèi)環(huán)境下的拉取速度。
最后還有些其他的資源,如本人長期參與的一個項目 KIND( Kubernetes in docker)。這個項目相當(dāng)于是使用docker 容器作為不同的 node,可以把這些 node 組成一個集群網(wǎng)絡(luò),搭建一套 Kubernetes。而這個集群使用的容器運行時就是 containerd,雖然一開始使用的是 Docker,但后期逐步都將其替換成了 containerd,類似的還有包括 K3C、K3S,效果都是差不多的。
總結(jié)
以上是生活随笔為你收集整理的实操|如何将 Containerd 用作 Kubernetes runtime的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Wi-Fi 6 与 5G 相比哪个更快?
- 下一篇: 网页出现不河蟹弹窗?那是被劫持了!