Containerd 的前世此生和保姆級入門教程

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

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

巨佬們心想,想當初想和你合做搞箇中立的核心運行時,你死要面子活受罪,就是不一樣意,好傢伙,如今本身搞了一個,還捐出來了,馬老師,發生甚莫事了?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 這個公司卻失敗了。

2. Containerd 架構

時至今日,Containerd 已經變成一個工業級的容器運行時了,連口號都有了:超簡單!超健壯!可移植性超強!

固然,爲了讓 Docker 覺得本身不會搶飯碗,Containerd 聲稱本身的設計目的主要是爲了嵌入到一個更大的系統中(暗指 Kubernetes),而不是直接由開發人員或終端用戶使用。

事實上呢,Containerd 如今基本上啥都能幹了,開發人員或者終端用戶能夠在宿主機中管理完整的容器生命週期,包括容器鏡像的傳輸和存儲、容器的執行和管理、存儲和網絡等。你們能夠考慮學起來了。

學習 Containerd 最好的時機是關注公衆號 雲原生實驗室 後,其次是如今,看完了再關注公衆號也不遲????。

先來看看 Containerd 的架構:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

能夠看到 Containerd 仍然採用標準的 C/S 架構,服務端經過 GRPC 協議提供穩定的 API,客戶端經過調用服務端的 API 進行高級的操做。

爲了解耦,Containerd 將不一樣的職責劃分給不一樣的組件,每一個組件就至關於一個子系統(subsystem)。鏈接不一樣子系統的組件被稱爲模塊。

整體上 Containerd 被劃分爲兩個子系統:

  • Bundle : 在 Containerd 中,Bundle 包含了配置、元數據和根文件系統數據,你能夠理解爲容器的文件系統。而 Bundle 子系統容許用戶從鏡像中提取和打包 Bundles。

  • Runtime : Runtime 子系統用來執行 Bundles,好比建立容器。

其中,每個子系統的行爲都由一個或多個模塊協做完成(架構圖中的 Core 部分)。每一種類型的模塊都以插件的形式集成到 Containerd 中,並且插件之間是相互依賴的。例如,上圖中的每個長虛線的方框都表示一種類型的插件,包括 Service PluginMetadata PluginGC PluginRuntime Plugin 等,其中 Service Plugin 又會依賴 Metadata Plugin、GC Plugin 和 Runtime Plugin。每個小方框都表示一個細分的插件,例如 Metadata Plugin 依賴 Containers Plugin、Content Plugin 等。總之,萬物皆插件,插件就是模塊,模塊就是插件。

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

這裏介紹幾個經常使用的插件:

  • Content Plugin : 提供對鏡像中可尋址內容的訪問,全部不可變的內容都被存儲在這裏。

  • Snapshot Plugin : 用來管理容器鏡像的文件系統快照。鏡像中的每個 layer 都會被解壓成文件系統快照,相似於 Docker 中的 graphdriver

  • Metrics : 暴露各個組件的監控指標。

從整體來看,Containerd 被分爲三個大塊:StorageMetadata 和 Runtime,能夠將上面的架構圖提煉一下:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

這是使用 bucketbench[1] 對 Dockercrio 和 Containerd 的性能測試結果,包括啓動、中止和刪除容器,以比較它們所耗的時間:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

能夠看到 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 只支持經過 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 插件下面就分爲 containerdcni 和 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用來保存持久化數據,包括 SnapshotsContentMetadata 以及各類插件的數據。每個插件都有本身單獨的目錄,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 到 0 之間。若是做爲 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 以及運行時本身管理本身建立的容器的 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

4. Containerd 快速安裝

若是你想在一分鐘內快速裝好 Kubernetes 和 Containerd,可使用 Sealos[3] 來部署。該項目旨在作一個簡單幹淨輕量級穩定的 kubernetes 安裝工具,一條命令,離線安裝,包含全部依賴,內核負載不依賴 haproxy keepalived,純 golang 開發,99 年證書。1.20.0 版本的離線包搭載了最新版本的 Containerd,還支持 arm64 架構,簡直就是簡直了。

部署方法特別簡單,首先下載並安裝 sealossealos 是個 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 那麼完善,但基本功能已經具有了。下面將圍繞鏡像容器這兩個方面來介紹其使用方法。

鏡像

鏡像下載:

????  → 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

[3]Sealos: https://github.com/fanux/sealos

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

相關文章
相關標籤/搜索