整理 | 山河已無恙 校對 | 小雨青年
出品 | CSDN雲原生
聲明:本文出自CNCF網站,最初由Alessandro Lo Manto在Sighup部落格上發表。CSDN將文章翻譯成中文,分享給大家。
2020年底,Kubernetes團隊棄用了Docker,並宣佈將在2021年底完全移除對Docker的支持。這種棄用帶來了很多變化,因為使用Docker作為容器運行時接口(CRI)而不是開放容器倡議(OCI)造成了一點混亂。
那麼,為什麼要這麼大驚小怪呢?我們應該繼續寫Dockerfile嗎?
如今,人們仍然很難理解Docker是什麼。公司、容器、映象和開發者使用的工具之間似乎沒有什麼區別,對許多人來說,只有一個叫做「Docker」的詞來指代這一切。

Docker內部的奧秘
Docker是一個開發、發佈和運行應用程序的開放平臺。Docker能夠將應用程序與基礎設施分離,從而快速交付軟體。Docker利用了Linux核心及其功能,因為獨立運行進程,所以隔離方法十分重要。
Docker之所以使用Linux Container(LXC),是因為其使用Runtime 作為後端。隨著任務的發展,LXC被改變為Containerd。
當前的Docker設置分為:Containerd(用於管理容器)和Dockerd(用於提供來自Docker主機的資訊的持久進程守護進程)。被定義為「容器」的Docker,只不過是一個與使用者友好的容器互動工具。它的創建方式使每個人都可以安裝它、構建容器映象、從註冊表中提取映象,並創建、啟動和管理容器。這就是Docker被稱為「容器」的原因。
那麼Kubernetes是什麼?和這一切又有什麼關係?
要想理解為什麼Kubernetes和Docker會一起出名,我們需要先了解Docker是如何集成在Kubernetes中以及開放容器倡議(OCI)和容器運行時接口(CRI)的含義。

什麼是OCI
Kubernetes採用容器的概念,它並不是在一臺伺服器上運行容器化應用程序,而是將它們分佈在一個集群上。
容器的普及需要有一個開放的映象標準。Docker Inc和CoreOS創建了OCI(Open Container Initiative,開放容器倡議),其使命是產生標準格式。它推出了兩個具體的標準:
對二進位制格式映象的要求
一個描述如何互動和運行容器的規範。OCI維護著一個名為Runc的參考實現,Containerd在後臺使用Runc與容器進行互動
OCI增加了不同容器運行時解決方案之間的互操作性,因此,映象可以在任何其他遵守此標準的系統中運行。

什麼是CRI
為了工作,Kubernetes 需要一個支持CRI的容器運行時。CRI是一個Kubernetes API,它定義了Kubernetes與不同容器運行時互動的方式。因為它是標準化的規範,所以您可以選擇想要使用的CRI實現或自行編寫。
為什麼Kubernetes不需要Docker作為容器運行時?Docker比Kubernetes更老,並且沒有實現CRI,為什麼它能夠發揮作用?
Dockershim元件的創建是為了讓你與CRI互動。但如今Docker有替代品,Kubernetes便不再需要保持這種額外的複雜性了。
前面解釋過,Docker並不是一個容器運行時,它是一系列與容器互動的工具,只是一個中間人。

應該停止將Kubernetes與Docker一起使用嗎?
如果您的集群已經由GKE、EKS或AKS(默認為Containerd)等主要雲提供商配置,或者您只是一個Kubernetes使用者,這對您沒有影響。
Docker已經並將繼續在Kubernetes生態系統中發揮重要作用。後者將繼續運行Docker容器並從Docker註冊表中提取映象,因為Docker生成了符合OCI的映象。
但是,讓我們來談談自己吧!在本文中,我們將引導您使用Containerd而不是Docker創建一個Kubernetes集群。

Vagrant VM上的集群設置
下面我們編寫一個基本的Vagrantfile和腳本配置供大家理解,可以按照以下提供的步驟創建Kubernetes集群。
作為先決條件,您需要安裝和配置Virtualbox和Vagrant。
Step 1:在啟動集群之前,首先在您選擇的檔案夾中創建一個Vagrantfile檔案。
Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :
ENV['VAGRANT_NO_PARALLEL'] = 'yes'
Vagrant.configure(2) do |config|
# Kubernetes Master Server
config.vm.define "master" do |node|
node.vm.box = "generic/ubuntu2004"
node.vm.box_check_update = false
node.vm.box_version = "3.2.18"
node.vm.hostname = "master"
node.vm.network "private_network", ip: "172.0.0.100"
node.vm.provider :virtualbox do |v|
v.name = "master"
v.memory = 2048
v.cpus = 2
end
node.vm.provider :libvirt do |v|
v.memory = 2048
v.nested = true
v.cpus = 2
end
end
# Kubernetes Worker Node
config.vm.define "worker0" do |node|
node.vm.box = "generic/ubuntu2004"
node.vm.box_check_update = false
node.vm.box_version = "3.2.18"
node.vm.hostname = "worker0"
node.vm.network "private_network", ip: "172.0.1.101"
node.vm.provider :virtualbox do |v|
v.name = "worker0"
v.memory = 1024
v.cpus = 1
end
node.vm.provider :libvirt do |v|
v.memory = 1024
v.nested = true
v.cpus = 1
end
end
end
Step 2:執行vagrant命令。它將啟動兩個節點,一個主節點和一個工作節點。
vagrant up
Step 3 :登入主節點和工作節點安裝集群初始化配置腳本。
vagrant ssh master
vagrant ssh worker0
main.sh
#!/bin/bash
echo "[TASK 1] Disable and turn off SWAP"
sed -i '/swap/d' /etc/fstab
swapoff -a
echo "[TASK 2] Stop and Disable firewall"
systemctl disable --now ufw >/dev/null 2>&1
echo "[TASK 3] Enable and Load Kernel modules"
cat >>/etc/modules-load.d/containerd.conf<<EOF
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter
echo "[TASK 4] Add Kernel settings"
cat >>/etc/sysctl.d/kubernetes.conf<<EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system >/dev/null 2>&1
echo "[TASK 5] Install containerd runtime"
apt update -qq >/dev/null 2>&1
apt install -qq -y containerd apt-transport-https >/dev/null 2>&1
mkdir /etc/containerd
containerd config default > /etc/containerd/config.toml
systemctl restart containerd
systemctl enable containerd >/dev/null 2>&1
echo "[TASK 6] Add apt repo for kubernetes"
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - >/dev/null 2>&1
apt-add-repository "deb http://apt.kubernetes.io/ kubernetes-xenial main" >/dev/null 2>&1
echo "[TASK 7] Install Kubernetes components (kubeadm, kubelet and kubectl)"
apt install -qq -y kubeadm=1.21.0-00 kubelet=1.21.0-00 kubectl=1.21.0-00 >/dev/null 2>&1
Master node
vagrant@master:~$ vim main.sh
vagrant@master:~$ sudo bash main.sh
[TASK 1] Disable and turn off SWAP
[TASK 2] Stop and Disable firewall
[TASK 3] Enable and Load Kernel modules
[TASK 4] Add Kernel settings
[TASK 5] Install containerd runtime
[TASK 6] Add apt repo for kubernetes
[TASK 7] Install Kubernetes components (kubeadm, kubelet and kubectl)
Worker node
vagrant@worker0:~$ vim main.sh
vagrant@worker0:~$ sudo bash main.sh
[TASK 1] Disable and turn off SWAP
[TASK 2] Stop and Disable firewall
[TASK 3] Enable and Load Kernel modules
[TASK 4] Add Kernel settings
[TASK 5] Install containerd runtime
[TASK 6] Add apt repo for kubernetes
[TASK 7] Install Kubernetes components (kubeadm, kubelet and kubectl)
Step 4 :只在主節點上安裝下面的腳本,並在最後複製kubeadm join命令。
master.sh
#!/bin/bash
echo "[TASK 1] Pull required containers"
kubeadm config images pull >/dev/null 2>&1
echo "[TASK 2] Initialize Kubernetes Cluster"
kubeadm init --apiserver-advertise-address=172.0.0.100 --pod-network-cidr=192.168.0.0/16 >> /root/kubeinit.log 2>/dev/null
echo "[TASK 3] Deploy Calico network"
kubectl --kubeconfig=/etc/kubernetes/admin.conf create -f https://docs.projectcalico.org/v3.18/manifests/calico.yaml >/dev/null 2>&1
mkdir /home/vagrant/.kube
cp /etc/kubernetes/admin.conf /home/vagrant/.kube/config
chown -R vagrant:vagrant /home/vagrant/.kube
echo "[TASK 4] Generate and save cluster join command"
kubeadm token create --print-join-command
vagrant@master:~$ vim master.sh
vagrant@master:~$ sudo bash master.sh
[TASK 1] Pull required containers
[TASK 2] Initialize Kubernetes Cluster
[TASK 3] Deploy Calico network
[TASK 4] Generate and save cluster join command
kubeadm join 172.0.0.100:6443 --token 5d6fgz.0lll5srvyxa9wfcm --discovery-token-ca-cert-hash sha256:0828fbc966896ac32550a7641d54593ef98738d2878ed80c1966431888cc1324
Step 5 :複製並以sudo使用者身份在工作節點中運行join命令。
vagrant@worker0:~$ sudo kubeadm join 172.0.0.100:6443 --token 5d6fgz.0lll5srvyxa9wfcm --discovery-token-ca-cert-hash sha256:0828fbc966896ac32550a7641d54593ef98738d2878ed80c1966431888cc1324
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the control-plane to see this node join the cluster
Step 6 :在主節點上,列出所有集群節點,以確保工作節點連接到主節點並處於就緒狀態。
kubectl get nodes -o wide
您可以使用Containerd而不是Docker來查看運行時。
vagrant@master:~$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
master Ready control-plane,master 2m41s v1.21.0 10.0.2.15 <none> Ubuntu 20.04.2 LTS 5.4.0-72-generic containerd://1.5.2
worker0 Ready <none> 98s v1.21.0 10.0.2.15 <none> Ubuntu 20.04.2 LTS 5.4.0-72-generic containerd://1.5.2

結論
至此,我們已經解決了為什麼Kubernetes不需要Docker來工作的問題,並且也看到了Docker在工作流程中的重要性,以及在沒有Docker運行時的情況下安裝Kubernetes是多麼簡單。
Kubernetes 正在成長,但改變不一定是痛苦的經歷。
大多數使用者不需要採取任何行動。對於那些需要的人來說,仍然有時間進行下一步的測試和計劃。
