Containerd 的前世今生和保姆級入門教程
1. Containerd 的前世今生
很久以前,Docker 強勢崛起,以“鏡像”這個大招席卷全球,對其他容器技術(shù)進行致命的降維打擊,使其毫無招架之力,就連 Google 也不例外。Google 為了不被拍死在沙灘上,被迫拉下臉面(當(dāng)然,跪舔是不可能的),希望 Docker 公司和自己聯(lián)合推進一個開源的容器運行時作為 Docker 的核心依賴,不然就走著瞧。Docker 公司覺得自己的智商被侮辱了,走著瞧就走著瞧,誰怕誰啊!
很明顯,Docker 公司的這個決策斷送了自己的大好前程,造成了今天的悲劇。
緊接著,Google 聯(lián)合 Red Hat、IBM 等幾位巨佬連哄帶騙忽悠 Docker 公司將?libcontainer?捐給中立的社區(qū)(OCI,Open Container Intiative),并改名為?runc,不留一點 Docker 公司的痕跡~~
這還不夠,為了徹底扭轉(zhuǎn) Docker 一家獨大的局面,幾位大佬又合伙成立了一個基金會叫?CNCF(Cloud Native Computing Fundation),這個名字想必大家都很熟了,我就不詳細介紹了。CNCF 的目標(biāo)很明確,既然在當(dāng)前的維度上干不過 Docker,干脆往上爬,升級到大規(guī)模容器編排的維度,以此來擊敗 Docker。
Docker 公司當(dāng)然不甘示弱,搬出了 Swarm 和 Kubernetes 進行 PK,最后的結(jié)局大家都知道了,Swarm 戰(zhàn)敗。然后 Docker 公司耍了個小聰明,將自己的核心依賴?Containerd?捐給了 CNCF,以此來標(biāo)榜 Docker 是一個 PaaS 平臺。
很明顯,這個小聰明又大大加速了自己的滅亡。

巨佬們心想,想當(dāng)初想和你合作搞個中立的核心運行時,你死要面子活受罪,就是不同意,好家伙,現(xiàn)在自己搞了一個,還捐出來了,馬老師,發(fā)生甚莫事了?
這好嗎?
這不好
也罷,這倒省事了,我就直接拿?Containerd?來做文章吧。
首先呢,為了表示 Kubernetes 的中立性,當(dāng)然要搞個標(biāo)準(zhǔn)化的容器運行時接口,只要適配了這個接口的容器運行時,都可以和我一起玩耍哦,第一個支持這個接口的當(dāng)然就是?Containerd?啦。至于這個接口的名字,大家應(yīng)該都知道了,它叫 CRI(Container Runntime Interface)。
這樣還不行,為了蠱惑 Docker 公司,Kubernetes 暫時先委屈自己,專門在自己的組件中集成了一個?shim(你可以理解為墊片),用來將 CRI 的調(diào)用翻譯成 Docker 的 API,讓 Docker 也能和自己愉快地玩耍,溫水煮青蛙,養(yǎng)肥了再殺。。。
就這樣,Kubernetes 一邊假裝和 Docker 愉快玩耍,一邊背地里不斷優(yōu)化 Containerd 的健壯性以及和 CRI 對接的絲滑性?,F(xiàn)在 Containerd 的翅膀已經(jīng)完全硬了,是時候卸下我的偽裝,和 Docker say bye bye 了。后面的事情大家也都知道了~~
Docker 這門技術(shù)成功了,Docker 這個公司卻失敗了。
2. Containerd 架構(gòu)
時至今日,Containerd 已經(jīng)變成一個工業(yè)級的容器運行時了,連口號都有了:超簡單!超健壯!可移植性超強!
當(dāng)然,為了讓 Docker 以為自己不會搶飯碗,Containerd 聲稱自己的設(shè)計目的主要是為了嵌入到一個更大的系統(tǒng)中(暗指 Kubernetes),而不是直接由開發(fā)人員或終端用戶使用。
事實上呢,Containerd 現(xiàn)在基本上啥都能干了,開發(fā)人員或者終端用戶可以在宿主機中管理完整的容器生命周期,包括容器鏡像的傳輸和存儲、容器的執(zhí)行和管理、存儲和網(wǎng)絡(luò)等。大家可以考慮學(xué)起來了。
學(xué)習(xí) Containerd 最好的時機是關(guān)注公眾號 云原生實驗室 后,其次是現(xiàn)在,看完了再關(guān)注公眾號也不遲?。
先來看看 Containerd 的架構(gòu):

可以看到 Containerd 仍然采用標(biāo)準(zhǔn)的 C/S 架構(gòu),服務(wù)端通過?GRPC?協(xié)議提供穩(wěn)定的 API,客戶端通過調(diào)用服務(wù)端的 API 進行高級的操作。
為了解耦,Containerd 將不同的職責(zé)劃分給不同的組件,每個組件就相當(dāng)于一個子系統(tǒng)(subsystem)。連接不同子系統(tǒng)的組件被稱為模塊。
總體上 Containerd 被劃分為兩個子系統(tǒng):
Bundle?: 在 Containerd 中, Bundle?包含了配置、元數(shù)據(jù)和根文件系統(tǒng)數(shù)據(jù),你可以理解為容器的文件系統(tǒng)。而?Bundle 子系統(tǒng)允許用戶從鏡像中提取和打包 Bundles。Runtime?: Runtime 子系統(tǒng)用來執(zhí)行 Bundles,比如創(chuàng)建容器。
其中,每一個子系統(tǒng)的行為都由一個或多個模塊協(xié)作完成(架構(gòu)圖中的?Core?部分)。每一種類型的模塊都以插件的形式集成到 Containerd 中,而且插件之間是相互依賴的。例如,上圖中的每一個長虛線的方框都表示一種類型的插件,包括?Service Plugin、Metadata Plugin、GC Plugin、Runtime Plugin?等,其中?Service Plugin?又會依賴 Metadata Plugin、GC Plugin 和 Runtime Plugin。每一個小方框都表示一個細分的插件,例如?Metadata Plugin?依賴 Containers Plugin、Content Plugin 等??傊?,萬物皆插件,插件就是模塊,模塊就是插件。

這里介紹幾個常用的插件:
Content Plugin?: 提供對鏡像中可尋址內(nèi)容的訪問,所有不可變的內(nèi)容都被存儲在這里。 Snapshot Plugin?: 用來管理容器鏡像的文件系統(tǒng)快照。鏡像中的每一個 layer 都會被解壓成文件系統(tǒng)快照,類似于 Docker 中的? graphdriver。Metrics?: 暴露各個組件的監(jiān)控指標(biāo)。
從總體來看,Containerd 被分為三個大塊:Storage、Metadata?和?Runtime,可以將上面的架構(gòu)圖提煉一下:

這是使用?bucketbench[1] 對?Docker、crio?和?Containerd?的性能測試結(jié)果,包括啟動、停止和刪除容器,以比較它們所耗的時間:

可以看到 Containerd 在各個方面都表現(xiàn)良好,總體性能還是優(yōu)越于?Docker?和?crio?的。
3. Containerd 安裝
了解了 Containerd 的概念后,就可以動手安裝體驗一把了。本文的演示環(huán)境為?Ubuntu 18.04。
安裝依賴
為 seccomp 安裝依賴:
???→?sudo?apt-get?update
???→?sudo?apt-get?install?libseccomp2
下載并解壓 Containerd 程序
Containerd 提供了兩個壓縮包,一個叫?containerd-${VERSION}.${OS}-${ARCH}.tar.gz,另一個叫?cri-containerd-${VERSION}.${OS}-${ARCH}.tar.gz。其中?cri-containerd-${VERSION}.${OS}-${ARCH}.tar.gz?包含了所有 Kubernetes 需要的二進制文件。如果你只是本地測試,可以選擇前一個壓縮包;如果是作為 Kubernetes 的容器運行時,需要選擇后一個壓縮包。
Containerd 是需要調(diào)用?runc?的,而第一個壓縮包是不包含?runc?二進制文件的,如果你選擇第一個壓縮包,還需要提前安裝 runc。所以我建議直接使用?cri-containerd?壓縮包。
首先從?release 頁面[2]下載最新版本的壓縮包,當(dāng)前最新版本為 1.4.3:
???→?wget?https://github.com/containerd/containerd/releases/download/v1.4.3/cri-containerd-cni-1.4.3-linux-amd64.tar.gz
#?也可以替換成下面的?URL?加速下載
???→?wget?https://download.fastgit.org/containerd/containerd/releases/download/v1.4.3/cri-containerd-cni-1.4.3-linux-amd64.tar.gz
可以通過 tar 的?-t?選項直接看到壓縮包中包含哪些文件:
???→?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)的各個目錄中:
???→?sudo?tar?-C?/?-xzf?cri-containerd-cni-1.4.3-linux-amd64.tar.gz
將?/usr/local/bin?和?/usr/local/sbin?追加到?~/.bashrc?文件的?$PATH?環(huán)境變量中:
export?PATH=$PATH:/usr/local/bin:/usr/local/sbin
立即生效:
???→?source?~/.bashrc
查看版本:
???→?ctr?version
Client:
??Version:??v1.4.3
??Revision:?269548fa27e0089a8b8278fc4fc781d7f65a939b
??Go?version:?go1.15.5
Server:
??Version:??v1.4.3
??Revision:?269548fa27e0089a8b8278fc4fc781d7f65a939b
??UUID:?d1724999-91b3-4338-9288-9a54c9d52f70
生成配置文件
Containerd 的默認(rèn)配置文件為?/etc/containerd/config.toml,我們可以通過命令來生成一個默認(rèn)的配置:
???→?mkdir?/etc/containerd
???→?containerd?config?default?>?/etc/containerd/config.toml
鏡像加速
由于某些不可描述的因素,在國內(nèi)拉取公共鏡像倉庫的速度是極慢的,為了節(jié)約拉取時間,需要為 Containerd 配置鏡像倉庫的?mirror。Containerd 的鏡像倉庫 mirror 與 Docker 相比有兩個區(qū)別:
Containerd 只支持通過? CRI?拉取鏡像的 mirror,也就是說,只有通過?crictl?或者 Kubernetes 調(diào)用時 mirror 才會生效,通過?ctr?拉取是不會生效的。Docker?只支持為?Docker Hub?配置 mirror,而?Containerd?支持為任意鏡像倉庫配置 mirror。
配置鏡像加速之前,先來看下 Containerd 的配置結(jié)構(gòu),乍一看可能會覺得很復(fù)雜,復(fù)雜就復(fù)雜在 plugin 的配置部分:
[plugins]
??[plugins."io.containerd.gc.v1.scheduler"]
????pause_threshold?=?0.02
????deletion_threshold?=?0
????mutation_threshold?=?100
????schedule_delay?=?"0s"
????startup_delay?=?"100ms"
??[plugins."io.containerd.grpc.v1.cri"]
????disable_tcp_service?=?true
????stream_server_address?=?"127.0.0.1"
????stream_server_port?=?"0"
????stream_idle_timeout?=?"4h0m0s"
????enable_selinux?=?false
????sandbox_image?=?"k8s.gcr.io/pause:3.1"
????stats_collect_period?=?10
????systemd_cgroup?=?false
????enable_tls_streaming?=?false
????max_container_log_line_size?=?16384
????disable_cgroup?=?false
????disable_apparmor?=?false
????restrict_oom_score_adj?=?false
????max_concurrent_downloads?=?3
????disable_proc_mount?=?false
????[plugins."io.containerd.grpc.v1.cri".containerd]
??????snapshotter?=?"overlayfs"
??????default_runtime_name?=?"runc"
??????no_pivot?=?false
??????[plugins."io.containerd.grpc.v1.cri".containerd.default_runtime]
????????runtime_type?=?""
????????runtime_engine?=?""
????????runtime_root?=?""
????????privileged_without_host_devices?=?false
??????[plugins."io.containerd.grpc.v1.cri".containerd.untrusted_workload_runtime]
????????runtime_type?=?""
????????runtime_engine?=?""
????????runtime_root?=?""
????????privileged_without_host_devices?=?false
??????[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
????????[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
??????????runtime_type?=?"io.containerd.runc.v1"
??????????runtime_engine?=?""
??????????runtime_root?=?""
??????????privileged_without_host_devices?=?false
????[plugins."io.containerd.grpc.v1.cri".cni]
??????bin_dir?=?"/opt/cni/bin"
??????conf_dir?=?"/etc/cni/net.d"
??????max_conf_num?=?1
??????conf_template?=?""
????[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://registry-1.docker.io"]
????[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"]
????shim?=?"containerd-shim"
????runtime?=?"runc"
????runtime_root?=?""
????no_shim?=?false
????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.devmapper"]
????root_path?=?""
????pool_name?=?""
????base_image_size?=?""
每一個頂級配置塊的命名都是?plugins."io.containerd.xxx.vx.xxx"?這種形式,其實每一個頂級配置塊都代表一個插件,其中?io.containerd.xxx.vx?表示插件的類型,vx 后面的 xxx 表示插件的?ID??梢酝ㄟ^?ctr?一覽無余:
???→?ctr?plugin?ls
TYPE????????????????????????????ID????????????????????PLATFORMS??????STATUS
io.containerd.content.v1????????content???????????????-??????????????ok
io.containerd.snapshotter.v1????btrfs?????????????????linux/amd64????error
io.containerd.snapshotter.v1????devmapper?????????????linux/amd64????error
io.containerd.snapshotter.v1????aufs??????????????????linux/amd64????ok
io.containerd.snapshotter.v1????native????????????????linux/amd64????ok
io.containerd.snapshotter.v1????overlayfs?????????????linux/amd64????ok
io.containerd.snapshotter.v1????zfs???????????????????linux/amd64????error
io.containerd.metadata.v1???????bolt??????????????????-??????????????ok
io.containerd.differ.v1?????????walking???????????????linux/amd64????ok
io.containerd.gc.v1?????????????scheduler?????????????-??????????????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。
鏡像加速的配置就在 cri 插件配置塊下面的 registry 配置塊,所以需要修改的部分如下:
????[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://dockerhub.mirrors.nwafu.edu.cn"]
????????[plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"]
??????????endpoint?=?["https://registry.aliyuncs.com/k8sxio"]
????????[plugins."io.containerd.grpc.v1.cri".registry.mirrors."gcr.io"]
??????????endpoint?=?["xxx"]
registry.mirrors."xxx"?: 表示需要配置 mirror 的鏡像倉庫。例如, registry.mirrors."docker.io"?表示配置 docker.io 的 mirror。endpoint?: 表示提供 mirror 的鏡像加速服務(wù)。例如,這里推薦使用西北農(nóng)林科技大學(xué)提供的鏡像加速服務(wù)作為? docker.io?的 mirror。
至于?gcr.io,目前還沒有公共的加速服務(wù)。我自己掏錢搭了個加速服務(wù),拉取速度大概是?3M/s?左右,有加速需求的同學(xué)可以通過微信號:cloud-native-yang 加我為好友再詳細咨詢。
存儲配置
Containerd 有兩個不同的存儲路徑,一個用來保存持久化數(shù)據(jù),一個用來保存運行時狀態(tài)。
root?=?"/var/lib/containerd"
state?=?"/run/containerd"
root用來保存持久化數(shù)據(jù),包括?Snapshots,?Content,?Metadata?以及各種插件的數(shù)據(jù)。每一個插件都有自己單獨的目錄,Containerd 本身不存儲任何數(shù)據(jù),它的所有功能都來自于已加載的插件,真是太機智了。
???→?tree?-L?2?/var/lib/containerd/
/var/lib/containerd/
├──?io.containerd.content.v1.content
│???├──?blobs
│???└──?ingest
├──?io.containerd.grpc.v1.cri
│???├──?containers
│???└──?sandboxes
├──?io.containerd.metadata.v1.bolt
│???└──?meta.db
├──?io.containerd.runtime.v1.linux
│???└──?k8s.io
├──?io.containerd.runtime.v2.task
├──?io.containerd.snapshotter.v1.aufs
│???└──?snapshots
├──?io.containerd.snapshotter.v1.btrfs
├──?io.containerd.snapshotter.v1.native
│???└──?snapshots
├──?io.containerd.snapshotter.v1.overlayfs
│???├──?metadata.db
│???└──?snapshots
└──?tmpmounts
18?directories,?2?files
state?用來保存臨時數(shù)據(jù),包括 sockets、pid、掛載點、運行時狀態(tài)以及不需要持久化保存的插件數(shù)據(jù)。
???→?tree?-L?2?/run/containerd/
/run/containerd/
├──?containerd.sock
├──?containerd.sock.ttrpc
├──?io.containerd.grpc.v1.cri
│???├──?containers
│???└──?sandboxes
├──?io.containerd.runtime.v1.linux
│???└──?k8s.io
├──?io.containerd.runtime.v2.task
└──?runc
????└──?k8s.io
8?directories,?2?files
OOM
還有一項配置需要留意:
oom_score?=?0
Containerd 是容器的守護者,一旦發(fā)生內(nèi)存不足的情況,理想的情況應(yīng)該是先殺死容器,而不是殺死 Containerd。所以需要調(diào)整 Containerd 的?OOM?權(quán)重,減少其被?OOM Kill?的幾率。最好是將?oom_score?的值調(diào)整為比其他守護進程略低的值。這里的 oom_socre 其實對應(yīng)的是?/proc/,在早期的 Linux 內(nèi)核版本里使用?oom_adj?來調(diào)整權(quán)重, 后來改用?oom_socre_adj?了。該文件描述如下:
The value of?
/proc/?is added to the badness score before it is used to determine which task to kill. Acceptable values range from -1000 (OOM_SCORE_ADJ_MIN) to +1000 (OOM_SCORE_ADJ_MAX). This allows userspace to polarize the preference for oom killing either by always preferring a certain task or completely disabling it. The lowest possible value, -1000, is equivalent to disabling oom killing entirely for that task since it will always report a badness score of 0./oom_score_adj
在計算最終的?badness score?時,會在計算結(jié)果是中加上?oom_score_adj?,這樣用戶就可以通過該在值來保護某個進程不被殺死或者每次都殺某個進程。其取值范圍為?-1000?到?1000。
如果將該值設(shè)置為?-1000,則進程永遠不會被殺死,因為此時?badness score?永遠返回0。
建議 Containerd 將該值設(shè)置為?-999?到?0?之間。如果作為 Kubernetes 的 Worker 節(jié)點,可以考慮設(shè)置為?-999。
Systemd 配置
建議通過 systemd 配置 Containerd 作為守護進程運行,配置文件在上文已經(jīng)被解壓出來了:
???→?cat?/etc/systemd/system/containerd.service
#?Copyright?The?containerd?Authors.
#
#?Licensed?under?the?Apache?License,?Version?2.0?(the?"License");
#?you?may?not?use?this?file?except?in?compliance?with?the?License.
#?You?may?obtain?a?copy?of?the?License?at
#
#?????http://www.apache.org/licenses/LICENSE-2.0
#
#?Unless?required?by?applicable?law?or?agreed?to?in?writing,?software
#?distributed?under?the?License?is?distributed?on?an?"AS?IS"?BASIS,
#?WITHOUT?WARRANTIES?OR?CONDITIONS?OF?ANY?KIND,?either?express?or?implied.
#?See?the?License?for?the?specific?language?governing?permissions?and
#?limitations?under?the?License.
[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
這里有兩個重要的參數(shù):
Delegate?: 這個選項允許 Containerd 以及運行時自己管理自己創(chuàng)建的容器的?
cgroups。如果不設(shè)置這個選項,systemd 就會將進程移到自己的?cgroups?中,從而導(dǎo)致 Containerd 無法正確獲取容器的資源使用情況。KillMode?: 這個選項用來處理 Containerd 進程被殺死的方式。默認(rèn)情況下,systemd 會在進程的 cgroup 中查找并殺死 Containerd 的所有子進程,這肯定不是我們想要的。
KillMode字段可以設(shè)置的值如下。我們需要將 KillMode 的值設(shè)置為?
process,這樣可以確保升級或重啟 Containerd 時不殺死現(xiàn)有的容器。control-group(默認(rèn)值):當(dāng)前控制組里面的所有子進程,都會被殺掉 process:只殺主進程 mixed:主進程將收到 SIGTERM 信號,子進程收到 SIGKILL 信號 none:沒有進程會被殺掉,只是執(zhí)行服務(wù)的 stop 命令。
現(xiàn)在到了最關(guān)鍵的一步:啟動 Containerd。執(zhí)行一條命令就完事:
???→?systemctl?enable?containerd?--now
接下來進入本文最后一部分:Containerd 的基本使用方式。本文只會介紹 Containerd 的本地使用方法,即本地客戶端?ctr?的使用方法,不會涉及到?crictl,后面有機會再介紹?crictl。
4. Containerd 快速安裝
如果你想在一分鐘內(nèi)快速裝好 Kubernetes 和 Containerd,可以使用?Sealos[3] 來部署。該項目旨在做一個簡單干凈輕量級穩(wěn)定的 kubernetes 安裝工具,一條命令,離線安裝,包含所有依賴,內(nèi)核負載不依賴 haproxy keepalived,純 golang 開發(fā),99 年證書。1.20.0 版本的離線包搭載了最新版本的 Containerd,還支持?arm64?架構(gòu),簡直就是簡直了。
部署方法特別簡單,首先下載并安裝?sealos,?sealos?是個 golang 的二進制工具,直接下載拷貝到?bin?目錄即可,?release?頁面也可下載:
???→?wget?-c?https://sealyun.oss-cn-beijing.aliyuncs.com/latest/sealos
???→?chmod?+x?sealos?&&?mv?sealos?/usr/bin
下載離線資源包:
???→?wget?-c?https://sealyun.oss-cn-beijing.aliyuncs.com/7b6af025d4884fdd5cd51a674994359c-1.18.0/kube1.18.0.tar.gz
安裝一個三 master 的高可用 Kubernetes 集群:
???→?sealos?init?--passwd?123456?
--master?192.168.0.2??--master?192.168.0.3??--master?192.168.0.4??
--node?192.168.0.5?
--pkg-url?/root/kube1.18.0.tar.gz?
--version?v1.18.0
然后就完事了。。。
詳細使用方式請訪問 https://sealyun.com。
5. ctr 使用
ctr 目前很多功能做的還沒有 docker 那么完善,但基本功能已經(jīng)具備了。下面將圍繞鏡像和容器這兩個方面來介紹其使用方法。
鏡像
鏡像下載:
???→?ctr?i?pull?docker.io/library/nginx:alpine
docker.io/library/nginx:alpine:???????????????????????????????????????????????????resolved???????|++++++++++++++++++++++++++++++++++++++|
index-sha256:efc93af57bd255ffbfb12c89ec0714dd1a55f16290eb26080e3d1e7e82b3ea66:????done???????????|++++++++++++++++++++++++++++++++++++++|
manifest-sha256:6ceeeab513f7d15cea38c1f8dfe5455323b5a1bfd568516b3b0ee70406f75247:?done???????????|++++++++++++++++++++++++++++++++++++++|
config-sha256:0fde4fb87e476fd1655b3f04f55aa5b4b3ef7de7c701eb46573bb5a5dcf66fd2:???done???????????|++++++++++++++++++++++++++++++++++++++|
layer-sha256:abaddf4965e5e9ce9953f2e136b3bf9cc15365adbcf0c68b108b1cc26c12b1be:????done???????????|++++++++++++++++++++++++++++++++++++++|
layer-sha256:05e7bc50f07f000e9993ec0d264b9ffcbb9a01a4d69c68f556d25e9811a8f7f4:????done???????????|++++++++++++++++++++++++++++++++++++++|
layer-sha256:c78f7f670e47cf98494e7dbe08e463d34c160bf6a5939a2155ff4438cb8b0e80:????done???????????|++++++++++++++++++++++++++++++++++++++|
layer-sha256:ce77cf6a2ede66c463dcdd39f1a43cfbac3723a99e94f697bc20faee0f7cce1b:????done???????????|++++++++++++++++++++++++++++++++++++++|
layer-sha256:3080fd9f46494247c9298a6a3d9694f03f6a32898a07ffbe1c17a0752bae5c4e:????done???????????|++++++++++++++++++++++++++++++++++++++|
elapsed:?17.3s????????????????????????????????????????????????????????????????????total:??8.7?Mi?(513.8?KiB/s)
unpacking?linux/amd64?sha256:efc93af57bd255ffbfb12c89ec0714dd1a55f16290eb26080e3d1e7e82b3ea66...
done
本地鏡像列表查詢:
???→?ctr?i?ls
REF???????????????????????????????????????????????????????????????TYPE??????????????????????????????????????????????????????DIGEST??????????????????????????????????????????????????????????????????SIZE??????PLATFORMS????????????????????????????????????????????????????????????????????????????????LABELS
docker.io/library/nginx:alpine????????????????????????????????????application/vnd.docker.distribution.manifest.list.v2+json?sha256:efc93af57bd255ffbfb12c89ec0714dd1a55f16290eb26080e3d1e7e82b3ea66?9.3?MiB???linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/s390x?-
這里需要注意PLATFORMS,它是鏡像的能夠運行的平臺標(biāo)識。
將鏡像掛載到主機目錄:
???→?ctr?i?mount?docker.io/library/nginx:alpine?/mnt
???→?tree?-L?1?/mnt
/mnt
├──?bin
├──?dev
├──?docker-entrypoint.d
├──?docker-entrypoint.sh
├──?etc
├──?home
├──?lib
├──?media
├──?mnt
├──?opt
├──?proc
├──?root
├──?run
├──?sbin
├──?srv
├──?sys
├──?tmp
├──?usr
└──?var
18?directories,?1?file
將鏡像從主機目錄上卸載:
???→?ctr?i?unmount?/mnt
將鏡像導(dǎo)出為壓縮包:
???→?ctr?i?export?nginx.tar.gz?docker.io/library/nginx:alpine
從壓縮包導(dǎo)入鏡像:
???→?ctr?i?import?nginx.tar.gz
其他操作可以自己查看幫助:
???→?ctr?i?--help
NAME:
???ctr?images?-?manage?images
USAGE:
???ctr?images?command?[command?options]?[arguments...]
COMMANDS:
???check???????check?that?an?image?has?all?content?available?locally
???export??????export?images
???import??????import?images
???list,?ls????list?images?known?to?containerd
???mount???????mount?an?image?to?a?target?path
???unmount?????unmount?the?image?from?the?target
???pull????????pull?an?image?from?a?remote
???push????????push?an?image?to?a?remote
???remove,?rm??remove?one?or?more?images?by?reference
???tag?????????tag?an?image
???label???????set?and?clear?labels?for?an?image
OPTIONS:
???--help,?-h??show?help
對鏡像的更高級操作可以使用子命令?content,例如在線編輯鏡像的?blob?并生成一個新的?digest:
???→?ctr?content?ls
DIGEST?????????SIZE?AGE??LABELS
...
...
sha256:fdd7fff110870339d34cf071ee90fbbe12bdbf3d1d9a14156995dfbdeccd7923?740B?7?days??containerd.io/gc.ref.content.2=sha256:4e537e26e21bf61836f827e773e6e6c3006e3c01c6d59f4b058b09c2753bb929,containerd.io/gc.ref.content.1=sha256:188c0c94c7c576fff0792aca7ec73d67a2f7f4cb3a6e53a84559337260b36964,containerd.io/gc.ref.content.0=sha256:b7199797448c613354489644be1f60aa2d8e9c2278989100c72ede3001334f7b,containerd.io/distribution.source.ghcr.fuckcloudnative.io=yangchuansheng/grafana-backup-tool
???→?ctr?content?edit?--editor?vim?sha256:fdd7fff110870339d34cf071ee90fbbe12bdbf3d1d9a14156995dfbdeccd7923
容器
創(chuàng)建容器:
???→?ctr?c?create?docker.io/library/nginx:alpine?nginx
???→?ctr?c?ls
CONTAINER????IMAGE?????????????????????????????RUNTIME
nginx????????docker.io/library/nginx:alpine????io.containerd.runc.v2
查看容器的詳細配置:
#?和?docker?inspect?類似
???→?ctr?c?info?nginx
其他操作可以自己查看幫助:
???→?ctr?c?--help
NAME:
???ctr?containers?-?manage?containers
USAGE:
???ctr?containers?command?[command?options]?[arguments...]
COMMANDS:
???create???????????create?container
???delete,?del,?rm??delete?one?or?more?existing?containers
???info?????????????get?info?about?a?container
???list,?ls?????????list?containers
???label????????????set?and?clear?labels?for?a?container
???checkpoint???????checkpoint?a?container
???restore??????????restore?a?container?from?checkpoint
OPTIONS:
???--help,?-h??show?help
任務(wù)
上面?create?的命令創(chuàng)建了容器后,并沒有處于運行狀態(tài),只是一個靜態(tài)的容器。一個 container 對象只是包含了運行一個容器所需的資源及配置的數(shù)據(jù)結(jié)構(gòu),這意味著 namespaces、rootfs 和容器的配置都已經(jīng)初始化成功了,只是用戶進程(這里是?nginx)還沒有啟動。
然而一個容器真正的運行起來是由 task 對象實現(xiàn)的,task?代表任務(wù)的意思,可以為容器設(shè)置網(wǎng)卡,還可以配置工具來對容器進行監(jiān)控等。
所以還需要通過 task 啟動容器:
???→?ctr?task?start?-d?nginx
???→?ctr?task?ls
TASK?????PID???????STATUS
nginx????131405????RUNNING
當(dāng)然,也可以一步到位直接創(chuàng)建并運行容器:
???→?ctr?run?-d?docker.io/library/nginx:alpine?nginx
進入容器:
#?和?docker?的操作類似,但必須要指定?--exec-id,這個?id?可以隨便寫,只要唯一就行
???→?ctr?task?exec?--exec-id?0?-t?nginx?sh
暫停容器:
#?和?docker?pause?類似
???→?ctr?task?pause?nginx
容器狀態(tài)變成了 PAUSED:
???→?ctr?task?ls
TASK?????PID???????STATUS
nginx????149857????PAUSED
恢復(fù)容器:
???→?ctr?task?resume?nginx
ctr 沒有 stop 容器的功能,只能暫?;蛘邭⑺廊萜鳌?/strong>
殺死容器:
???→?ctr?task?kill?nginx
獲取容器的 cgroup 信息:
#?這個命令用來獲取容器的內(nèi)存、CPU 和 PID 的限額與使用量。
???→?ctr?task?metrics?nginx
ID???????TIMESTAMP
nginx????2020-12-15?09:15:13.943447167?+0000?UTC
METRIC???????????????????VALUE
memory.usage_in_bytes????77131776
memory.limit_in_bytes????9223372036854771712
memory.stat.cache????????6717440
cpuacct.usage????????????194187935
cpuacct.usage_percpu?????[0?335160?0?5395642?3547200?58559242?0?0?0?0?0?0?6534104?5427871?3032481?2158941?8513633?4620692?8261063?3885961?3667830?0?4367411?356280?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?1585841?0?7754942?5818102?21430929?0?0?0?0?0?0?1811840?2241260?2673960?6041161?8210604?2991221?10073713?1111020?3139751?0?640080?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0?0]
pids.current?????????????97
pids.limit???????????????0
查看容器中所有進程的?PID:
???→?ctr?task?ps?nginx
PID???????INFO
149857????-
149921????-
149922????-
149923????-
149924????-
149925????-
149926????-
149928????-
149929????-
149930????-
149932????-
149933????-
149934????-
...
注意:這里的 PID 是宿主機看到的 PID,不是容器中看到的 PID。
命名空間
除了 k8s 有命名空間以外,Containerd 也支持命名空間。
???→?ctr?ns?ls
NAME????LABELS
default
如果不指定,ctr?默認(rèn)是?default?空間。
目前 Containerd 的定位還是解決運行時,所以目前他還不能完全替代?dockerd,例如使用?Dockerfile?來構(gòu)建鏡像。其實這不是什么大問題,我再給大家介紹一個大招:Containerd 和 Docker 一起用!
Containerd + Docker
事實上,Docker 和 Containerd 是可以同時使用的,只不過 Docker 默認(rèn)使用的 Containerd 的命名空間不是 default,而是?moby。下面就是見證奇跡的時刻。
首先從其他裝了 Docker 的機器或者 GitHub 上下載 Docker 相關(guān)的二進制文件,然后使用下面的命令啟動 Docker:
???→?dockerd?--containerd?/run/containerd/containerd.sock?--cri-containerd
接著用 Docker 運行一個容器:
???→?docker?run?-d?--name?nginx?nginx:alpine
現(xiàn)在再回過頭來查看 Containerd 的命名空間:
???→?ctr?ns?ls
NAME????LABELS
default
moby
查看該命名空間下是否有容器:
???→?ctr?-n?moby?c?ls
CONTAINER???????????????????????????????????????????????????????????IMAGE????RUNTIME
b7093d7aaf8e1ae161c8c8ffd4499c14ba635d8e174cd03711f4f8c27818e89a????-????????io.containerd.runtime.v1.linux
我艸,還可以醬紫?看來以后用 Containerd 不耽誤我?docker build?了~~
最后提醒一句:Kubernetes 用戶不用驚慌,Kubernetes 默認(rèn)使用的是 Containerd 的?k8s.io?命名空間,所以?ctr -n k8s.io?就能看到 Kubernetes 創(chuàng)建的所有容器啦,也不用擔(dān)心?crictl?不支持 load 鏡像了,因為?ctr -n k8s.io?可以 load 鏡像啊,嘻嘻?
參考資料
[1]bucketbench:?https://github.com/estesp/bucketbench
[2]release 頁面:?https://github.com/containerd/containerd/releases
[3]Sealos:?https://github.com/fanux/sealos
聽過云原生玩家都關(guān)注了他
后臺回復(fù)?k8s?獲取史上最方便快捷的 Kubernetes 高可用部署工具
只需一條命令,連 ssh 都不需要!
【往期推薦】
2020-12-20
2020-12-20
2020-12-19
2020-12-19
2020-12-18
﹀
﹀
﹀
深度內(nèi)容
推薦加入

素質(zhì)二連,走一個
