新聞中心
一文搞定 Containerd 的使用
作者:k8s技術(shù)圈 2021-08-13 05:50:01
云計(jì)算 從 docker 1.11 版本開始,Docker 容器運(yùn)行就不是簡單通過 Docker Daemon 來啟動(dòng)了,而是通過集成 containerd、runc 等多個(gè)組件來完成的。

創(chuàng)新互聯(lián)公司堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿足客戶于互聯(lián)網(wǎng)時(shí)代的德清網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!
在學(xué)習(xí) Containerd 之前我們有必要對 Docker 的發(fā)展歷史做一個(gè)簡單的回顧,因?yàn)檫@里面牽涉到的組件實(shí)戰(zhàn)是有點(diǎn)多,有很多我們會(huì)經(jīng)常聽到,但是不清楚這些組件到底是干什么用的,比如 libcontainer、runc、containerd、CRI、OCI 等等。
Docker
從 Docker 1.11 版本開始,Docker 容器運(yùn)行就不是簡單通過 Docker Daemon 來啟動(dòng)了,而是通過集成 containerd、runc 等多個(gè)組件來完成的。雖然 Docker Daemon 守護(hù)進(jìn)程模塊在不停的重構(gòu),但是基本功能和定位沒有太大的變化,一直都是 CS 架構(gòu),守護(hù)進(jìn)程負(fù)責(zé)和 Docker Client 端交互,并管理 Docker 鏡像和容器?,F(xiàn)在的架構(gòu)中組件 containerd 就會(huì)負(fù)責(zé)集群節(jié)點(diǎn)上容器的生命周期管理,并向上為 Docker Daemon 提供 gRPC 接口。
docker 架構(gòu)
當(dāng)我們要?jiǎng)?chuàng)建一個(gè)容器的時(shí)候,現(xiàn)在 Docker Daemon 并不能直接幫我們創(chuàng)建了,而是請求 containerd 來創(chuàng)建一個(gè)容器,containerd 收到請求后,也并不會(huì)直接去操作容器,而是創(chuàng)建一個(gè)叫做 containerd-shim 的進(jìn)程,讓這個(gè)進(jìn)程去操作容器,我們指定容器進(jìn)程是需要一個(gè)父進(jìn)程來做狀態(tài)收集、維持 stdin 等 fd 打開等工作的,假如這個(gè)父進(jìn)程就是 containerd,那如果 containerd 掛掉的話,整個(gè)宿主機(jī)上所有的容器都得退出了,而引入 containerd-shim 這個(gè)墊片就可以來規(guī)避這個(gè)問題了。
然后創(chuàng)建容器需要做一些 namespaces 和 cgroups 的配置,以及掛載 root 文件系統(tǒng)等操作,這些操作其實(shí)已經(jīng)有了標(biāo)準(zhǔn)的規(guī)范,那就是 OCI(開放容器標(biāo)準(zhǔn)),runc 就是它的一個(gè)參考實(shí)現(xiàn)(Docker 被逼無耐將 libcontainer 捐獻(xiàn)出來改名為 runc 的),這個(gè)標(biāo)準(zhǔn)其實(shí)就是一個(gè)文檔,主要規(guī)定了容器鏡像的結(jié)構(gòu)、以及容器需要接收哪些操作指令,比如 create、start、stop、delete 等這些命令。runc 就可以按照這個(gè) OCI 文檔來創(chuàng)建一個(gè)符合規(guī)范的容器,既然是標(biāo)準(zhǔn)肯定就有其他 OCI 實(shí)現(xiàn),比如 Kata、gVisor 這些容器運(yùn)行時(shí)都是符合 OCI 標(biāo)準(zhǔn)的。
所以真正啟動(dòng)容器是通過 containerd-shim 去調(diào)用 runc 來啟動(dòng)容器的,runc 啟動(dòng)完容器后本身會(huì)直接退出,containerd-shim 則會(huì)成為容器進(jìn)程的父進(jìn)程, 負(fù)責(zé)收集容器進(jìn)程的狀態(tài), 上報(bào)給 containerd, 并在容器中 pid 為 1 的進(jìn)程退出后接管容器中的子進(jìn)程進(jìn)行清理, 確保不會(huì)出現(xiàn)僵尸進(jìn)程。
而 Docker 將容器操作都遷移到 containerd 中去是因?yàn)楫?dāng)前做 Swarm,想要進(jìn)軍 PaaS 市場,做了這個(gè)架構(gòu)切分,讓 Docker Daemon 專門去負(fù)責(zé)上層的封裝編排,當(dāng)然后面的結(jié)果我們知道 Swarm 在 Kubernetes 面前是慘敗,然后 Docker 公司就把 containerd 項(xiàng)目捐獻(xiàn)給了 CNCF 基金會(huì),這個(gè)也是現(xiàn)在的 Docker 架構(gòu)。
CRI
我們知道 Kubernetes 提供了一個(gè) CRI 的容器運(yùn)行時(shí)接口,那么這個(gè) CRI 到底是什么呢?這個(gè)其實(shí)也和 Docker 的發(fā)展密切相關(guān)的。
在 Kubernetes 早期的時(shí)候,當(dāng)時(shí) Docker 實(shí)在是太火了,Kubernetes 當(dāng)然會(huì)先選擇支持 Docker,而且是通過硬編碼的方式直接調(diào)用 Docker API,后面隨著 Docker 的不斷發(fā)展以及 Google 的主導(dǎo),出現(xiàn)了更多容器運(yùn)行時(shí),Kubernetes 為了支持更多更精簡的容器運(yùn)行時(shí),Google 就和紅帽主導(dǎo)推出了 CRI 標(biāo)準(zhǔn),用于將 Kubernetes 平臺(tái)和特定的容器運(yùn)行時(shí)(當(dāng)然主要是為了干掉 Docker)解耦。
CRI(Container Runtime Interface 容器運(yùn)行時(shí)接口)本質(zhì)上就是 Kubernetes 定義的一組與容器運(yùn)行時(shí)進(jìn)行交互的接口,所以只要實(shí)現(xiàn)了這套接口的容器運(yùn)行時(shí)都可以對接到 Kubernetes 平臺(tái)上來。不過 Kubernetes 推出 CRI 這套標(biāo)準(zhǔn)的時(shí)候還沒有現(xiàn)在的統(tǒng)治地位,所以有一些容器運(yùn)行時(shí)可能不會(huì)自身就去實(shí)現(xiàn) CRI 接口,于是就有了 shim(墊片), 一個(gè) shim 的職責(zé)就是作為適配器將各種容器運(yùn)行時(shí)本身的接口適配到 Kubernetes 的 CRI 接口上,其中 dockershim 就是 Kubernetes 對接 Docker 到 CRI 接口上的一個(gè)墊片實(shí)現(xiàn)。
cri shim
Kubelet 通過 gRPC 框架與容器運(yùn)行時(shí)或 shim 進(jìn)行通信,其中 kubelet 作為客戶端,CRI shim(也可能是容器運(yùn)行時(shí)本身)作為服務(wù)器。
CRI 定義的 API(https://github.com/kubernetes/kubernetes/blob/release-1.5/pkg/kubelet/api/v1alpha1/runtime/api.proto) 主要包括兩個(gè) gRPC 服務(wù),ImageService 和 RuntimeService,ImageService 服務(wù)主要是拉取鏡像、查看和刪除鏡像等操作,RuntimeService 則是用來管理 Pod 和容器的生命周期,以及與容器交互的調(diào)用(exec/attach/port-forward)等操作,可以通過 kubelet 中的標(biāo)志 --container-runtime-endpoint 和 --image-service-endpoint 來配置這兩個(gè)服務(wù)的套接字。
kubelet cri
不過這里同樣也有一個(gè)例外,那就是 Docker,由于 Docker 當(dāng)時(shí)的江湖地位很高,Kubernetes 是直接內(nèi)置了 dockershim 在 kubelet 中的,所以如果你使用的是 Docker 這種容器運(yùn)行時(shí)的話是不需要單獨(dú)去安裝配置適配器之類的,當(dāng)然這個(gè)舉動(dòng)似乎也麻痹了 Docker 公司。
dockershim
現(xiàn)在如果我們使用的是 Docker 的話,當(dāng)我們在 Kubernetes 中創(chuàng)建一個(gè) Pod 的時(shí)候,首先就是 kubelet 通過 CRI 接口調(diào)用 dockershim,請求創(chuàng)建一個(gè)容器,kubelet 可以視作一個(gè)簡單的 CRI Client, 而 dockershim 就是接收請求的 Server,不過他們都是在 kubelet 內(nèi)置的。
dockershim 收到請求后, 轉(zhuǎn)化成 Docker Daemon 能識(shí)別的請求, 發(fā)到 Docker Daemon 上請求創(chuàng)建一個(gè)容器,請求到了 Docker Daemon 后續(xù)就是 Docker 創(chuàng)建容器的流程了,去調(diào)用 containerd,然后創(chuàng)建 containerd-shim 進(jìn)程,通過該進(jìn)程去調(diào)用 runc 去真正創(chuàng)建容器。
其實(shí)我們仔細(xì)觀察也不難發(fā)現(xiàn)使用 Docker 的話其實(shí)是調(diào)用鏈比較長的,真正容器相關(guān)的操作其實(shí) containerd 就完全足夠了,Docker 太過于復(fù)雜笨重了,當(dāng)然 Docker 深受歡迎的很大一個(gè)原因就是提供了很多對用戶操作比較友好的功能,但是對于 Kubernetes 來說壓根不需要這些功能,因?yàn)槎际峭ㄟ^接口去操作容器的,所以自然也就可以將容器運(yùn)行時(shí)切換到 containerd 來。
切換到containerd
切換到 containerd 可以消除掉中間環(huán)節(jié),操作體驗(yàn)也和以前一樣,但是由于直接用容器運(yùn)行時(shí)調(diào)度容器,所以它們對 Docker 來說是不可見的。因此,你以前用來檢查這些容器的 Docker 工具就不能使用了。
你不能再使用 docker ps 或 docker inspect 命令來獲取容器信息。由于不能列出容器,因此也不能獲取日志、停止容器,甚至不能通過 docker exec 在容器中執(zhí)行命令。
當(dāng)然我們?nèi)匀豢梢韵螺d鏡像,或者用 docker build 命令構(gòu)建鏡像,但用 Docker 構(gòu)建、下載的鏡像,對于容器運(yùn)行時(shí)和 Kubernetes,均不可見。為了在 Kubernetes 中使用,需要把鏡像推送到鏡像倉庫中去。
從上圖可以看出在 containerd 1.0 中,對 CRI 的適配是通過一個(gè)單獨(dú)的 CRI-Containerd 進(jìn)程來完成的,這是因?yàn)樽铋_始 containerd 還會(huì)去適配其他的系統(tǒng)(比如 swarm),所以沒有直接實(shí)現(xiàn) CRI,所以這個(gè)對接工作就交給 CRI-Containerd 這個(gè) shim 了。
然后到了 containerd 1.1 版本后就去掉了 CRI-Containerd 這個(gè) shim,直接把適配邏輯作為插件的方式集成到了 containerd 主進(jìn)程中,現(xiàn)在這樣的調(diào)用就更加簡潔了。
containerd cri
與此同時(shí) Kubernetes 社區(qū)也做了一個(gè)專門用于 Kubernetes 的 CRI 運(yùn)行時(shí) CRI-O,直接兼容 CRI 和 OCI 規(guī)范。
cri-o
這個(gè)方案和 containerd 的方案顯然比默認(rèn)的 dockershim 簡潔很多,不過由于大部分用戶都比較習(xí)慣使用 Docker,所以大家還是更喜歡使用 dockershim 方案。
但是隨著 CRI 方案的發(fā)展,以及其他容器運(yùn)行時(shí)對 CRI 的支持越來越完善,Kubernetes 社區(qū)在2020年7月份就開始著手移除 dockershim 方案了:https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/2221-remove-dockershim,現(xiàn)在的移除計(jì)劃是在 1.20 版本中將 kubelet 中內(nèi)置的 dockershim 代碼分離,將內(nèi)置的 dockershim 標(biāo)記為維護(hù)模式,當(dāng)然這個(gè)時(shí)候仍然還可以使用 dockershim,目標(biāo)是在 1.23/1.24 版本發(fā)布沒有 dockershim 的版本(代碼還在,但是要默認(rèn)支持開箱即用的 docker 需要自己構(gòu)建 kubelet,會(huì)在某個(gè)寬限期過后從 kubelet 中刪除內(nèi)置的 dockershim 代碼)。
那么這是否就意味著 Kubernetes 不再支持 Docker 了呢?當(dāng)然不是的,這只是廢棄了內(nèi)置的 dockershim 功能而已,Docker 和其他容器運(yùn)行時(shí)將一視同仁,不會(huì)單獨(dú)對待內(nèi)置支持,如果我們還想直接使用 Docker 這種容器運(yùn)行時(shí)應(yīng)該怎么辦呢?可以將 dockershim 的功能單獨(dú)提取出來獨(dú)立維護(hù)一個(gè) cri-dockerd 即可,就類似于 containerd 1.0 版本中提供的 CRI-Containerd,當(dāng)然還有一種辦法就是 Docker 官方社區(qū)將 CRI 接口內(nèi)置到 Dockerd 中去實(shí)現(xiàn)。
但是我們也清楚 Dockerd 也是去直接調(diào)用的 Containerd,而 containerd 1.1 版本后就內(nèi)置實(shí)現(xiàn)了 CRI,所以 Docker 也沒必要再去單獨(dú)實(shí)現(xiàn) CRI 了,當(dāng) Kubernetes 不再內(nèi)置支持開箱即用的 Docker 的以后,最好的方式當(dāng)然也就是直接使用 Containerd 這種容器運(yùn)行時(shí),而且該容器運(yùn)行時(shí)也已經(jīng)經(jīng)過了生產(chǎn)環(huán)境實(shí)踐的,接下來我們就來學(xué)習(xí)下 Containerd 的使用。
Containerd
我們知道很早之前的 Docker Engine 中就有了 containerd,只不過現(xiàn)在是將 containerd 從 Docker Engine 里分離出來,作為一個(gè)獨(dú)立的開源項(xiàng)目,目標(biāo)是提供一個(gè)更加開放、穩(wěn)定的容器運(yùn)行基礎(chǔ)設(shè)施。分離出來的 containerd 將具有更多的功能,涵蓋整個(gè)容器運(yùn)行時(shí)管理的所有需求,提供更強(qiáng)大的支持。
containerd 是一個(gè)工業(yè)級標(biāo)準(zhǔn)的容器運(yùn)行時(shí),它強(qiáng)調(diào)簡單性、健壯性和可移植性,containerd 可以負(fù)責(zé)干下面這些事情:
- 管理容器的生命周期(從創(chuàng)建容器到銷毀容器)
- 拉取/推送容器鏡像
- 存儲(chǔ)管理(管理鏡像及容器數(shù)據(jù)的存儲(chǔ))
- 調(diào)用 runc 運(yùn)行容器(與 runc 等容器運(yùn)行時(shí)交互)
- 管理容器網(wǎng)絡(luò)接口及網(wǎng)絡(luò)
架構(gòu)
containerd 可用作 Linux 和 Windows 的守護(hù)程序,它管理其主機(jī)系統(tǒng)完整的容器生命周期,從鏡像傳輸和存儲(chǔ)到容器執(zhí)行和監(jiān)測,再到底層存儲(chǔ)到網(wǎng)絡(luò)附件等等。
containerd 架構(gòu)
上圖是 containerd 官方提供的架構(gòu)圖,可以看出 containerd 采用的也是 C/S 架構(gòu),服務(wù)端通過 unix domain socket 暴露低層的 gRPC API 接口出去,客戶端通過這些 API 管理節(jié)點(diǎn)上的容器,每個(gè) containerd 只負(fù)責(zé)一臺(tái)機(jī)器,Pull 鏡像,對容器的操作(啟動(dòng)、停止等),網(wǎng)絡(luò),存儲(chǔ)都是由 containerd 完成。具體運(yùn)行容器由 runc 負(fù)責(zé),實(shí)際上只要是符合 OCI 規(guī)范的容器都可以支持。
為了解耦,containerd 將系統(tǒng)劃分成了不同的組件,每個(gè)組件都由一個(gè)或多個(gè)模塊協(xié)作完成(Core 部分),每一種類型的模塊都以插件的形式集成到 Containerd 中,而且插件之間是相互依賴的,例如,上圖中的每一個(gè)長虛線的方框都表示一種類型的插件,包括 Service Plugin、Metadata Plugin、GC Plugin、Runtime Plugin 等,其中 Service Plugin 又會(huì)依賴 Metadata Plugin、GC Plugin 和 Runtime Plugin。每一個(gè)小方框都表示一個(gè)細(xì)分的插件,例如 Metadata Plugin 依賴 Containers Plugin、Content Plugin 等。比如:
- Content Plugin: 提供對鏡像中可尋址內(nèi)容的訪問,所有不可變的內(nèi)容都被存儲(chǔ)在這里。
- Snapshot Plugin: 用來管理容器鏡像的文件系統(tǒng)快照,鏡像中的每一層都會(huì)被解壓成文件系統(tǒng)快照,類似于 Docker 中的 graphdriver。
總體來看 containerd 可以分為三個(gè)大塊:Storage、Metadata 和 Runtime。
containerd 架構(gòu)2
安裝
這里我使用的系統(tǒng)是 Linux Mint 20.2,首先需要安裝 seccomp 依賴:
- ~ apt-get update
- ~ apt-get install libseccomp2 -y
由于 containerd 需要調(diào)用 runc,所以我們也需要先安裝 runc,不過 containerd 提供了一個(gè)包含相關(guān)依賴的壓縮包 cri-containerd-cni-${VERSION}.${OS}-${ARCH}.tar.gz,可以直接使用這個(gè)包來進(jìn)行安裝。首先從 release 頁面下載最新版本的壓縮包,當(dāng)前為 1.5.5 版本:
- ~ wget https://github.com/containerd/containerd/releases/download/v1.5.5/cri-containerd-cni-1.5.5-linux-amd64.tar.gz
- # 如果有限制,也可以替換成下面的 URL 加速下載
- # wget https://download.fastgit.org/containerd/containerd/releases/download/v1.5.5/cri-containerd-cni-1.5.5-linux-amd64.tar.gz
可以通過 tar 的 -t 選項(xiàng)直接看到壓縮包中包含哪些文件:
- ~ tar -tf cri-containerd-cni-1.4.3-linux-amd64.tar.gz
- etc/
- etc/cni/
- etc/cni/net.d/
- etc/cni/net.d/10-containerd-net.conflist
- etc/crictl.yaml
- etc/systemd/
- etc/systemd/system/
- etc/systemd/system/containerd.service
- usr/
- usr/local/
- usr/local/bin/
- usr/local/bin/containerd-shim-runc-v2
- usr/local/bin/ctr
- usr/local/bin/containerd-shim
- usr/local/bin/containerd-shim-runc-v1
- usr/local/bin/crictl
- usr/local/bin/critest
- usr/local/bin/containerd
- usr/local/sbin/
- usr/local/sbin/runc
- opt/
- opt/cni/
- opt/cni/bin/
- opt/cni/bin/vlan
- opt/cni/bin/host-local
- opt/cni/bin/flannel
- opt/cni/bin/bridge
- opt/cni/bin/host-device
- opt/cni/bin/tuning
- opt/cni/bin/firewall
- opt/cni/bin/bandwidth
- opt/cni/bin/ipvlan
- opt/cni/bin/sbr
- opt/cni/bin/dhcp
- opt/cni/bin/portmap
- opt/cni/bin/ptp
- opt/cni/bin/static
- opt/cni/bin/macvlan
- opt/cni/bin/loopback
- opt/containerd/
- opt/containerd/cluster/
- opt/containerd/cluster/version
- opt/containerd/cluster/gce/
- opt/containerd/cluster/gce/cni.template
- opt/containerd/cluster/gce/configure.sh
- opt/containerd/cluster/gce/cloud-init/
- opt/containerd/cluster/gce/cloud-init/master.yaml
- opt/containerd/cluster/gce/cloud-init/node.yaml
- opt/containerd/cluster/gce/env
直接將壓縮包解壓到系統(tǒng)的各個(gè)目錄中:
- ~ tar -C / -xzf cri-containerd-cni-1.5.5-linux-amd64.tar.gz
當(dāng)然要記得將 /usr/local/bin 和 /usr/local/sbin 追加到 ~/.bashrc 文件的 PATH 環(huán)境變量中:
- export PATH=$PATH:/usr/local/bin:/usr/local/sbin
然后執(zhí)行下面的命令使其立即生效:
- ~ source ~/.bashrc
containerd 的默認(rèn)配置文件為 /etc/containerd/config.toml,我們可以通過如下所示的命令生成一個(gè)默認(rèn)的配置:
- ~ mkdir /etc/containerd
- ~ containerd config default > /etc/containerd/config.toml
由于上面我們下載的 containerd 壓縮包中包含一個(gè) etc/systemd/system/containerd.service 的文件,這樣我們就可以通過 systemd 來配置 containerd 作為守護(hù)進(jìn)程運(yùn)行了,內(nèi)容如下所示:
- ~ cat /etc/systemd/system/containerd.service
- [Unit]
- Description=containerd container runtime
- Documentation=https://containerd.io
- After=network.target local-fs.target
- [Service]
- ExecStartPre=-/sbin/modprobe overlay
- ExecStart=/usr/local/bin/containerd
- Type=notify
- Delegate=yes
- KillMode=process
- Restart=always
- RestartSec=5
- # Having non-zero Limit*s causes performance problems due to accounting overhead
- # in the kernel. We recommend using cgroups to do container-local accounting.
- LimitNPROC=infinity
- LimitCORE=infinity
- LimitNOFILE=1048576
- # Comment TasksMax if your systemd version does not supports it.
- # Only systemd 226 and above support this version.
- TasksMax=infinity
- OOMScoreAdjust=-999
- [Install]
- WantedBy=multi-user.target
這里有兩個(gè)重要的參數(shù):
Delegate: 這個(gè)選項(xiàng)允許 containerd 以及運(yùn)行時(shí)自己管理自己創(chuàng)建容器的 cgroups。如果不設(shè)置這個(gè)選項(xiàng),systemd 就會(huì)將進(jìn)程移到自己的 cgroups 中,從而導(dǎo)致 containerd 無法正確獲取容器的資源使用情況。
KillMode: 這個(gè)選項(xiàng)用來處理 containerd 進(jìn)程被殺死的方式。默認(rèn)情況下,systemd 會(huì)在進(jìn)程的 cgroup 中查找并殺死 containerd 的所有子進(jìn)程。KillMode 字段可以設(shè)置的值如下。
- control-group(默認(rèn)值):當(dāng)前控制組里面的所有子進(jìn)程,都會(huì)被殺掉
- process:只殺主進(jìn)程
- mixed:主進(jìn)程將收到 SIGTERM 信號(hào),子進(jìn)程收到 SIGKILL 信號(hào)
- none:沒有進(jìn)程會(huì)被殺掉,只是執(zhí)行服務(wù)的 stop 命令
我們需要將 KillMode 的值設(shè)置為 process,這樣可以確保升級或重啟 containerd 時(shí)不殺死現(xiàn)有的容器。
現(xiàn)在我們就可以啟動(dòng) containerd 了,直接執(zhí)行下面的命令即可:
- ~ systemctl enable containerd --now
啟動(dòng)完成后就可以使用 containerd 的本地 CLI 工具 ctr 了,比如查看版本:
ctr version
配置
我們首先來查看下上面默認(rèn)生成的配置文件 /etc/containerd/config.toml:
- disabled_plugins = []
- imports = []
- oom_score = 0
- plugin_dir = ""
- required_plugins = []
- root = "/var/lib/containerd"
- state = "/run/containerd"
- version = 2
- [cgroup]
- path = ""
- [debug]
- address = ""
- format = ""
- gid = 0
- level = ""
- uid = 0
- [grpc]
- address = "/run/containerd/containerd.sock"
- gid = 0
- max_recv_message_size = 16777216
- max_send_message_size = 16777216
- tcp_address = ""
- tcp_tls_cert = ""
- tcp_tls_key = ""
- uid = 0
- [metrics]
- address = ""
- grpc_histogram = false
- [plugins]
- [plugins."io.containerd.gc.v1.scheduler"]
- deletion_threshold = 0
- mutation_threshold = 100
- pause_threshold = 0.02
- schedule_delay = "0s"
- startup_delay = "100ms"
- [plugins."io.containerd.grpc.v1.cri"]
- disable_apparmor = false
- disable_cgroup = false
- disable_hugetlb_controller = true
- disable_proc_mount = false
- disable_tcp_service = true
- enable_selinux = false
- enable_tls_streaming = false
- ignore_image_defined_volumes = false
- max_concurrent_downloads = 3
- max_container_log_line_size = 16384
- netns_mounts_under_state_dir = false
- restrict_oom_score_adj = false
- sandbox_image = "k8s.gcr.io/pause:3.5"
- selinux_category_range = 1024
- stats_collect_period = 10
- stream_idle_timeout = "4h0m0s"
- stream_server_address = "127.0.0.1"
- stream_server_port = "0"
- systemd_cgroup = false
- tolerate_missing_hugetlb_controller = true
- unset_seccomp_profile = ""
- [plugins."io.containerd.grpc.v1.cri".cni]
- bin_dir = "/opt/cni/bin"
- conf_dir = "/etc/cni/net.d"
- conf_template = ""
- max_conf_num = 1
- [plugins."io.containerd.grpc.v1.cri".containerd]
- default_runtime_name = "runc"
- disable_snapshot_annotations = true
- discard_unpacked_layers = false
- no_pivot = false
- snapshotter = "overlayfs"
- [plugins."io.containerd.grpc.v1.cri".containerd.default_runtime]
- base_runtime_spec = ""
- container_annotations = []
- pod_annotations = []
- privileged_without_host_devices = false
- runtime_engine = ""
- runtime_root = ""
- runtime_type = ""
- [plugins."io.containerd.grpc.v1.cri".containerd.default_runtime.options]
- [plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
- [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
- base_runtime_spec = ""
- container_annotations = []
- pod_annotations = []
- privileged_without_host_devices = false
- runtime_engine = ""
- runtime_root = ""
- runtime_type = "io.containerd.runc.v2"
- [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
- BinaryName = ""
- CriuImagePath = ""
- CriuPath = ""
- CriuWorkPath = ""
- IoGid = 0
- IoUid = 0
- NoNewKeyring = false
- NoPivotRoot = false
- Root = ""
- ShimCgroup = ""
- SystemdCgroup = false
- [plugins."io.containerd.grpc.v1.cri".containerd.untrusted_workload_runtime]
- base_runtime_spec = ""
- container_annotations = []
- pod_annotations = []
- privileged_without_host_devices = false
- runtime_engine = ""
- runtime_root = ""
- runtime_type = ""
- [plugins."io.containerd.grpc.v1.cri".containerd.untrusted_workload_runtime.options]
- [plugins."io.containerd.grpc.v1.cri".image_decryption]
- key_model = "node"
- [plugins."io.containerd.grpc.v1.cri".registry]
- config_path = ""
- [plugins."io.containerd.grpc.v1.cri".registry.auths]
- [plugins."io.containerd.grpc.v1.cri".registry.configs]
- [plugins."io.containerd.grpc.v1.cri".registry.headers]
- [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
- [plugins."io.containerd.grpc.v1.cri".x509_key_pair_streaming]
- tls_cert_file = ""
- tls_key_file = ""
- [plugins."io.containerd.internal.v1.opt"]
- path = "/opt/containerd"
- [plugins."io.containerd.internal.v1.restart"]
- interval = "10s"
- [plugins."io.containerd.metadata.v1.bolt"]
- content_sharing_policy = "shared"
- [plugins."io.containerd.monitor.v1.cgroups"]
- no_prometheus = false
- [plugins."io.containerd.runtime.v1.linux"]
- no_shim = false
- runtime = "runc"
- runtime_root = ""
- shim = "containerd-shim"
- shim_debug = false
- [plugins."io.containerd.runtime.v2.task"]
- platforms = ["linux/amd64"]
- [plugins."io.containerd.service.v1.diff-service"]
- default = ["walking"]
- [plugins."io.containerd.snapshotter.v1.aufs"]
- root_path = ""
- [plugins."io.containerd.snapshotter.v1.btrfs"]
- root_path = ""
- [plugins."io.containerd.snapshotter.v1.devmapper"]
- async_remove = false
- base_image_size = ""
- pool_name = ""
- root_path = ""
- [plugins."io.containerd.snapshotter.v1.native"]
- root_path = ""
- [plugins."io.containerd.snapshotter.v1.overlayfs"]
- root_path = ""
- [plugins."io.containerd.snapshotter.v1.zfs"]
- root_path = ""
- [proxy_plugins]
- [stream_processors]
- [stream_processors."io.containerd.ocicrypt.decoder.v1.tar"]
- accepts = ["application/vnd.oci.image.layer.v1.tar+encrypted"]
- args = ["--decryption-keys-path", "/etc/containerd/ocicrypt/keys"]
- env = ["OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf"]
- path = "ctd-decoder"
- returns = "application/vnd.oci.image.layer.v1.tar"
- [stream_processors."io.containerd.ocicrypt.decoder.v1.tar.gzip"]
- accepts = ["application/vnd.oci.image.layer.v1.tar+gzip+encrypted"]
- args = ["--decryption-keys-path", "/etc/containerd/ocicrypt/keys"]
- env = ["OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf"]
- path = "ctd-decoder"
- returns = "application/vnd.oci.image.layer.v1.tar+gzip"
- [timeouts]
- "io.containerd.timeout.shim.cleanup" = "5s"
- "io.containerd.timeout.shim.load" = "5s"
- "io.containerd.timeout.shim.shutdown" = "3s"
- "io.containerd.timeout.task.state" = "2s"
- [ttrpc]
- address = ""
- gid = 0
- uid = 0
這個(gè)配置文件比較復(fù)雜,我們可以將重點(diǎn)放在其中的 plugins 配置上面,仔細(xì)觀察我們可以發(fā)現(xiàn)每一個(gè)頂級配置塊的命名都是 plugins."io.containerd.xxx.vx.xxx" 這種形式,每一個(gè)頂級配置塊都表示一個(gè)插件,其中 io.containerd.xxx.vx 表示插件的類型,vx 后面的 xxx 表示插件的 ID,我們可以通過 ctr 查看插件列表:
- ~ ctr plugin ls
- ctr plugin ls
- TYPE ID PLATFORMS STATUS
- io.containerd.content.v1 content - ok
- io.containerd.snapshotter.v1 aufs linux/amd64 ok
- io.containerd.snapshotter.v1 btrfs linux/amd64 skip
- io.containerd.snapshotter.v1 devmapper linux/amd64 error
- io.containerd.snapshotter.v1 native linux/amd64 ok
- io.containerd.snapshotter.v1 overlayfs linux/amd64 ok
- io.containerd.snapshotter.v1 zfs linux/amd64 skip
- io.containerd.metadata.v1 bolt - ok
- io.containerd.differ.v1 walking linux/amd64 ok
- io.containerd.gc.v1 scheduler - ok
- io.containerd.service.v1 introspection-service - ok
- io.containerd.service.v1 containers-service - ok
- io.containerd.service.v1 content-service - ok
- io.containerd.service.v1 diff-service - ok
- io.containerd.service.v1 images-service - ok
- io.containerd.service.v1 leases-service - ok
- io.containerd.service.v1 namespaces-service - ok
- io.containerd.service.v1 snapshots-service - ok
- io.containerd.runtime.v1 linux linux/amd64 ok
- io.containerd.runtime.v2 task linux/amd64 ok
- io.containerd.monitor.v1 cgroups linux/amd64 ok
- io.containerd.service.v1 tasks-service - ok
- io.containerd.internal.v1 restart - ok
- io.containerd.grpc.v1 containers - ok
- io.containerd.grpc.v1 content - ok
- io.containerd.grpc.v1 diff - ok
- io.containerd.grpc.v1 events - ok
- io.containerd.grpc.v1 healthcheck - ok
- io.containerd.grpc.v1 images - ok
- io.containerd.grpc.v1 leases - ok
- io.containerd.grpc.v1 namespaces - ok
- io.containerd.internal.v1 opt - ok
- io.containerd.grpc.v1 snapshots - ok
- io.containerd.grpc.v1 tasks - ok
- io.containerd.grpc.v1 version - ok
- io.containerd.grpc.v1 cri linux/amd64 ok
頂級配置塊下面的子配置塊表示該插件的各種配置,比如 cri 插件下面就分為 containerd、cni 和 registry 的配置,而 containerd 下面又可以配置各種 runtime,還可以配置默認(rèn)的 runtime。比如現(xiàn)在我們要為鏡像配置一個(gè)加速器,那么就需要在 cri 配置塊下面的 registry 配置塊下面進(jìn)行配置 registry.mirrors:
- [plugins."io.containerd.grpc.v1.cri".registry]
- [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
- [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
- endpoint = ["https://bqr1dr1n.mirror.aliyuncs.com"]
- [plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"]
- endpoint = ["https://registry.aliyuncs.com/k8sxio"]
- registry.mirrors."xxx": 表示需要配置 mirror 的鏡像倉庫,例如 registry.mirrors."docker.io" 表示配置 docker.io 的 mirror。
- endpoint: 表示提供 mirror 的鏡像加速服務(wù),比如我們可以注冊一個(gè)阿里云的鏡像服務(wù)來作為 docker.io 的 mirror。
另外在默認(rèn)配置中還有兩個(gè)關(guān)于存儲(chǔ)的配置路徑:
- root = "/var/lib/containerd"
- state = "/run/containerd"
其中 root 是用來保存持久化數(shù)據(jù),包括 Snapshots, Content, Metadata 以及各種插件的數(shù)據(jù),每一個(gè)插件都有自己單獨(dú)的目錄,Containerd 本身不存儲(chǔ)任何數(shù)據(jù),它的所有功能都來自于已加載的插件。
而另外的 state 是用來保存運(yùn)行時(shí)的臨時(shí)數(shù)據(jù)的,包括 sockets、pid、掛載點(diǎn)、運(yùn)行時(shí)狀態(tài)以及不需要持久化的插件數(shù)據(jù)。
使用
我們知道 Docker CLI 工具提供了需要增強(qiáng)用戶體驗(yàn)的功能,containerd 同樣也提供一個(gè)對應(yīng)的 CLI 工具:ctr,不過 ctr 的功能沒有 docker 完善,但是關(guān)于鏡像和容器的基本功能都是有的。接下來我們就先簡單介紹下 ctr 的使用。
幫助
直接輸入 ctr 命令即可獲得所有相關(guān)的操作命令使用方式:
- ~ ctr
- NAME:
- ctr -
- __
- _____/ /______
- / ___/ __/ ___/
- / /__/ /_/ /
- \___/\__/_/
- containerd CLI
- USAGE:
- ctr [global options] command [command options] [arguments...]
- VERSION:
- v1.5.5
- DESCRIPTION:
- ctr is an unsupported debug and administrative client for interacting
- with the containerd daemon. Because it is unsupported, the commands,
- options, and operations are not guaranteed to be backward compatible or
- stable from release to release of the containerd project.
- COMMANDS:
- plugins, plugin provides information about containerd plugins
- version print the client and server versions
- containers, c, container manage containers
- content manage content
- events, event display containerd events
- images, image, i manage images
本文題目:一文搞定Containerd的使用
網(wǎng)址分享:http://m.fisionsoft.com.cn/article/dhepcee.html


咨詢
建站咨詢
