實操|如何將 Containerd 用做 Kubernetes runtime

日前專爲開發者提供技術分享的又拍雲 OpenTalk 公開課邀請了網易有道資深運維開發工程師張晉濤,直播分享《Containerd 上手實踐 》,詳細介紹 Containerd 的發展歷程、主要特性,以及如何將其做爲 Kubernetes runtime 的上手實踐。如下是直播內容整理html

關於做者:張晉濤,現就任於網易有道, 對 Docker、Kubernetes 及相關生態有大量實踐及深刻源碼的研究,《Docker 核心知識必知必會》專欄做者。PS 講師長期堅持更新 K8S 生態週報,若有興趣可訂閱其公衆號【MoeLove】。node

你們好,今天分享的內容將會從 Kubernetes 宣佈棄用 dockershim 提及,介紹 Containerd 相關特性,分享如何將 Containerd 用做 Kubernetes 的 runtime。linux

Kubernetes 宣佈棄用 dockershim

不少媒體將該事件宣稱爲 Kubernetes 宣佈棄用 Docker,其實這是一種誤導。那麼應該如何正確的去看待呢?首先是瞭解整個事情的來龍去脈,得須要知道 dockershim 是什麼。redis

dockershimdocker

dockershim 是 Kubernetes 的一個組件,主要目的是爲了經過 CRI 操做 Docker。Docker在 2013 年就出現了,2014 年 Kubernetes 發佈並默認使用 Docker 做爲容器運行時,而 dockershim首次正式出現是在 2016 年。Docker 在建立之初並無考慮到容器編排或者是考慮 Kubernetes,但 Kubernetes 在建立之初便採用Docker 做爲它的默認容器進行時,後續代碼當中包含了不少對 Docker 相關的操做邏輯。後期 Kubernetes 爲了可以作解耦,兼容更多的容器進行時,將操做 Docker 相關邏輯總體獨立起來組成了 dockershim。網絡

Container Runtime Interface架構

再說 CRI(Container Runtime Interface)即容器運行時接口,該概念是由Kubernetes 提出並在 2016 年末開始應用,其主要目標是加強 Kubernetes 的可擴展性,能夠不固定、不捆綁某一個容器運行時,實現可插拔式的容器進行時。好比可使用 Docker 爲容器運行時也可使用其餘的例如 rkt,而且但願經過開放 CRI 這個統一的接口來提升代碼的可維護性,而不是須要支持 Docker 時就對 Docker 進行適配,須要支持另外一個運行時就得對其作相關的適配。它但願是任何一個成爲 Kubernetes 的容器運行時都遵照 CRI 統一的接口與規範,實現了 CRI 就能夠做爲 Kubernetes 的運行時,並不須要關注具體是什麼。運維

爲何要棄用 dockershim工具

dockershim 的目的是爲了 Kubernetes 經過 CRI 操做 Docker,因此Kubernetes 任何的功能變更或 Docker 有任何的功能特性變動,dockershim 代碼必須加以改動保證可以支持變動。另一個緣由是隨着容器技術的推動,容器運行時已經多種多樣了,好比本次分享的主角 Containerd,還有 cri-o 以及 rkt 的容器運行時,不過這個 rkt 容器運行時的項目已經不維護了。阿里雲

此外,原先 Kubernetes 須要去調用 dockershim 跟 Docker 作溝通,Docker 的底層運行時是 containerd,能夠發現最終都是要調用 containerd,而且 containerd 自身也是能夠支持 CRI 的。那爲何要先繞過一層 Docker 呢?是否是能夠直接經過 CRI 跟 Containerd 進行交互呢?這也就形成了如今 Kubernetes 社區但願棄用 dockershim。

棄用 dockershim 的影響

終端⽤⼾⽆任何影響。這裏指的是使用來自雲廠商/使用別人提供的Kubernetes 集羣的終端用戶,他們不須要關注集羣自己的容器運行時究竟是什麼,由於和你交互的都是 Kubernetes 自身的 CRI,而任何一個能夠做爲 Kubernetes 底層容器運行時的東西都必須是兼容 CRI 接口的,上層就已經屏蔽掉這個細節了。

對負責維護 Kubernetes 集羣的工做人員有必定影響。當升級 Kubernetes 集羣版本時,須要考慮是否切換容器進行時。若是目前在用最新版本 Kubernetes V1.20,而且使用 Docker 做爲容器運行時,要考慮它可否正常工做。實際上是能夠正常工做的,只是在啓動 Kubernetes 的時候會發現一條日誌,提醒你當前使用的容器運行時 Docker 已經再也不被 Kubernetes 支持,由於已經準備棄用dockershim,所以會有這個提醒。

Kubernetes 社區計劃在 2021 年將dockershim 正式移除。換個角度考慮,既然社區不想在 Kubernetes 源代碼當中維護 dockershim 了,那是否是能夠把 dockershim 組件給單獨的拿出來呢?答案是能夠的,如今 Mirantis 和 Docker 已經決定以後共同合做維護 dockershim 組件。此外,還能夠經過樹外的 dockershim 獨立組件,繼續使用 Docker 做爲容器運行時,而且使用這種方式只須要作一些簡單的配置,把原先使用內置的Kubernetes 自身攜帶的 dockershim 組件,改爲使用一個獨立的 dockershim 組件,自己變更很小。

那麼 Docker 到底還可否使用呢?在我看來,毋庸置疑,Docker 仍然是現階段容器構建和運行的最佳選擇。

快速瞭解 Containerd

Containerd 是中間層的容器運行時。它構建在平臺之下,做爲平臺下層的一個容器運行時,但又比最底層的容器運行時像 runc、gVisor 要高一點,因此被稱爲中間層的容器運行時。除此以外也可稱做爲資源管理器,能夠用來管理容器的進程、鏡像以及管理文件系統的快照,還有元數據和依賴的管理。既然它能夠做爲一個資源管理器來使用,若是想要在此之上構建一個屬於本身的容器平臺就會很方便。

Containerd 是由 Docker 公司建立,而且在 2017年捐贈給了 CNCF,2019 年 Containerd 從 CNCF 正式畢業。Containerd 項目一開始的目標是用來管理容器的進程,以後逐步變動成爲一個完整的容器運行時,是 Docker 的底層容器運行時。須要說明的是,containerd 是能夠拋開 Docker 與 Kubernetes 自身獨立工做的。

Containerd 與 CRI

Containerd 在以前的版本中考慮到了 CRI,但它是將CRI 做爲獨立的進程存在的。在上圖中看到,CRI-Containerd 實際上是一個獨立組件,Kubernetes 經過 CRI 接口調用 CRI-Containerd,再由這個組件去調用 containerd。在 Containerd1.1 版本以後對該特性作了從新的設計,它將 CRI 的支持經過插件化的方式來實現,Kubernetes 經過 CRI 接口調用的實際上是 Containerd 當中 CNI 的插件,以此來達到通訊的目的,調用鏈更少更短了。

Containerd 的特性

  • 支持 OCI 鏡像規範,即前文所提到的 runc
  • 支持 OCI 運行時規範。Docker 引導了 OCI 組織的成立,該組織主要有兩個規範:鏡像規範與運行時規範。這兩個規範在 Docker 成立時把 Docker 鏡像規範與底層容器運行時規範都給捐贈出來做爲它的初始工做
  • 支持鏡像的 push/pull 做用
  • 支持容器網絡管理。由於能夠啓動和運行容器,容器啓動後支持相互之間的訪問,或彼此之間網絡的隔離,因此須要支持容器網絡的管理
  • 存儲支持多租戶。Containerd 的相關操做有經過 namespace 來作隔離的,能夠指定不一樣的 namespace 來實現,它默認的 namespace 叫 default,在 default 的 namespace下面下載多個鏡像。可是在其餘的 namespace 下看不到這些鏡像,也用不到,以此來達到多租戶的隔離
  • 支持容器運行時和容器的生命週期管理
  • 支持管理網絡名稱空間容器以加入現有名稱空間,可讓某一個容器加入到現有的 namespace 當中

Containerd 的總體架構

上圖是 Containerd 總體的架構。由下往上,Containerd支持的操做系統和架構有 Linux、Windows 以及像 ARM 的一些平臺。在這些底層的操做系統之上運行的就是底層容器運行時,其中有上文提到的runc、gVisor 等。在底層容器運行時之上的是Containerd 相關的組件,好比 Containerd 的 runtime、core、API、backend、store 還有metadata 等等。構築在 Containerd 組件之上以及跟這些組件作交互的都是 Containerd 的 client,Kubernetes 跟 Containerd 經過 CRI 作交互時,自己也做爲 Containerd 的一個 client。Containerd 自己有提供了一個 CRI,叫 ctr,不過這個命令行工具並非很好用。

在這些組件之上就是真正的平臺,Google Cloud、Docker、IBM、阿里雲、微軟雲還有RANCHER等等都是,這些平臺目前都已經支持 containerd, 而且有些已經做爲本身的默認容器運行時了。

Containerd 主要功能與上手實踐

鏡像管理

首先是上文中頻繁提到的鏡像管理。具體操做是須要經過一個 client 去跟 Containerd 作交互。如圖中所示,這裏選擇了ctr 的命令行工具。ctr指定一個address 參數跟 Containerd交互,它是在後臺持續運行的一個服務,須要指定它的地址。圖中是經過 pull 一個 redis alpine linux 的鏡像,接下來經過 image ls就能夠看到已經成功 pull 下來的鏡像。

容器管理

做爲一個容器運行時對容器進行管理是必不可少的功能。一樣的經過 -a 參數來指定 address 與Containerd 進行通訊,經過 container create+鏡像名稱+容器名稱來建立一個容器,經過 container ls 能夠看到剛纔建立的容器。須要注意的是,最後一列叫作 runtime,它的 runtime 叫作 io.containerd.runc.v2,代表是 v2 版本的 Containerd API。

在 Containerd 中經過 container create 建立出來的容器其實並無論用,還須要讓其運行起來。這時一般會把它看成一個 task,對它執行 task start,就能夠把剛纔建立的鏡像跑起來了。經過 task ls 就能夠看到名叫 redis 的 Containerd,其中有一個正在運行的進程,而且展示出了進程號。

命名空間

須要說明的是,能夠經過 -n 來指定一個默認的叫作 default 的命名空間,然後經過 task ls 就看到剛纔啓動容器的進程,它實際上是在運行中的。若是把 namespace 換一個,好比圖中的 moby 就是 Docker 項目當前使用的 namespace 名稱,Docker 在使用 Containerd 做爲容器運行時的時候,會默認使用它。

繼續往下看,經過 ctr -n 指定使用 moby 命名空間,-a 參數指定containerd 的地址,而後 task ls 來看看moby 項目當中到底運行着什麼。能夠看到有一條記錄是正在運行當中的。這條記錄如何和 Docker 當中的容器或任務作匹配比較呢?一個簡單的辦法就是經過 docker ps --no-trunc-- format 跟容器完整的 ID,而後 grep 就能夠看到剛纔經過 ctr 命令獲得的ID 了。

須要注意的是,若是使用 Containerd 做爲 Kubernetes 的容器運行時,那麼它的 namespace 叫 k8s.io。到這裏可能有些人已經發現,Containerd 做爲 Docker 的運行時可使用不一樣的命名空間,好比 moby。用做 Kubernetes 容器運行時也可使用不一樣的命名空間,好比 k8s.io。那是否存在一種辦法可讓平臺當中既有 Kubernetes 又有 Docker,還有 Containerd 呢?答案是確定的,直接將其所有裝到一塊兒,但不配置 Docker 做爲容器運行時,先觀察一段時間看看,這也是一種辦法。

Containerd 用做 Kubernetes 的 runtime

上圖是 Containerd 用做 Kubernetes 的 runtime 整個流程圖。Kubernetes 經過 CRI 接口,調用到 CRI plugin,plugin 是Containerd 一個內置的插件,其中包含了最主要的兩部分:一是 image service,包含了鏡像服務相關的;二是 runtime service,即運行時的服務。若是在 containerd 當中部署了一個項目或服務,它首先會調度到某一臺機器,這臺機器上的 Kubernetes 就會開始工做,它會查詢服務、須要哪些鏡像,然後把相關的鏡像讓運行時給拉下來,再去啓動對應的 pod 或者相關的容器。

其次它還會跟 CNI 作交互。CNI 即 Container Network Interface,是Kubernetes 提供的一個容器網絡接口)。主要注意的是,交互過程當中可能會提早建立出來 pause的 container,是一個佔位的過程,這裏先不對此作更深刻的介紹。

當 Containerd 做爲 Kubernetes 的容器運行時,配置相對很簡單:經過 containerd config default命令能夠直接查詢到 Containerd 完整的默認配置,下圖中能夠看到主要是配置 CRI。因此在這裏的配置文件當中,經過 plugins.io.containerd.grpc.vi.cri 對其進行配置,首先是 default-runtime,其次配置一個 runtime.runc。

這裏簡單介紹配置 runc 須要注意的參數。好比 io.containerd.runc.v2 須要配置runtime.type;涉及配置與 runc 相關的一些配置會包含一些 CNI 的配置、目錄之類的,具體的配置上圖中已經展現了。總而言之若是想要提起來一個服務、一個 pod 或是 container,要注意都是須要配置的。它都會有一個 pause的鏡像,即 sandbox_image,能夠從中指定一個默認的鏡像。固然也能夠經過此處換源,加快國內環境下的拉取速度。

最後還有些其餘的資源,如本人長期參與的一個項目 KIND( Kubernetes in docker)。這個項目至關因而使用docker 容器做爲不一樣的 node,能夠把這些 node 組成一個集羣網絡,搭建一套 Kubernetes。而這個集羣使用的容器運行時就是 containerd,雖然一開始使用的是 Docker,但後期逐步都將其替換成了 containerd,相似的還有包括 K3C、K3S,效果都是差很少的。

推薦閱讀

雲原生網絡代理(MOSN)的進化之路

聊聊 HTTP 常見的請求方式

相關文章
相關標籤/搜索