K8s 終將廢棄 docker,TKE 早已支持 containerd

近日 K8s 官方稱最先將在 1.23版本棄用 docker 做爲容器運行時,並在博客中強調可使用如 containerd 等 CRI 運行時來代替 docker。本文會作詳細解讀,並介紹 docker 與 containerd 的關係,以及爲何 containerd 是更好的選擇。這裏先回答下TKE用戶關心的問題:咱們的集羣該怎麼辦?node

TKE集羣該怎麼辦

  • TKE早在 2019年5月就已經支持選擇 containerd 做爲容器運行時。若是新建集羣,推薦選擇 containerd 做爲容器運行時
  • 已有集羣在升級到 K8s 1.23(假定 TKE 第一個不支持 dockershim 的 K8s版本,也多是 1.24)以前,仍然能夠繼續使用 docker 做爲容器運行時
  • 已有集羣經過 TKE 集羣升級功能升級到 1.23時, TKE會提供切換運行時爲 containerd 的選項。固然,這種狀況下沒辦法作到 Pod 不受影響,只能採用重裝節點的方式來升級
  • 已有集羣也能夠將運行時切換爲 containerd ,新增節點會使用 containerd , 存量節點不受影響仍然使用 docker (注意: 這會形成同一集羣中 docker 節點與 containerd 節點共存,若是有使用 Docker in Docker, 或者其餘依賴節點上 docker daemon 與 docker.sock 的業務,須要提早採起措施來避免產生問題,例如經過按節點標籤調度,保證這類業務調度到 docker 節點;或者採用如前文所述在 containerd 集羣運行 Docker in Docker 的方案)
  • 固然,在將來 docker 也有可能在內部實現 CRI 或者添加一個 dockershim 進程,若是 docker 作了相應適配,TKE 這邊在將來也會進行支持。

解讀 K8s 棄用 dockershim

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

本次改動主要內容是準備刪除 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

棄用 Docker 後會有什麼不一樣?

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 的此生前世

因此 containerd 究竟是個啥?和 docker 又是什麼關係?可能有些同窗看到博客後會發出這樣的疑問,接下來就給同窗們講解下 containerd 和 docker 的淵源。

docker 與 containerd

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 仍然是更好的選擇。

在 Kubernetes 集羣中使用 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

K8s 1.11 先後版本配置區別是什麼?

containerd 的 stream 服務在 K8s 不一樣版本運行時場景下配置不一樣。

  • 在 K8s 1.11 以前:
    kubelet 不會作 stream proxy,只會作重定向。即 kubelet 會將 containerd 暴露的 stream server 地址發送給 apiserver,並讓 apiserver 直接訪問 containerd 的 stream 服務。此時,您須要給 stream 服務轉發器認證,用於安全防禦。
  • 在 K8s 1.11 以後:
    K8s1.11 引入了 kubelet stream proxy, 使 containerd stream 服務只須要監聽本地地址便可。

在 TKE 集羣中使用 containerd

從 2019年5月份開始,TKE就開始支持把 containerd 做爲容器運行時選項之一。隨着TKE逐步在 containerd 集羣中支持日誌收集服務和 GPU 能力,2020年 9月份 containerd 在 TKE 也摘掉了 Beta 版本的標籤,能夠正式用於生產環境中了。在長期使用中,咱們也發現了一些 containerd 的問題而且及時進行了修復,如:

想要在TKE集羣中使用 containerd 做爲運行時有三種方式:

  1. 在建立集羣時,選擇 1.12.4 及以上版本的 K8s 後,選擇 containerd 爲運行時組件便可

  2. 在已有 docker 集羣中,經過建立運行時爲 containerd 的節點池來建立一部分 containerd 節點(新建節點池 > 更多設置 > 運行時組件)

  3. 在已有 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

[4] kubernetes CHANGELOG-1.20

【騰訊雲原生】雲說新品、雲研新術、雲遊新活、雲賞資訊,掃碼關注同名公衆號,及時獲取更多幹貨!!

相關文章
相關標籤/搜索