前言
前篇文章中跟大家探討了 kubernetes
內關於容器運行標準化的架構,透過 Container Runtime Interface
與各式各樣的 Container Runtime
解決方案溝通,可以讓 kubernetes
本身只需要專注於 kubelet
以及 CRI 標準
的互動,第三方開發者則可以根據需求開發出各式各樣不同的產品,只要能夠滿足 CRI 標準
即可。
就我所知,目前相容於 CRI
的解決方案可根據其架構分成三大類,分別是
- 真的是輕量級虛擬化的容器
- 背後是虛擬機器,但是卻運作起來像輕量級虛擬化的容器
- 真的是傳統虛擬機器
接下來的數篇文章會根據每個類別詳細介紹一種解決方案,並探討設計理念與架構,與此同時大家也可以思考自己的環境是不是有可能可以搭載其他的解決方案,而非永遠使用 Docker
?
本篇會先透過一個簡單的環境實驗來帶過如何建置一個使用 containerd
的 kubernetes
環境,可以體驗到不安裝 docker
也可以運行的 kubernetes
叢集, 同時之後若安裝了 docker
也可以觀察到是否真的如上篇文章所說, docker
與 kubernetes
本身創造出來的 container
是互相不干涉的。
環境建置
接下來的建置環境與步驟是參考 Containerd GitHub 濃縮的。
環境需求
- OS: Ubuntu 16.04
- 18.04 會因為
kubernetes deb
相關的問題導致套件更新失敗,建議先基於16.04
測試即可
- 18.04 會因為
- Python: 2.7+
- Ansible: 2.4+
- 有沒有使用
ansible
都無所謂,因為我們會拆解裡面的步驟,去看看全部的安裝步驟
-
- 有沒有使用
環境安裝
環境的部分只有包括 containerd
的相依套件,並不包含 kubernetes
叢集本身,但是有包含 kubeadm
, kubectl
, kubelet
相關套件。
Ansible 版本
基本上官方文件已經將相關的步驟都建置完畢,本身只需要準備 hosts
的檔案即可,該 ansible playbook
支援 Ubuntu
以及 CentOS
兩套發行版本。
首先在 ansible host
執行下列指令抓取相關的 ansible-playbook
1
2git clone https://github.com/containerd/cri
cd ./cri/contrib/ansible
接下來根據需求產生對應的 hosts
檔案,裡面放置要被安裝的機器,或是直接採用 localhost
的方式再本機運行該 playbook
即可。
這部分比較屬於 ansible
的方式,不熟悉的讀者可以自行尋找資源看看如何運行,或是放棄採用 ansible
, 而是逐條逐條的自行安裝1
ansible-playbook -i hosts cri-containerd.yaml
順利跑完應該會看到類似下圖的結果
1 | ... |
手動安裝版本
如果不想透過 ansible
一鍵安裝完畢,接下來透過拆解該 ansible playbook
就可以知道實際上要做哪些步驟來建置 containerd
的環境。
- 讀取下列變數
var/vars.yaml
- containerd_release_version: 1.1.0-rc.0
- cni_bin_dir: /opt/cni/bin/
- cni_conf_dir: /etc/cni/net.d/
第一個指明containerd
的版本後,後續兩個變數則是CNI (Container Network Interface)
會用到的路徑,之後的文章會細部探討CNI
的設計與使用。
- 根據發行版本讀取不同的檔案來安裝相關套件 (Ubuntu 為範例)
- unzip
- tar
- apt-transport-https
- btrfs-tools
- libseccomp2
- socat
- util-linux
- 安裝
kubernetes
會用到的相關工具- kubelet
- kubeadm
- kubectl
安裝
containerd
會用到的相關工具- cri-containerd
- 來源網址是 https://storage.googleapis.com/cri-containerd-release, 點進去可以看到各種不同版本的
cri-containerd
. - 解壓縮該安裝檔案後可以看到有下列內容,包含了
systemd
的設定,相關的執行檔案。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29./
./opt/
./opt/containerd/
./opt/containerd/cluster/
./opt/containerd/cluster/gce/
./opt/containerd/cluster/gce/cloud-init/
./opt/containerd/cluster/gce/cloud-init/node.yaml
./opt/containerd/cluster/gce/cloud-init/master.yaml
./opt/containerd/cluster/gce/configure.sh
./opt/containerd/cluster/gce/env
./opt/containerd/cluster/version
./opt/containerd/cluster/health-monitor.sh
./usr/
./usr/local/
./usr/local/sbin/
./usr/local/sbin/runc
./usr/local/bin/
./usr/local/bin/crictl
./usr/local/bin/containerd
./usr/local/bin/containerd-stress
./usr/local/bin/critest
./usr/local/bin/containerd-release
./usr/local/bin/containerd-shim
./usr/local/bin/ctr
./etc/
./etc/systemd/
./etc/systemd/system/
./etc/systemd/system/containerd.service
./etc/crictl.yaml
透過
systemd
啟動containerd
- 設定
netfilter
相關設定,讓kernel
啟動相關功能- br_netfilter -> bridge 層級啟動 netfilter (ebtables)
- bridge-nf-call-iptables -> bridge 層級也會去呼叫 iptables 處理
- ip_forward -> 轉發 ip 封包
這些功能基本上以前安裝docker
的時候都會幫忙處理,所以基本上都不會特別注意到
- 接下來就是重頭戲了,如何讓
kubernetes
知道要使用containerd
作為其Container Runtime
而非使用dockershim
來銜接docker
.
根據 官方文件,kubelet
裡面有下列的參數可以設定–container-runtime string
The container runtime to use. Possible values: ‘docker’, ‘remote’, ‘rkt(deprecated)’. (default “docker”)
–container-runtime-endpoint string
[Experimental] The endpoint of remote runtime service. Currently unix socket is supported on Linux, and tcp is supported on windows.
其中 container-runtime
可以使用三種來處理,分別是內建的 docker
, rkt
以及客製化的 remote
.
當上述選擇了 remote
後,也必須要一起設定 container-runtime-endpoint
告訴 kubelet
這時候要怎麼跟非內建的 container runtime
溝通。
因此安裝過程中,該 ansible playbook
就會嘗試修改 kubelet
的啟動參數,這部分因為整個安裝過程都是透過 kubeadm
去架設 kubernetes cluster
, 所以最後是修改 /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
這個檔案. 關於 kubeadm
與 kubelet
彼此之間的設定過程可以參考下列文章 kubeadm/kubelet-integration
1 | [email protected]:~$ sudo cat /etc/systemd/system/kubelet.service.d/10-kubeadm.conf |
當我們實際觀察這個檔案,可以發現裡面關於環境變數的部分加入了下列選項,這時候我們就明瞭到底 kubernetes
之後是如何跟 containerd
溝通的--container-runtime=remote --runtime-request-timeout=15m --container-runtime-endpoint=unix:///run/containerd/containerd.sock"
- 最後就是啟動
kubelet
將基本的服務起來Kubernetes Cluster
一切都準備就緒後,接下來非常簡單的透過kubeadm
的方式來創建kubernetes cluster
.
這個範例中我採用flannel
作為 CNI, 之後也會有文章詳細介紹Flannel
的運作原理。
依序執行下列指令將 kubernetes cluster
給建立起來,並安裝 CNI
同時也打開taint
讓 master node
也可以部署 Pod
.
1 | sudo swapoff -a && sudo sysctl -w vm.swappiness=0 |
這時候使用簡單的工具確認 kubernetes
有正常起來即可1
2
3
4
5
6[email protected]:~/cri/contrib/ansible$ kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-5c98db65d4-ghzpl 0/1 Running 0 26s
kube-system coredns-5c98db65d4-thsdq 0/1 Running 0 26s
kube-system kube-flannel-ds-amd64-ztqgs 1/1 Running 0 14s
kube-system kube-proxy-jfs66 1/1 Running 0 26s
觀察
Containerd
接下來我們要透過不同的工具來觀察當使用 containerd
作為部署時有什麼不同
由於此時我們不是透過 docker
來管理整個 kubernetes
底層需要的 container
,而是透過 kubelet & CRI & containerd
來管理。
所以這時候就不能透過 docker ps
或是 docker images
等指令來看相關的 container
資源,取而代之的是 crictl
這個在前面步驟伴隨 containerd
一起安裝到系統內的工具。
1 | [email protected]:~/cri/contrib/ansible$ crictl |
說明非常簡單,就是 CRI
命令列,其預設的設定檔案位於 /etc/crictl.yaml
1
2[email protected]:~/cri/contrib/ansible$ cat /etc/crictl.yaml
runtime-endpoint: /run/containerd/containerd.sock
所以接下來呼叫的任何 crictl
指令都會跟 containerd
互動來取得回應,我們就根據上面的提示來試試看各種指令
基於 kubernetes POD
概念的資源顯示1
2
3
4
5
6
7
8[email protected]:~/cri/contrib/ansible$ sudo crictl pods PODSANDBOX ID CREATED STATE NAME NAMESPACE ATTEMPT
4ed054a456e96 17 minutes ago SANDBOX_READY coredns-5c98db65d4-ghzpl kube-system 0 c5209a01e0936 17 minutes ago SANDBOX_READY coredns-5c98db65d4-thsdq kube-system 0
0a771e9912232 18 minutes ago SANDBOX_READY kube-flannel-ds-amd64-ztqgs kube-system 0
0642c904298c8 18 minutes ago SANDBOX_READY kube-proxy-jfs66 kube-system 0
46039553a0fcc 18 minutes ago SANDBOX_READY kube-scheduler-k8s-dev kube-system 0
ec6d7cde198cc 18 minutes ago SANDBOX_READY kube-controller-manager-k8s-dev kube-system 0
be0bc17da244d 18 minutes ago SANDBOX_READY etcd-k8s-dev kube-system 0
490d8b240d8ca 18 minutes ago SANDBOX_READY kube-apiserver-k8s-dev kube-system 0
相關的 container images
, 這些 images
再之前都是透過 docker pull
等方式安裝,如今我們系統內完全不裝 docker
也是可以使用,這一切都是仰賴 OCI
標準的制定外加 containerd
的幫忙。
1 | [email protected]:~/cri/contrib/ansible$ sudo crictl images |
接下來看看運行的 container
, 其結果跟 docker ps
非常相似,使用起來幾乎沒有任何違和感,我覺得設定 alias docker=crictl
都不會有什麼錯覺?
1 | [email protected]:~/cri/contrib/ansible$ sudo crictl ps |
除了 container
本身的管理外,我們來看一下系統內運行的應用程式, 可以觀察到系統上有一個 containerd
正在運行,同時 kuberlet
內關於 container-runtime
的參數有被修改。
此外對於每個運作的 container
, containetd
都會產生一個 containerd-shim
來運作。
1 | [email protected]:~/cri/contrib/ansible$ ps -axo command | grep containerd |
Docker
為了驗證之前說明是否 dockerd
與 kubelet
共用 containerd
但是彼此資源是互相隔離的,這時候我們來安裝一下 docker
並且於背景運行一個 container
試試看
1 | sudo docker run -d hwchiu/netutils |
接下來我們使用 docker
指令針對上述的操作都進行一次,來比較看看1
2
3
4
5
6[email protected]:~$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hwchiu/netutils latest a0d1dad34d58 13 months ago 222MB
[email protected]:~$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
320b653e39d8 hwchiu/netutils "/bin/bash ./entrypo…" 15 minutes ago Up 15 minutes inspiring_haslett
非常乾淨簡單,完全看不到任何 kubernetes
用到的容器資源,此外我們再度透過 `ps
觀察
1 | /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock |
首先,可以看到 dockerd
這時候也透過參數的方式去連接 containerd
, 另外可以看到針對我上述的 docker image
所產生的 containerd-shim
與 kubernetes
產生的 containerd-shim
有明顯的參數不同
- -namespace moby v.s k8s.io
- workdir 不同
- -runtime-root /var/run/docker/runtime-runc
此外我們也可以透過 containerd cli(ctr) and containerd
來觀察更多有趣的資訊
首先可以看到對於 containerd
的確有不同的 namespace
,也的確如上面所觀察到的是 moby
與 k8s.io
1
2
3
4[email protected]:~$ sudo ctr namespaces ls
NAME LABELS
k8s.io
moby
此外我們也可以看到不少關於 containerd 預設的設定,預設情況下 linux
環境中都是採用 runc
作為 OCI Runtime Spec
的解決方案。
而 kubelet
透過 CRI
與 docker
最後選擇都是會採取 plugin.linux
的設定,所以兩者背後最後都是透過 runc
來創建 Container
。
1 | sudo containerd config default |
Summary
本文中我們簡述了如何建置一套基於 containerd
而非 docker
的 kubernetes cluster
, 並且透過相關指令與工具來觀察在此設定下,kubernetes
,containerd
, dockerd
等彼此的交互關係,可以更加深對於 CRI, OCI
等概念的理解。
kubelet -> containerd -> containetrd-shin(k8s.io) -> runc
docker client -> docker enginer -> containerd -> containerd-shim(moby) -> runc
個人資訊
我目前於 Hiskio 平台上面有開設 Kubernetes 相關課程,歡迎有興趣的人參考並分享,裡面有我從底層到實戰中對於 Kubernetes 的各種想法
線上課程詳細資訊: https://course.hwchiu.com/
另外,歡迎按讚加入我個人的粉絲專頁,裡面會定期分享各式各樣的文章,有的是翻譯文章,也有部分是原創文章,主要會聚焦於 CNCF 領域
https://www.facebook.com/technologynoteniu
如果有使用 Telegram 的也可以訂閱下列頻道來,裡面我會定期推播通知各類文章
https://t.me/technologynote
你的捐款將給予我文章成長的動力
參考
- https://github.com/containerd/cri/tree/master/contrib/ansible
- https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/#installing-runtime
- https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/kubelet-integration/#the-kubelet-drop-in-file-for-systemd
- https://gist.github.com/mcastelino/35c221a81c70afc12f8b0929774b60a3