天天看點

再見 Docker,是時候擁抱下一代容器工具 Containerd 了!

公衆号關注 「奇妙的 Linux 世界」

設為「星标」,每天帶你玩轉 Linux !

再見 Docker,是時候擁抱下一代容器工具 Containerd 了!

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 平台。

很明顯,這個小聰明又大大加速了自己的滅亡。

再見 Docker,是時候擁抱下一代容器工具 Containerd 了!

巨佬們心想,想當初想和你合作搞個中立的核心運作時,你死要面子活受罪,就是不同意,好家夥,現在自己搞了一個,還捐出來了,馬老師,發生甚莫事了?

這好嗎?

這不好

也罷,這倒省事了,我就直接拿 

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 的架構:

再見 Docker,是時候擁抱下一代容器工具 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 等。總之,萬物皆插件,插件就是子產品,子產品就是插件。

再見 Docker,是時候擁抱下一代容器工具 Containerd 了!

這裡介紹幾個常用的插件:

  • Content Plugin : 提供對鏡像中可尋址内容的通路,所有不可變的内容都被存儲在這裡。
  • Snapshot Plugin : 用來管理容器鏡像的檔案系統快照。鏡像中的每一個 layer 都會被解壓成檔案系統快照,類似于 Docker 中的 

    graphdriver

  • Metrics : 暴露各個元件的監控名額。

從總體來看,Containerd 被分為三個大塊:

Storage

Metadata

 和 

Runtime

,可以将上面的架構圖提煉一下:

再見 Docker,是時候擁抱下一代容器工具 Containerd 了!

這是使用 bucketbench[1] 對 

Docker

crio

 和 

Containerd

 的性能測試結果,包括啟動、停止和删除容器,以比較它們所耗的時間:

再見 Docker,是時候擁抱下一代容器工具 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 隻支援通過 

    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。

存儲配置

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 以及運作時自己管理自己建立的容器的 

    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. 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]。
再見 Docker,是時候擁抱下一代容器工具 Containerd 了!
再見 Docker,是時候擁抱下一代容器工具 Containerd 了!

你可能還喜歡

點選下方圖檔即可閱讀

再見 Docker,是時候擁抱下一代容器工具 Containerd 了!

Kubernetes 1.20 版本開始将棄用 Docker,是時候擁抱 Containerd 和 Podman 了!

再見 Docker,是時候擁抱下一代容器工具 Containerd 了!

點選上方圖檔,打開小程式,加入「玩轉 Linux」圈子

再見 Docker,是時候擁抱下一代容器工具 Containerd 了!

更多有趣的網際網路新鮮事,關注「奇妙的網際網路」視訊号全了解!

再見 Docker,是時候擁抱下一代容器工具 Containerd 了!

繼續閱讀