1. Containerd 的前世此生node
好久之前,Docker 強勢崛起,以「鏡像」這個大招席捲全球,對其餘容器技術進行致命的降維打擊,使其毫無招架之力,就連 Google 也不例外。Google 爲了避免被拍死在沙灘上,被迫拉下臉面(固然,跪舔是不可能的),但願 Docker 公司和本身聯合推動一個開源的容器運行時做爲 Docker 的核心依賴,否則就走着瞧。Docker 公司以爲本身的智商被侮辱了,走着瞧就走着瞧,誰怕誰啊!linux
很明顯,Docker 公司的這個決策斷送了本身的大好前程,形成了今天的悲劇。nginx
緊接着,Google 聯合 Red Hat、IBM 等幾位巨佬連哄帶騙忽悠 Docker 公司將 libcontainer
捐給中立的社區(OCI,Open Container Intiative),並更名爲 runc
,不留一點 Docker 公司的痕跡~~git
這還不夠,爲了完全扭轉 Docker 一家獨大的局面,幾位大佬又合夥成立了一個基金會叫 CNCF
(Cloud Native Computing Fundation),這個名字想必你們都很熟了,我就不詳細介紹了。CNCF 的目標很明確,既然在當前的維度上幹不過 Docker,乾脆往上爬,升級到大規模容器編排的維度,以此來擊敗 Docker。github
Docker 公司固然不甘示弱,搬出了 Swarm 和 Kubernetes 進行 PK,最後的結局你們都知道了,Swarm 戰敗。而後 Docker 公司耍了個小聰明,將本身的核心依賴 Containerd
捐給了 CNCF,以此來標榜 Docker 是一個 PaaS 平臺。golang
很明顯,這個小聰明又大大加速了本身的滅亡。docker
巨佬們心想,想當初想和你合做搞箇中立的核心運行時,你死要面子活受罪,就是不一樣意,好傢伙,如今本身搞了一個,還捐出來了,馬老師,發生甚莫事了?express
這好嗎?apache
這很差json
也罷,這倒省事了,我就直接拿 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 這個公司卻失敗了。
時至今日,Containerd 已經變成一個工業級的容器運行時了,連口號都有了:超簡單!超健壯!可移植性超強!
固然,爲了讓 Docker 覺得本身不會搶飯碗,Containerd 聲稱本身的設計目的主要是爲了嵌入到一個更大的系統中(暗指 Kubernetes),而不是直接由開發人員或終端用戶使用。
事實上呢,Containerd 如今基本上啥都能幹了,開發人員或者終端用戶能夠在宿主機中管理完整的容器生命週期,包括容器鏡像的傳輸和存儲、容器的執行和管理、存儲和網絡等。你們能夠考慮學起來了。
學習 Containerd 最好的時機是關注公衆號 雲原生實驗室 後,其次是如今,看完了再關注公衆號也不遲????。
先來看看 Containerd 的架構:
能夠看到 Containerd 仍然採用標準的 C/S 架構,服務端經過 GRPC
協議提供穩定的 API,客戶端經過調用服務端的 API 進行高級的操做。
爲了解耦,Containerd 將不一樣的職責劃分給不一樣的組件,每一個組件就至關於一個子系統(subsystem)。鏈接不一樣子系統的組件被稱爲模塊。
整體上 Containerd 被劃分爲兩個子系統:
Bundle : 在 Containerd 中,Bundle
包含了配置、元數據和根文件系統數據,你能夠理解爲容器的文件系統。而 Bundle 子系統容許用戶從鏡像中提取和打包 Bundles。
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
的。
瞭解了 Containerd 的概念後,就能夠動手安裝體驗一把了。本文的演示環境爲 Ubuntu 18.04
。
爲 seccomp 安裝依賴:
???? → sudo apt-get update ???? → sudo apt-get install libseccomp2
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 只支持經過 CRI
拉取鏡像的 mirror,也就是說,只有經過 crictl
或者 Kubernetes 調用時 mirror 纔會生效,經過 ctr
拉取是不會生效的。
Docker
只支持爲 Docker Hub
配置 mirror,而 Containerd
支持爲任意鏡像倉庫配置 mirror。
配置鏡像加速以前,先來看下 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 的鏡像倉庫。例如,registry.mirrors."docker.io"
表示配置 docker.io 的 mirror。
endpoint : 表示提供 mirror 的鏡像加速服務。例如,這裏推薦使用西北農林科技大學提供的鏡像加速服務做爲 docker.io
的 mirror。
至於 gcr.io
,目前尚未公共的加速服務。我本身掏錢搭了個加速服務,拉取速度大概是 3M/s
左右,有加速需求的同窗能夠經過微信號:cloud-native-yang 加我爲好友再詳細諮詢。
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_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
到 0
之間。若是做爲 Kubernetes 的 Worker 節點,能夠考慮設置爲 -999
。
建議經過 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 以及運行時本身管理本身建立的容器的 cgroups
。若是不設置這個選項,systemd 就會將進程移到本身的 cgroups
中,從而致使 Containerd 沒法正確獲取容器的資源使用狀況。
KillMode : 這個選項用來處理 Containerd 進程被殺死的方式。默認狀況下,systemd 會在進程的 cgroup 中查找並殺死 Containerd 的全部子進程,這確定不是咱們想要的。KillMode
字段能夠設置的值以下。
咱們須要將 KillMode 的值設置爲 process
,這樣能夠確保升級或重啓 Containerd 時不殺死現有的容器。
control-group(默認值):當前控制組裏面的全部子進程,都會被殺掉
process:只殺主進程
mixed:主進程將收到 SIGTERM 信號,子進程收到 SIGKILL 信號
none:沒有進程會被殺掉,只是執行服務的 stop 命令。
如今到了最關鍵的一步:啓動 Containerd。執行一條命令就完事:
???? → systemctl enable containerd --now
接下來進入本文最後一部分:Containerd 的基本使用方式。本文只會介紹 Containerd 的本地使用方法,即本地客戶端 ctr
的使用方法,不會涉及到 crictl
,後面有機會再介紹 crictl
。
若是你想在一分鐘內快速裝好 Kubernetes 和 Containerd,可使用 Sealos[3] 來部署。該項目旨在作一個簡單幹淨輕量級穩定的 kubernetes 安裝工具,一條命令,離線安裝,包含全部依賴,內核負載不依賴 haproxy keepalived,純 golang 開發,99 年證書。1.20.0 版本的離線包搭載了最新版本的 Containerd,還支持 arm64
架構,簡直就是簡直了。
部署方法特別簡單,首先下載並安裝 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。
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 一塊兒用!
事實上,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
[3]Sealos: https://github.com/fanux/sealos