公衆号關注 「奇妙的 Linux 世界」
設為「星标」,每天帶你玩轉 Linux !
1. Containerd 的前世今生
很久以前,Docker 強勢崛起,以“鏡像”這個大招席卷全球,對其他容器技術進行緻命的降維打擊,使其毫無招架之力,就連 Google 也不例外。Google 為了不被拍死在沙灘上,被迫拉下臉面(當然,跪舔是不可能的),希望 Docker 公司和自己聯合推進一個開源的容器運作時作為 Docker 的核心依賴,不然就走着瞧。Docker 公司覺得自己的智商被侮辱了,走着瞧就走着瞧,誰怕誰啊!
很明顯,Docker 公司的這個決策斷送了自己的大好前程,造成了今天的悲劇。
緊接着,Google 聯合 Red Hat、IBM 等幾位巨佬連哄帶騙忽悠 Docker 公司将
libcontainer
捐給中立的社群(OCI,Open Container Intiative),并改名為
runc
,不留一點 Docker 公司的痕迹~~
這還不夠,為了徹底扭轉 Docker 一家獨大的局面,幾位大佬又合夥成立了一個基金會叫
CNCF
(Cloud Native Computing Fundation),這個名字想必大家都很熟了,我就不詳細介紹了。CNCF 的目标很明确,既然在目前的次元上幹不過 Docker,幹脆往上爬,更新到大規模容器編排的次元,以此來擊敗 Docker。
Docker 公司當然不甘示弱,搬出了 Swarm 和 Kubernetes 進行 PK,最後的結局大家都知道了,Swarm 戰敗。然後 Docker 公司耍了個小聰明,将自己的核心依賴
Containerd
捐給了 CNCF,以此來标榜 Docker 是一個 PaaS 平台。
很明顯,這個小聰明又大大加速了自己的滅亡。
巨佬們心想,想當初想和你合作搞個中立的核心運作時,你死要面子活受罪,就是不同意,好家夥,現在自己搞了一個,還捐出來了,馬老師,發生甚莫事了?
這好嗎?
這不好
也罷,這倒省事了,我就直接拿
Containerd
來做文章吧。
首先呢,為了表示 Kubernetes 的中立性,當然要搞個标準化的容器運作時接口,隻要适配了這個接口的容器運作時,都可以和我一起玩耍哦,第一個支援這個接口的當然就是
Containerd
啦。至于這個接口的名字,大家應該都知道了,它叫 CRI(Container Runntime Interface)。
這樣還不行,為了蠱惑 Docker 公司,Kubernetes 暫時先委屈自己,專門在自己的元件中內建了一個
shim
(你可以了解為墊片),用來将 CRI 的調用翻譯成 Docker 的 API,讓 Docker 也能和自己愉快地玩耍,溫水煮青蛙,養肥了再殺。。。
就這樣,Kubernetes 一邊假裝和 Docker 愉快玩耍,一邊背地裡不斷優化 Containerd 的健壯性以及和 CRI 對接的絲滑性。現在 Containerd 的翅膀已經完全硬了,是時候卸下我的僞裝,和 Docker say bye bye 了。後面的事情大家也都知道了~~
Docker 這門技術成功了,Docker 這個公司卻失敗了。
2. Containerd 架構
時至今日,Containerd 已經變成一個工業級的容器運作時了,連口号都有了:超簡單!超健壯!可移植性超強!
當然,為了讓 Docker 以為自己不會搶飯碗,Containerd 聲稱自己的設計目的主要是為了嵌入到一個更大的系統中(暗指 Kubernetes),而不是直接由開發人員或終端使用者使用。
事實上呢,Containerd 現在基本上啥都能幹了,開發人員或者終端使用者可以在主控端中管理完整的容器生命周期,包括容器鏡像的傳輸和存儲、容器的執行和管理、存儲和網絡等。大家可以考慮學起來了。
先來看看 Containerd 的架構:
可以看到 Containerd 仍然采用标準的 C/S 架構,服務端通過
GRPC
協定提供穩定的 API,用戶端通過調用服務端的 API 進行進階的操作。
為了解耦,Containerd 将不同的職責劃分給不同的元件,每個元件就相當于一個子系統(subsystem)。連接配接不同子系統的元件被稱為子產品。
總體上 Containerd 被劃分為兩個子系統:
- Bundle : 在 Containerd 中,
包含了配置、中繼資料和根檔案系統資料,你可以了解為容器的檔案系統。而 Bundle 子系統允許使用者從鏡像中提取和打包 Bundles。Bundle
- Runtime : Runtime 子系統用來執行 Bundles,比如建立容器。
其中,每一個子系統的行為都由一個或多個子產品協作完成(架構圖中的
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 : 提供對鏡像中可尋址内容的通路,所有不可變的内容都被存儲在這裡。
- Snapshot Plugin : 用來管理容器鏡像的檔案系統快照。鏡像中的每一個 layer 都會被解壓成檔案系統快照,類似于 Docker 中的
。graphdriver
- Metrics : 暴露各個元件的監控名額。
從總體來看,Containerd 被分為三個大塊:
Storage
、
Metadata
和
Runtime
,可以将上面的架構圖提煉一下:
這是使用 bucketbench[1] 對
Docker
、
crio
和
Containerd
的性能測試結果,包括啟動、停止和删除容器,以比較它們所耗的時間:
可以看到 Containerd 在各個方面都表現良好,總體性能還是優越于
Docker
和
crio
的。
3. Containerd 安裝
了解了 Containerd 的概念後,就可以動手安裝體驗一把了。本文的示範環境為
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 是需要調用
runc
的,而第一個壓縮包是不包含
runc
二進制檔案的,如果你選擇第一個壓縮包,還需要提前安裝 runc。是以我建議直接使用
cri-containerd
壓縮包。
首先從 release 頁面[2]下載下傳最新版本的壓縮包,目前最新版本為 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
直接将壓縮包解壓到系統的各個目錄中:
???? → sudo tar -C / -xzf cri-containerd-cni-1.4.3-linux-amd64.tar.gz
将
/usr/local/bin
和
/usr/local/sbin
追加到
~/.bashrc
檔案的
$PATH
環境變量中:
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 的預設配置檔案為
/etc/containerd/config.toml
,我們可以通過指令來生成一個預設的配置:
???? → mkdir /etc/containerd
???? → containerd config default > /etc/containerd/config.toml
鏡像加速
由于某些不可描述的因素,在國内拉取公共鏡像倉庫的速度是極慢的,為了節約拉取時間,需要為 Containerd 配置鏡像倉庫的
mirror
。Containerd 的鏡像倉庫 mirror 與 Docker 相比有兩個差別:
- Containerd 隻支援通過
拉取鏡像的 mirror,也就是說,隻有通過CRI
或者 Kubernetes 調用時 mirror 才會生效,通過crictl
拉取是不會生效的。ctr
-
隻支援為Docker
配置 mirror,而Docker Hub
支援為任意鏡像倉庫配置 mirror。Containerd
配置鏡像加速之前,先來看下 Containerd 的配置結構,乍一看可能會覺得很複雜,複雜就複雜在 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,還可以配置預設的 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 的鏡像倉庫。例如,
表示配置 docker.io 的 mirror。registry.mirrors."docker.io"
- endpoint : 表示提供 mirror 的鏡像加速服務。例如,這裡推薦使用西北農林科技大學提供的鏡像加速服務作為
的 mirror。docker.io
存儲配置
Containerd 有兩個不同的存儲路徑,一個用來儲存持久化資料,一個用來儲存運作時狀态。
root = "/var/lib/containerd"
state = "/run/containerd"
root
用來儲存持久化資料,包括
Snapshots
,
Content
,
Metadata
以及各種插件的資料。每一個插件都有自己單獨的目錄,Containerd 本身不存儲任何資料,它的所有功能都來自于已加載的插件,真是太機智了。
???? → 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
用來儲存臨時資料,包括 sockets、pid、挂載點、運作時狀态以及不需要持久化儲存的插件資料。
???? → 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 是容器的守護者,一旦發生記憶體不足的情況,理想的情況應該是先殺死容器,而不是殺死 Containerd。是以需要調整 Containerd 的
OOM
權重,減少其被 OOM Kill 的幾率。最好是将
oom_score
的值調整為比其他守護程序略低的值。這裡的 oom_socre 其實對應的是
/proc/<pid>/oom_socre_adj
,在早期的 Linux 核心版本裡使用
oom_adj
來調整權重, 後來改用
oom_socre_adj
了。該檔案描述如下:
The value of /proc/<pid>/oom_score_adj
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.
在計算最終的
badness score
時,會在計算結果是中加上
oom_score_adj
,這樣使用者就可以通過該在值來保護某個程序不被殺死或者每次都殺某個程序。其取值範圍為
-1000
到
1000
。
如果将該值設定為
-1000
,則程序永遠不會被殺死,因為此時
badness score
永遠傳回0。
建議 Containerd 将該值設定為
-999
到
之間。如果作為 Kubernetes 的 Worker 節點,可以考慮設定為
-999
。
Systemd 配置
建議通過 systemd 配置 Containerd 作為守護程序運作,配置檔案在上文已經被解壓出來了:
???? → 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
這裡有兩個重要的參數:
- Delegate : 這個選項允許 Containerd 以及運作時自己管理自己建立的容器的
。如果不設定這個選項,systemd 就會将程序移到自己的cgroups
中,進而導緻 Containerd 無法正确擷取容器的資源使用情況。cgroups
- KillMode : 這個選項用來處理 Containerd 程序被殺死的方式。預設情況下,systemd 會在程序的 cgroup 中查找并殺死 Containerd 的所有子程序,這肯定不是我們想要的。
KillMode
字段可以設定的值如下。
我們需要将 KillMode 的值設定為
,這樣可以確定更新或重新開機 Containerd 時不殺死現有的容器。process
-
- control-group(預設值):目前控制組裡面的所有子程序,都會被殺掉
- process:隻殺主程序
- mixed:主程序将收到 SIGTERM 信号,子程序收到 SIGKILL 信号
- none:沒有程序會被殺掉,隻是執行服務的 stop 指令。
現在到了最關鍵的一步:啟動 Containerd。執行一條指令就完事:
???? → systemctl enable containerd --now
接下來進入本文最後一部分:Containerd 的基本使用方式。本文隻會介紹 Containerd 的本地使用方法,即本地用戶端
ctr
的使用方法,不會涉及到
crictl
,後面有機會再介紹
crictl
。
4. ctr 使用
ctr 目前很多功能做的還沒有 docker 那麼完善,但基本功能已經具備了。下面将圍繞鏡像和容器這兩個方面來介紹其使用方法。
鏡像
鏡像下載下傳:
???? → 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,它是鏡像的能夠運作的平台辨別。
将鏡像挂載到主機目錄:
???? → 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
将鏡像導出為壓縮包:
???? → ctr i export nginx.tar.gz docker.io/library/nginx:alpine
從壓縮包導入鏡像:
???? → 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
容器
建立容器:
???? → 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
任務
上面
create
的指令建立了容器後,并沒有處于運作狀态,隻是一個靜态的容器。一個 container 對象隻是包含了運作一個容器所需的資源及配置的資料結構,這意味着 namespaces、rootfs 和容器的配置都已經初始化成功了,隻是使用者程序(這裡是
nginx
)還沒有啟動。
然而一個容器真正的運作起來是由 task 對象實作的,
task
代表任務的意思,可以為容器設定網卡,還可以配置工具來對容器進行監控等。
是以還需要通過 task 啟動容器:
???? → ctr task start -d nginx
???? → ctr task ls
TASK PID STATUS
nginx 131405 RUNNING
當然,也可以一步到位直接建立并運作容器:
???? → 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
容器狀态變成了 PAUSED:
???? → ctr task ls
TASK PID STATUS
nginx 149857 PAUSED
恢複容器:
???? → ctr task resume nginx
ctr 沒有 stop 容器的功能,隻能暫停或者殺死容器。
殺死容器:
???? → ctr task kill nginx
擷取容器的 cgroup 資訊:
# 這個指令用來擷取容器的記憶體、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
預設是
default
空間。
目前 Containerd 的定位還是解決運作時,是以目前他還不能完全替代
dockerd
,例如使用
Dockerfile
來建構鏡像。其實這不是什麼大問題,我再給大家介紹一個大招:Containerd 和 Docker 一起用!
Containerd + Docker
事實上,Docker 和 Containerd 是可以同時使用的,隻不過 Docker 預設使用的 Containerd 的命名空間不是 default,而是
moby
。下面就是見證奇迹的時刻。
首先從其他裝了 Docker 的機器或者 GitHub 上下載下傳 Docker 相關的二進制檔案,然後使用下面的指令啟動 Docker:
???? → dockerd --containerd /run/containerd/containerd.sock --cri-containerd
接着用 Docker 運作一個容器:
???? → docker run -d --name nginx nginx:alpine
現在再回過頭來檢視 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 預設使用的是 Containerd 的
k8s.io
命名空間,是以
ctr -n k8s.io
就能看到 Kubernetes 建立的所有容器啦,也不用擔心
crictl
不支援 load 鏡像了,因為
ctr -n k8s.io
可以 load 鏡像啊,嘻嘻????
參考資料
[1]
bucketbench: https://github.com/estesp/bucketbench
[2]
release 頁面: https://github.com/containerd/containerd/releases
本文轉載自:「雲原生實驗室」,原文:https://tinyurl.com/y8phlh7p,版權歸原作者所有。歡迎投稿,投稿郵箱: [email protected]。
你可能還喜歡
點選下方圖檔即可閱讀
Kubernetes 1.20 版本開始将棄用 Docker,是時候擁抱 Containerd 和 Podman 了!
點選上方圖檔,打開小程式,加入「玩轉 Linux」圈子
更多有趣的網際網路新鮮事,關注「奇妙的網際網路」視訊号全了解!