近日 K8s 官方稱最先將在 1.23版本棄用 docker 做爲容器運行時,並在博客中強調可使用如 containerd 等 CRI 運行時來代替 docker。本文會作詳細解讀,並介紹 docker 與 containerd 的關係,以及爲何 containerd 是更好的選擇。這裏先回答下TKE用戶關心的問題:咱們的集羣該怎麼辦?node
Docker support in the kubelet is now deprecated and will be removed in a future release. The kubelet uses a module called "dockershim" which implements CRI support for Docker and it has seen maintenance issues in the Kubernetes community. We encourage you to evaluate moving to a container runtime that is a full-fledged implementation of CRI (v1alpha1 or v1 compliant) as they become available. (#94624, @dims) [SIG Node]git
K8s 在 1.20的 change log 中提到 K8s 將於 1.20版本開始逐步放棄對 Docker 的支持。在 K8s 的官方博客中也提到具體的聲明和一些 FAQ。github
在博客中提到 K8s 將在 1.20版本中添加不推薦使用 docker 的信息,且最先將於 1.23版本中把 dockershim 從 kubelet 中移除,屆時用戶將沒法使用 docker 做爲 K8s 集羣的運行時,不過經過 docker 構建的鏡像在沒有 docker 的 K8s 集羣中依然可使用。docker
本次改動主要內容是準備刪除 kubelet 中的 dockershim,固然這種作法也是符合預期的。在早期 rkt 和 docker 爭霸時,kubelet 中須要維護兩坨代碼分別來適配 docker 和 rkt ,這使得 kubelet 每次發佈新功能都須要考慮對運行時組件的適配問題,嚴重拖慢了新版本發佈速度。另外虛擬化已是一個廣泛的需求,若是出現了類型的運行時,SIG-Node 小組可能還須要把和新運行時適配的代碼添加到 kubelet 中。這種作法並非長久之計,因而在 2016 年,SIG-Node提出了容器操做接口 CRI(Container Runtime Interface)。 CRI 是對容器操做的一組抽象,只要每種容器運行時都實現這組接口,kubelet 就能經過這組接口來適配全部的運行時。但 Docker 當時並無(也不打算)實現這組接口, kubelet 只能在內部維護一個稱之爲「dockershim」組件,這個組件充當了 docker 的 CRI 轉接器,kubelet 在建立容器時經過 CRI 接口調用 dockershim ,而 dockershim 在經過 http 請求把請求交給 docker 。因而 kubelet 的架構變成下圖這樣:
在使用實現了 CRI 接口的組件做爲容器運行時的狀況下,kubelet 建立容器的調用鏈如圖中紅色箭頭所示,kubelet 中的 ContainerManager 能夠直接經過 CRI 調用到容器運行時,這過程當中只須要一次 grpc 請求;而在使用 docker 時,ContainerManager 會走圖中藍色的調用鏈, CRI 的請求經過 unix:///var/run/dockershim.sock 流向 dockershim,dockershim 作轉換後把請求轉發給 docker,至於爲何 docker 後面還有個 containerd 稍後會講到。在 kubelet 中實現 docker 的轉接器原本就是一種不優雅的實現,這種作法讓調用鏈變長且不穩定性,還給 kubelet 的維護添加了額外工做,把這部份內容從 kubelet 刪掉就是時間問題了。json
If you’re an end-user of Kubernetes, not a whole lot will be changing for you. This doesn’t mean the death of Docker, and it doesn’t mean you can’t, or shouldn’t, use Docker as a development tool anymore. Docker is still a useful tool for building containers, and the images that result from running
docker build
can still run in your Kubernetes cluster.api
消息一出,你們最關心的事情應該就是棄用 docker 後到底會產生什麼影響?安全
官方的答覆是:Don't Panic!隨後又重點解釋了幾個你們最關心的問題,咱們來分析下官方提到的這些方面:網絡
正常的 K8s 用戶不會有任何影響架構
是的,生產環境中高版本的集羣只須要把運行時從 docker 切換到其餘的 runtime(如 containerd)便可。containerd 是 docker 中的一個底層組件,主要負責維護容器的生命週期,跟隨 docker 經歷了長期考驗。同時 2019年初就從 CNCF 畢業,能夠單獨做爲容器運行時用在集羣中。TKE 也早在 2019 年就已經提供了 containerd 做爲運行時選項,所以把 runtime 從 docker 轉換到 containerd 是一個基本無痛的過程。CRI-O 是另外一個常被說起的運行時組件,由 redhat 提供,比 containerd 更加輕量級,不過和 docker 的區別較大,可能轉換時會有一些不一樣之處。運維
開發環境中經過docker build
構建出來的鏡像依然能夠在集羣中使用
鏡像一直是容器生態的一大優點,雖然人們老是把鏡像稱之爲「docker鏡像」,但鏡像早就成爲了一種規範了。具體規範能夠參考image-spec。在任何地方只要構建出符合 Image Spec 的鏡像,就能夠拿到其餘符合 Image Spec 的容器運行時上運行。
在 Pod 中使用 DinD(Docker in Docker)的用戶會受到影響
有些使用者會把 docker 的 socket (/run/docker.sock)掛載到 Pod 中,並在 Pod 中調用 docker 的 api 構建鏡像或建立編譯容器等,官方在這裏的建議是使用 Kaniko、Img 或 Buildah。咱們能夠經過把 docker daemon 做爲 DaemonSet 或者給想要使用 docker 的 Pod 添加一個 docker daemon 的 sidecar 的方式在任意運行時中使用 DinD 的方案。TKE 也專門爲在 containerd 集羣中使用 DinD 提供了方案,詳見 在containerd中使用DinD。
因此 containerd 究竟是個啥?和 docker 又是什麼關係?可能有些同窗看到博客後會發出這樣的疑問,接下來就給同窗們講解下 containerd 和 docker 的淵源。
2016年,docker 把負責容器生命週期的模塊拆分出來,並將其捐贈給了社區,也就是如今的 containerd。docker 拆分後結構以下圖所示(固然 docker 公司還在 docker 中添加了部分編排的代碼)。
在咱們調用 docker 命令建立容器後,docker daemon 會經過 Image 模塊下載鏡像並保存到 Graph Driver 模塊中,以後經過 client 調用containerd 建立並運行容器。咱們在使用 docker 建立容器時可能須要使用--volume
給容器添加持久化存儲;還有可能經過--network
鏈接咱們用 docker 命令建立的幾個容器,固然,這些功能是 docker 中的 Storage 模塊和 Networking 模塊提供給咱們的。但 K8s 提供了更強的卷掛載能力和集羣級別的網絡能力,在集羣中 kubelet 只會使用到 docker 提供的鏡像下載和容器管理功能,而編排、網絡、存儲等功能都不會用到。下圖中能夠看出當前的模式下各模塊的調用鏈,同時圖中被紅框標註出的幾個模塊就是 kubelet 建立 Pod 時所依賴的幾個運行時的模塊。
containerd 被捐贈給CNCF社區後,社區給其添加了鏡像管理模塊和 CRI 模塊,這樣 containerd 不僅能夠管理容器的生命週期,還能夠直接做爲 K8s 的運行時使用。因而 containerd 在 2019年2月從 CNCF 社區畢業,正式進入生產環境。下圖中能看出以 containerd 做爲容器運行時,能夠給 kubelet 帶來建立 Pod 所需的所有功能,同時還獲得了更純粹的功能模塊以及更短的調用鏈。
從上面的對比能夠看出從 containerd 被捐贈給社區開始,就一直以成爲簡單、穩定且可靠的容器運行時爲目標;而 docker 則是但願能成爲一個完整的產品。官方文檔中也提到了這一點,docker 爲了給用戶更好的交互和使用體驗以及更多的功能,提供了不少開發人員所須要的特性,同時爲了給 swarm 作基礎,提供了網絡和卷的功能。而這些功能其實都是是 K8s 用不上的;containerd 則相反,僅提供了 kubelet 建立 Pod 所須要的基礎功能,固然這換來的就是更高的魯棒性以及更好的性能。在必定程度上講,即便在 kubelet 1.23 版本以後 docker 提供了 CRI 接口,containerd 仍然是更好的選擇。
固然如今有諸多的 CRI 實現者,比較主要的除了 containerd 還有 CRI-O。CRI-O 是主要由 Red Hat 員工開發的 CRI 運行時,徹底和 docker 沒有關係,所以從 docker 遷移過來可能會比較困難。無疑 containerd 纔是 docker 被拋棄後的 CRI 運行時的最佳人選,對於開發同窗來講整個遷移過程應該是無感知的,不過對於部分運維同窗可能會比較在乎部署和運行中細節上的差別。接下來咱們重點介紹下在 K8s 中使用 containerd 和 docker 的幾處區別。
對比項 | Docker | Containerd |
---|---|---|
存儲路徑 | 若是 docker 做爲 K8s 容器運行時,容器日誌的落盤將由 docker 來完成,保存在相似/var/lib/docker/containers/$CONTAINERID 目錄下。kubelet 會在 /var/log/pods 和 /var/log/containers 下面創建軟連接,指向 /var/lib/docker/containers/$CONTAINERID 該目錄下的容器日誌文件。 | 若是 Containerd 做爲 K8s 容器運行時, 容器日誌的落盤由 kubelet 來完成,保存至 /var/log/pods/$CONTAINER_NAME 目錄下,同時在 /var/log/containers 目錄下建立軟連接,指向日誌文件。 |
配置參數 | 在 docker 配置文件中指定:"log-driver": "json-file","log-opts": {"max-size": "100m","max-file": "5"} | 方法一:在 kubelet 參數中指定:--container-log-max-files=5 --container-log-max-size="100Mi" 方法二:在 KubeletConfiguration 中指定: "containerLogMaxSize": "100Mi", "containerLogMaxFiles": 5, |
把容器日誌保存到數據盤 | 把數據盤掛載到 「data-root」(缺省是 /var/lib/docker)便可。 | 建立一個軟連接 /var/log/pods 指向數據盤掛載點下的某個目錄。在 TKE 中選擇「將容器和鏡像存儲在數據盤」,會自動建立軟連接 /var/log/pods。 |
cni 配置方式的區別
在使用 docker 時,kubelet 中的 dockershim 負責調用 cni 插件,而 containerd 的場景中 containerd 中內置的 containerd-cri 插件負責調用 cni,所以關於 cni 的配置文件須要放在 containerd 的配置文件中(/etc/containerd/containerd.toml):
[plugins.cri.cni] bin_dir = "/opt/cni/bin" conf_dir = "/etc/cni/net.d"
stream 服務的區別
說明:
Kubectl exec/logs 等命令須要在 apiserver 跟容器運行時之間創建流轉發通道。
如何在 containerd 中使用並配置 Stream 服務?
Docker API 自己提供 stream 服務,kubelet 內部的 docker-shim 會經過 docker API 作流轉發。而containerd 的 stream 服務須要單獨配置:
[plugins.cri] stream_server_address = "127.0.0.1" stream_server_port = "0" enable_tls_streaming = false [plugins.cri] stream_server_address = "127.0.0.1" stream_server_port = "0" enable_tls_streaming = false
containerd 的 stream 服務在 K8s 不一樣版本運行時場景下配置不一樣。
從 2019年5月份開始,TKE就開始支持把 containerd 做爲容器運行時選項之一。隨着TKE逐步在 containerd 集羣中支持日誌收集服務和 GPU 能力,2020年 9月份 containerd 在 TKE 也摘掉了 Beta 版本的標籤,能夠正式用於生產環境中了。在長期使用中,咱們也發現了一些 containerd 的問題而且及時進行了修復,如:
想要在TKE集羣中使用 containerd 做爲運行時有三種方式:
在建立集羣時,選擇 1.12.4 及以上版本的 K8s 後,選擇 containerd 爲運行時組件便可
在已有 docker 集羣中,經過建立運行時爲 containerd 的節點池來建立一部分 containerd 節點(新建節點池 > 更多設置 > 運行時組件)
在已有 docker 集羣中,修改集羣或者節點池的"運行時組件"屬性爲"containerd"
注意: 後兩種方式會形成同一集羣中 docker 節點與 containerd 節點共存,若是有使用 Docker in Docker, 或者其餘依賴節點上 docker daemon 與 docker.sock 的業務,須要提早採起措施來避免產生問題,例如經過按節點標籤調度,保證這類業務調度到 docker 節點;或者採用如前文所述在 containerd 集羣運行 Docker in Docker 的方案。
現階段關於 containerd 和 docker 選擇問題能夠查看這裏。
[1] Don't Panic: Kubernetes and Docker
[2] Dockershim FAQ
[3] Dockershim Removal Kubernetes Enhancement Proposal
【騰訊雲原生】雲說新品、雲研新術、雲遊新活、雲賞資訊,掃碼關注同名公衆號,及時獲取更多幹貨!!