K8s 宣布與 Dockershim 分手,Docker 用戶該怎麼辦?

May 31, 2022 Cheng-Yi Fang

Photo by Alvaro Reyes on Unsplash

Photo by Andreas Dress on Unsplash

這邊要討論的事件,起源於一年多前(2020年底),K8s 官方宣佈 v1.20 版之後,將不再支援 Docker,這在當時引起許多論壇以及使用者間的騷動(彷彿一群吃瓜群眾在看 Johnny Depp 與 Amber Heard 之間發生了什麼事),也有一些文章出來解釋,請大家不用慌張。而接著就這樣過了一年半,在 K8s 即將迎來最新的 v1.24 釋出中,其中一項重大改變就是 K8s 正式將 Dockershim 移除了(我們祝福它們分手快樂)。

這邊先列出兩位事件主角過去的幾個重要的時間點:

這篇文章的主要目的是要解釋以下兩個問題:


Container Runtime Interface (CRI) 與 Dockershim 的背景介紹

在回答上面兩個問題之前,必須先解釋何謂 CRI。CRI 是由雲原生計算基金會(CNCF)在2016年首次引入,其主要目的是希望 K8s 能夠透過一個標準介面,與各種容器運行時介接,這個介接的介面標準就是所謂的 CRI(容器運行時介面)。

以下是一個遵循 CRI 的示意架構圖:

Photo by Yu-Ju Hong on kubernetes.io
Photo by Yu-Ju Hong on kubernetes.io

CRI shim 就是一個基於 CRI 所實作出來,扮演 Kubelet 與容器運行時之間的溝通橋樑。CRI shim 會透過 gRPC 接收 Kubelet 傳送過來的 Protobuf 格式資料(與 K8s 相同,gRPC 和 Protobuf 都是 Google 的產物),其中包含各式各樣針對 Pod 或 Container 的操作,如 Run, Stop, List, Remove, Start … 等。

而 Dockershim 就是一種 CRI shim 的實現,讓 Docker 的使用者能夠透過 K8s 對容器進行管理。這五年來,K8s 社群一直在維護著 Dockershim,但隨著有越來越多人使用不同的容器運行時,且 Docker 也被 Mirantis 買走後,K8s 社群就覺得這部分的責任不需要在他們身上。即使 K8s 在 v1.20 就宣布捨棄,但社群還是持續維護 Dockershim 到 v1.23。一直到現在 v1.24 才終於斷捨離,結束這段維持 5年多的關係…


Dockershim 被移除後,K8s 要如何使用 Docker

由於 Mirantis 在 2021年宣稱透過他們的 cri-dockerd,可以取代原本的 Dockershim,接下來我們就來驗證這件事。

此外,這篇文章撰寫的時間點是在 2022年5月,此時的 Kubespray 還無法支援 K8s v1.24 的部署。因此,以下會順便先介紹如何將舊版的 K8s (v1.22.2)升級至 v1.24。接著再介紹如何部署 Mirantis 的 cri-dockerd 以及設定 K8s CRI 使用 Docker。最後再測試 Docker 的容器運行時是否能正常使用。

前置環境

部署步驟

A. 先準備一個 K8s 環境(可參考 Kubespray 部署),並切換至 root 身份。

[vagrant@fanchy3-1 ~]$ sudo su
[root@fanchy3-1 vagrant]# cd
[root@fanchy3-1 ~]#

B. Build Kubernetes v1.24.0。

[root@fanchy3-1 ~]# yum install -y git
[root@fanchy3-1 ~]# git clone https://github.com/kubernetes/kubernetes
[root@fanchy3-1 ~]# cd kubernetes/
[root@fanchy3-1 kubernetes]# git checkout v1.24.0-alpha.3
[root@fanchy3-1 kubernetes]# make quick-release

C. 解開壓縮檔,載入新的 images。

[root@fanchy3-1 kubernetes]# cd _output/release-tars/
[root@fanchy3-1 kubernetes]# tar zxvf kubernetes-server-linux-amd64.tar.gz
[root@fanchy3-1 kubernetes]# tar zxvf kubernetes-client-linux-amd64.tar.gz
[root@fanchy3-1 kubernetes]# cd kubernetes/server/bin/
[root@fanchy3-1 bin]# docker load -i kube-apiserver.tar
[root@fanchy3-1 bin]# docker load -i kube-controller-manager.tar
[root@fanchy3-1 bin]# docker load -i kube-proxy.tar
[root@fanchy3-1 bin]# docker load -i kube-scheduler.tar

D. 升級 K8s Control Plane & Client。

修改 manifests YAML 會自動重啟 Control Plane 服務。如需備份原始 YAML 的話,建議放到其它資料夾,否則會額外建立新的 Control Plane。

修改 kube-apiserver 的 image,包含 image 名稱和 tag。以及拿掉 --insecure-port 那行。

[root@fanchy3-1 bin]# diff /etc/kubernetes/manifests/kube-apiserver.yaml /backup/kube-apiserver.yaml
33a34
>     - --insecure-port=0
55c56
<     image: k8s.gcr.io/kube-apiserver-amd64:v1.24.0-alpha.3
---
>     image: k8s.gcr.io/kube-apiserver:v1.22.2

修改 kube-controller-manager 的 image,包含 image 名稱和 tag。以及拿掉 --port 那行。

[root@fanchy3-1 bin]# diff /etc/kubernetes/manifests/kube-controller-manager.yaml /backup/kube-controller-manager.yaml
31a32
>     - --port=0
39c40
<     image: k8s.gcr.io/kube-controller-manager-amd64:v1.24.0-alpha.3
---
>     image: k8s.gcr.io/kube-controller-manager:v1.22.2

修改 kube-scheduler 的 image,包含 image 名稱和 tag。以及拿掉 --port 那行。

[root@fanchy3-1 bin]# diff /etc/kubernetes/manifests/kube-scheduler.yaml /backup/kube-scheduler.yaml
20c20,21
<     image: k8s.gcr.io/kube-scheduler-amd64:v1.24.0-alpha.3
---
>     - --port=0
>     image: k8s.gcr.io/kube-scheduler:v1.22.2

修改 kubescheduler-config 的 apiVersion:v1beta1 -> v1beta3

[root@fanchy3-1 bin]# diff /etc/kubernetes/kubescheduler-config.yaml /backup/kubescheduler-config.yaml 
1c1
< apiVersion: kubescheduler.config.k8s.io/v1beta3
---
> apiVersion: kubescheduler.config.k8s.io/v1beta1

預期應該會看到 kube-apiserver, kube-controller-manager, kube-scheduler, autoscaler_dns, coredns 被自動重新開啟。

[root@fanchy3-1 bin]# docker ps | grep -v pause
CONTAINER ID        IMAGE                         COMMAND                  CREATED             STATUS              PORTS               NAMES
3772990b76a7        8522d622299c                  "/opt/bin/flanneld -…"   3 minutes ago       Up 3 minutes                            k8s_kube-flannel_kube-flannel-c4pf9_kube-system_ff7d21ee-427b-4735-8b52-20bd5775fa8f_1
ce2dd4f70ce8        21fc69048bd5                  "/node-cache -locali…"   3 minutes ago       Up 3 minutes                            k8s_node-cache_nodelocaldns-f2q5n_kube-system_f35fcc58-9f11-4b90-b2d3-e4491c3eeebd_1
f267c6e7581b        873127efbc8a                  "/usr/local/bin/kube…"   3 minutes ago       Up 3 minutes                            k8s_kube-proxy_kube-proxy-9w9kc_kube-system_3785a792-2b2c-4244-a18c-c5e893d1159c_1
52cd89654dd8        quay.io/coreos/etcd:v3.4.13   "/usr/local/bin/etcd"    3 minutes ago       Up 3 minutes                            etcd1
5cb89fd4d9bd        32214fab87ea                  "kube-controller-man…"   3 minutes ago       Up 3 minutes                            k8s_kube-controller-manager_kube-controller-manager-fanchy3-1_kube-system_188b5e64a88454a974ec526fbc996ba6_1
e16b720dbef7        80bd8f5a50f0                  "kube-apiserver --ad…"   3 minutes ago       Up 3 minutes                            k8s_kube-apiserver_kube-apiserver-fanchy3-1_kube-system_0b273727edb9bada6fb21854aa6aad26_1
de4fa7bf56de        a30e10ff1ba4                  "kube-scheduler --au…"   3 minutes ago       Up 3 minutes                            k8s_kube-scheduler_kube-scheduler-fanchy3-1_kube-system_92cf4cb430cbbeedc5bcdeef7a7accfb_3

將新的 kubectl 放到正確位置後,預期會看到 Client/Server 版本都被升級到 1.24+。

[root@fanchy3-1 bin]# mv /usr/local/bin/kubectl /usr/local/bin/kubectl.v1.22.2
[root@fanchy3-1 bin]# cp ../../client/bin/kubectl /usr/local/bin/kubectl
[root@fanchy3-1 bin]# kubectl version
Client Version: version.Info{Major:"1", Minor:"24+", GitVersion:"v1.24.0-alpha.3", GitCommit:"30a21e9abdbbeb78d2b7ce59a79e46299ced2742", GitTreeState:"clean", BuildDate:"2022-03-16T06:16:00Z", GoVersion:"go1.17.7", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"24+", GitVersion:"v1.24.0-alpha.3", GitCommit:"30a21e9abdbbeb78d2b7ce59a79e46299ced2742", GitTreeState:"clean", BuildDate:"2022-03-16T05:58:22Z", GoVersion:"go1.17.7", Compiler:"gc", Platform:"linux/amd64"}

E. Build & 安裝 cri-dockerd。

[root@fanchy3-1 bin]# cd
[root@fanchy3-1 ~]# wget https://go.dev/dl/go1.17.8.linux-amd64.tar.gz
[root@fanchy3-1 ~]# rm -rf /usr/local/go && tar -C /usr/local -xzf go1.17.8.linux-amd64.tar.gz
[root@fanchy3-1 ~]# export PATH=$PATH:/usr/local/go/bin
[root@fanchy3-1 ~]# echo "export PATH=$PATH:/usr/local/go/bin" >> /etc/profile
[root@fanchy3-1 ~]# go version
[root@fanchy3-1 ~]# git clone https://github.com/Mirantis/cri-dockerd.git
[root@fanchy3-1 ~]# cd cri-dockerd/
[root@fanchy3-1 cri-dockerd]# mkdir bin
[root@fanchy3-1 cri-dockerd]# cd src && go get && go build -o ../bin/cri-dockerd
[root@fanchy3-1 src]# cd ..
[root@fanchy3-1 cri-dockerd]# install -o root -g root -m 0755 bin/cri-dockerd /usr/local/bin/cri-dockerd
[root@fanchy3-1 cri-dockerd]# cp -a packaging/systemd/* /etc/systemd/system
[root@fanchy3-1 cri-dockerd]# sed -i -e 's,/usr/bin/cri-dockerd,/usr/local/bin/cri-dockerd,' /etc/systemd/system/cri-docker.service

修改 cri-docker.service,--pod-cidr 帶入的數值是要給 Pod 使用的 IPs。

[root@fanchy3-1 cri-dockerd]# sed -i -e 's,--network-plugin=,--cni-bin-dir=/opt/cni/bin --cni-cache-dir=/var/lib/cni --cni-conf-dir=/etc/cni/net.d --network-plugin=cni --pod-cidr=10.233.64.0/18,' /etc/systemd/system/cri-docker.service

啟動 cri-docker。

[root@fanchy3-1 cri-dockerd]# systemctl daemon-reload
[root@fanchy3-1 cri-dockerd]# systemctl enable cri-docker.service
[root@fanchy3-1 cri-dockerd]# systemctl enable --now cri-docker.socket
[root@fanchy3-1 cri-dockerd]# systemctl start cri-docker
[root@fanchy3-1 cri-dockerd]# systemctl status cri-docker

F. 升級 Kubelet。

[root@fanchy3-1 cri-dockerd]# cd /root/kubernetes/_output/release-tars/kubernetes/server/bin/
[root@fanchy3-1 bin]# systemctl stop kubelet
[root@fanchy3-1 bin]# mv /usr/local/bin/kubelet kubelet.v1.22.2
[root@fanchy3-1 bin]# cp kubelet /usr/local/bin/

接下來,修改 kubelet.service。因為 K8s v1.24 的 KUBELET_NETWORK_PLUGIN 相關 flag 都被捨棄,所以必須移除此參數。並加入 --container-runtime=remote --container-runtime-endpoint=/var/run/cri-dockerd.sock

[root@fanchy3-1 bin]# vi /etc/systemd/system/kubelet.service

重新啟動 kubelet 後,預期會看到 node 已被升級至 v1.24.0-alpha.3。

[root@fanchy3-1 bin]# systemctl start kubelet
[root@fanchy3-1 bin]# kubectl get node

驗證測試

A. 功能是否正常。

a1. 建立一個 nginx deployment。

[root@fanchy3-1 ~]# kubectl get po
No resources found in default namespace.
[root@fanchy3-1 ~]# kubectl apply -f test.yaml 
deployment.apps/nginx-deployment created
[root@fanchy3-1 ~]# kubectl get po
NAME                               READY   STATUS              RESTARTS   AGE
nginx-deployment-9456bbbf9-vqhz6   0/1     ContainerCreating   0          4s
nginx-deployment-9456bbbf9-w2pd9   0/1     ContainerCreating   0          4s

a2. 可以看到 pod 正常開啟,透過 docker ps 也可以看到。

[root@fanchy3-1 ~]# kubectl get po
NAME                               READY   STATUS    RESTARTS   AGE
nginx-deployment-9456bbbf9-vqhz6   1/1     Running   0          80s
nginx-deployment-9456bbbf9-w2pd9   1/1     Running   0          80s
[root@fanchy3-1 ~]# docker ps
CONTAINER ID        IMAGE                         COMMAND                  CREATED              STATUS              PORTS               NAMES
806b3d103c9c        nginx                         "nginx -g 'daemon of…"   About a minute ago   Up About a minute                       k8s_nginx_nginx-deployment-9456bbbf9-w2pd9_default_a1918aec-57f4-46b2-87bc-1e16256109f8_0
4fdffc974ea7        nginx                         "nginx -g 'daemon of…"   About a minute ago   Up About a minute                       k8s_nginx_nginx-deployment-9456bbbf9-vqhz6_default_f8f4d1c7-d491-4cd5-b013-c19eaae76e0e_0
8451bc26461f        k8s.gcr.io/pause:3.1          "/pause"                 About a minute ago   Up About a minute                       k8s_POD_nginx-deployment-9456bbbf9-w2pd9_default_a1918aec-57f4-46b2-87bc-1e16256109f8_0
cce14b929f50        k8s.gcr.io/pause:3.1          "/pause"                 About a minute ago   Up About a minute                       k8s_POD_nginx-deployment-9456bbbf9-vqhz6_default_f8f4d1c7-d491-4cd5-b013-c19eaae76e0e_0
...

a3. kubectl exec 可以正常使用。

[root@fanchy3-1 ~]# kubectl exec -it nginx-deployment-9456bbbf9-vqhz6 -- bash
root@nginx-deployment-9456bbbf9-vqhz6:/#

B. 不使用 cri-dockerd。

在部署步驟 f. (升級 Kubelet),如果不使用 --container-runtime=remote --container-runtime-endpoint=/var/run/cri-dockerd.sock flag,Kubelet 會無法正常運行,並出現以下錯誤訊息。

[root@fanchy3-1 ~]# vi /etc/systemd/system/kubelet.service 
[root@fanchy3-1 ~]# systemctl daemon-reload
[root@fanchy3-1 ~]# systemctl restart kubelet
[root@fanchy3-1 ~]# systemctl status kubelet
● kubelet.service - Kubernetes Kubelet Server
   Loaded: loaded (/etc/systemd/system/kubelet.service; enabled; vendor preset: disabled)
   Active: activating (auto-restart) (Result: exit-code) since 一 2022-03-21 00:31:10 UTC; 5s ago
     Docs: https://github.com/GoogleCloudPlatform/kubernetes
  Process: 24978 ExecStart=/usr/local/bin/kubelet $KUBE_LOGTOSTDERR $KUBE_LOG_LEVEL $KUBELET_API_SERVER $KUBELET_ADDRESS $KUBELET_PORT $KUBELET_HOSTNAME $KUBELET_ARGS $DOCKER_SOCKET $KUBELET_VOLUME_PLUGIN $KUBELET_CLOUDPROVIDER (code=exited, status=1/FAILURE)
 Main PID: 24978 (code=exited, status=1/FAILURE)
...
[root@fanchy3-1 ~]# journalctl -xeu kubelet -f
...
 3月 21 00:31:20 fanchy3-1 kubelet[25005]: I0321 00:31:20.852907   25005 util_unix.go:104] "Using this format as endpoint is deprecated, please consider using full url format." deprecatedFormat="" fullURLFormat="unix://"
 3月 21 00:31:20 fanchy3-1 kubelet[25005]: W0321 00:31:20.853255   25005 clientconn.go:1331] [core] grpc: addrConn.createTransport failed to connect to {  <nil> 0 <nil>}. Err: connection error: desc = "transport: Error while dialing dial unix: missing address". Reconnecting...
 3月 21 00:31:20 fanchy3-1 kubelet[25005]: Error: failed to run Kubelet: unable to determine runtime API version: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing dial unix: missing address"
 3月 21 00:31:20 fanchy3-1 kubelet[25005]: Usage:
 3月 21 00:31:20 fanchy3-1 kubelet[25005]: kubelet [flags]
 3月 21 00:31:20 fanchy3-1 kubelet[25005]: Flags:
...

透過上述步驟推論,K8s 1.24+ 之後,必須(可以)使用 cri-dockerd,才能繼續使用 Docker。

後續影響與個人觀點

撇開一些商業策略的原因,一般軟體架構的改變,過程中雖然會來回不斷的做調整,但大方向不外乎是朝著更高的效能、彈性(Flexibility)、可擴充性(Scalability)、高可用性(High Availability)、廠商獨立性(Vendor Independence)… 等方向去做改善。雖然軟體的開發過程中,常會面臨需要作權衡(trade-off)的階段,但理應不會去做弊大於利的調整。

K8s 社群決定使用 CRI 並移除 Dockershim 的主要目的是為了 K8s 本身的彈性,以支援不同的容器運行時,並減少支援單一容器運行時(Docker)的維護成本,可以更專注在 K8s 本身的功能開發與維護。(也或許是因為 Mirantis 這位第三者的介入,加速了 K8s 與 Docker 之間關係的變化…)

目前 Mirantis 已經買下了 Docker,而他們也會希望更多 K8s 的使用者會去選擇使用 Docker,所以他們也勢必會維護好 cri-dockerd 的功能。因此,原本在 K8s 下使用 Docker 用戶,也無需太過擔心。


雙子星雲端為 CNCF 會員,是 CNCF 所認證的 Kubernetes 服務提供商,在雲端技術擁有十多年以上的經驗,為台灣雲端技術早期領先者。目前為國家級 AI 雲的軟體及 Kubernetes 技術與服務提供商,更是諸多企業與單位導入容器與管理平台的最佳夥伴。

雙子星雲端除了既有的產品 AI Console 與 Gemini API Gateway 之外,也提供企業諮詢與導入雲原生與 Kubernetes 相關技術服務,協助企業擁抱 Cloud Natvive,達到數位轉型的目標。


相關文章

回雙子星技術部落格列表

Gemini AI Console

熱門文章


kubernetes professional service

關於我們

雙子星雲端是混合多雲技術的領導者,是國際認證之 KCSP - Kubernetes 服務提供商,同時也為 CNCF 雲原生計算基金會會員。

雙子星的雲端專家擁有 Kubernetes、OpenStack 與 Google Cloud Platform 等多項證照,我們的軟體至今已為上百家機構和數千台的 CPU/GPU 伺服器提供雲端服務。