終於可以像使用 Docker 一樣絲滑地使用 Containerd 了


作者:米開朗基楊


有追求的工程師一般都是有技術潔癖的,雲原生的世界更是如此,Kubernetes 雖然制定了容器運行時接口(CRI)標准,但早期能用的容器運行時只有 Docker,而 Docker 又不適配這個標准,於是給 Docker 開了后門,花了大量的精力去適配它。后來有了更多的容器運行時可以選擇后,Kubernetes 就不得不重新考量要不要繼續適配 Docker 了,因為每次更新 Kubelet 都要考慮與 Docker 的適配問題。

標准這個東西就是這樣,我定好標准,你兼容了就一起玩,不兼容就拜拜,它就像兩個人在一起的底線,你可以重,你可以丑,你也可以不完善,但是你不兼容標准就真的不能一起玩了,於是 Kubernetes 就把 Docker 踢出了群聊。

最終 Kubernetes 選擇了 Containerd,時至今日 Containerd 已經變成一個工業級的容器運行時了,它足夠簡單、健壯,可移植性也很強。

現有 CLI 的不足

雖然 Docker 能干的事情,現在 Containerd 都能干,但 Containerd 還有一個非常明顯的缺陷:CLI 不夠友好。它無法像 Docker 和 Podman 一樣通過一條簡單的命令啟動一個容器,它的兩個 CLI 工具 ctrcrictl 都無法實現這么一件非常簡單的需求,而這個需求是大多數人都需要的,我總不能為了在本地測試容器而專門部署一個 Kubernetes 集群吧?

ctr 的設計對人類不太友好,例如缺少以下這些和 Docker 類似的功能:

  • docker run -p <PORT>
  • docker run --restart=always
  • 通過憑證文件 ~/.docker/config.json 來拉取鏡像
  • docker logs

除此之外還有一個 CLI 工具叫 crictl,和 ctr 一樣不太友好。

為了解決這個痛點,Containerd 官方推出了一個新的 CLI 叫 nerdctl。nerdctl 的使用體驗和 docker 一樣順滑,例如:

🐳  → nerdctl run -d -p 8080:80 --name=nginx --restart=always nginx

nerdctl 只是 docker 的復制品?

nerdctl 的目標並不是單純地復制 docker 的功能,它還實現了很多 docker 不具備的功能,例如延遲拉取鏡像(lazy-pulling)、鏡像加密(imgcrypt)等。

延遲拉取鏡像功能可以參考這篇文章:Containerd 使用 Stargz Snapshotter 延遲拉取鏡像

雖然這些功能預計最終也會在 Docker 中實現,但可能需要幾個月甚至幾年的時間,因為 Docker 目前的設計只使用一小部分 Containerd 子系統。將來 Docker 有可能重構代碼以使用完整的 Containerd,但目前還沒看到什么實質性進展。所以 Containerd 社區決定創建一個新的 CLI 來更友好地使用 Containerd

nerdctl 試用

你可以從 nerdctl 的 release 中下載最新的可執行文件,每一個版本都有兩種可用的發行版:

  • nerdctl-<VERSION>-linux-amd64.tar.gz : 只包含 nerdctl。
  • nerdctl-full-<VERSION>-linux-amd64.tar.gz : 包含了 nerdctl 和相關依賴組件(containerd, runc, CNI, …)。

如果你已經安裝了 Containerd,只需要選擇前一個發行版,否則就選擇完整版。

安裝好 nerdctl 后,就可以使用 nerdctl 來運行容器了:

🐳  → nerdctl run -d -p 80:80 --name=nginx --restart=always nginx:alpine

docker.io/library/nginx:alpine:                                                   resolved       |++++++++++++++++++++++++++++++++++++++|
index-sha256:d33e9e24389d7d8b90fe2bcc2dd1bc09b4d235e916ba9d5d9a71cf52e340edb6:    done           |++++++++++++++++++++++++++++++++++++++|
manifest-sha256:c1f4e1974241c3f9ddb2866b2bf8e7afbceaa42dae82aabda5e946d03f054ed2: done           |++++++++++++++++++++++++++++++++++++++|
config-sha256:bfad9487e175364fd6315426feeee34bf5e6f516d2fe6a4e9b592315e330828e:   done           |++++++++++++++++++++++++++++++++++++++|
layer-sha256:29d3f97df6fd99736a0676f9e57e53dfa412cf60b26d95008df9da8197f1f366:    done           |++++++++++++++++++++++++++++++++++++++|
layer-sha256:9aae54b2144e5b2b00c610f8805128f4f86822e1e52d3714c463744a431f0f4a:    done           |++++++++++++++++++++++++++++++++++++++|
layer-sha256:a5f0adaddd5456b7c5a3753ab541b5fad750f0a6499a15f63571b964eb3e2616:    done           |++++++++++++++++++++++++++++++++++++++|
layer-sha256:5df810e1c460527fe400cdd2cab62228f5fb3da0f2dce86a6a6c354972f19b6e:    done           |++++++++++++++++++++++++++++++++++++++|
layer-sha256:345aee38d3533398e0eb7118e4323a8970f7615136f2170dfb2b0278bbd9099d:    done           |++++++++++++++++++++++++++++++++++++++|
layer-sha256:e6a4c36d7c0e358e5fc02ccdac645b18b85dcfec09d4fb5f8cbdc187ce9467a0:    done           |++++++++++++++++++++++++++++++++++++++|
elapsed: 5.7 s                                                                    total:  9.4 Mi (1.6 MiB/s)
27b55e0b18b10c4c8f34e3ba709614e7b1760a75db061d2ce5183e8b1101ce09

查看創建的容器:

🐳  → nerdctl ps
CONTAINER ID    IMAGE                             COMMAND                   CREATED          STATUS    PORTS                 NAMES
3b5faa266a43    docker.io/library/nginx:alpine    "/docker-entrypoint.…"    3 minutes ago    Up        0.0.0.0:80->80/tcp    nginx

和 Docker 一樣,Containerd 也有一個子命令 network

🐳  → nerdctl network ls
NETWORK ID    NAME               FILE
0             bridge
              k8s-pod-network    /etc/cni/net.d/10-calico.conflist
              host
              none

來看下默認的 bridge 配置:

🐳  → nerdctl network inspect bridge
[
    {
        "CNI": {
            "cniVersion": "0.4.0",
            "name": "bridge",
            "nerdctlID": 0,
            "plugins": [
                {
                    "type": "bridge",
                    "bridge": "nerdctl0",
                    "isGateway": true,
                    "ipMasq": true,
                    "hairpinMode": true,
                    "ipam": {
                        "type": "host-local",
                        "routes": [
                            {
                                "dst": "0.0.0.0/0"
                            }
                        ],
                        "ranges": [
                            [
                                {
                                    "subnet": "10.4.0.0/24",
                                    "gateway": "10.4.0.1"
                                }
                            ]
                        ]
                    }
                },
                {
                    "type": "portmap",
                    "capabilities": {
                        "portMappings": true
                    }
                },
                {
                    "type": "firewall"
                },
                {
                    "type": "tuning"
                }
            ]
        },
        "NerdctlID": 0
    }
]

可以看到 network 子命令背后還是 CNI 在運作,與 docker network 子命令原理不同。

構建鏡像

nerdctl 也可以和 buildkit 結合使用來構建容器鏡像,需要先下載 buildkit 的可執行文件:

🐳  → wget https://github.com/moby/buildkit/releases/download/v0.8.2/buildkit-v0.8.2.darwin-amd64.tar.gz

將其解壓到 $PATH 中:

🐳  → tar -C /usr/local/ -zxvf buildkit-v0.8.2.linux-amd64.tar.gz

編寫 systemd unit 文件:

# /etc/systemd/system/buildkit.service
[Unit]
Description=BuildKit
Documentation=https://github.com/moby/buildkit

[Service]
ExecStart=/usr/local/bin/buildkitd --oci-worker=false --containerd-worker=true

[Install]
WantedBy=multi-user.target

啟用 buildkit.service 並設置開機自動運行:

🐳  → systemctl enable --now buildkit.service

下面以 KubeSphere 項目為例,展示如何使用 nerdctl 來構建鏡像。

首先克隆 KubeSphere 官方倉庫:

🐳  → git clone --depth=1 https://github.com.cnpmjs.org/kubesphere/kubesphere.git

進入倉庫目錄,編譯二進制文件:

🐳  → cd kubesphere
🐳  → make ks-apiserver

將二進制文件拷貝到 Dockerfile 目錄:

🐳  → cp bin/cmd/ks-apiserver build/ks-apiserver

進入 Dockerfile 目錄,修改 Dockerfile:

# Copyright 2020 The KubeSphere Authors. All rights reserved.
# Use of this source code is governed by an Apache license
# that can be found in the LICENSE file.
FROM alpine:3.11

ARG HELM_VERSION=v3.5.2

RUN apk add --no-cache ca-certificates
# install helm
RUN wget https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz && \
    tar xvf helm-${HELM_VERSION}-linux-amd64.tar.gz && \
    rm helm-${HELM_VERSION}-linux-amd64.tar.gz && \
    mv linux-amd64/helm /usr/bin/ && \
    rm -rf linux-amd64
# To speed up building process, we copy binary directly from make
# result instead of building it again, so make sure you run the
# following command first before building docker image
#   make ks-apiserver
#
COPY  ks-apiserver /usr/local/bin/

EXPOSE 9090
CMD ["sh"]

構建鏡像:

🐳  → cd build/ks-apiserver

🐳  → nerdctl build -t ks-apiserver .
[+] Building 22.6s (9/9) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                                0.0s
 => => transferring dockerfile: 812B                                                                                                                                                0.0s
 => [internal] load .dockerignore                                                                                                                                                   0.0s
 => => transferring context: 2B                                                                                                                                                     0.0s
 => [internal] load metadata for docker.io/library/alpine:3.11                                                                                                                      1.0s
 => [1/4] FROM docker.io/library/alpine:3.11@sha256:bf5fa774f08a9ed2cb301e522b769d43d48124315a4ec50eae3228d03b9dc558                                                                7.9s
 => => resolve docker.io/library/alpine:3.11@sha256:bf5fa774f08a9ed2cb301e522b769d43d48124315a4ec50eae3228d03b9dc558                                                                0.0s
 => => sha256:9b794450f7b6db7c944ba1f4161edb68cb535052fe7db8ac06e613516c4a658d 2.10MB / 2.82MB                                                                                     21.4s
 => => extracting sha256:9b794450f7b6db7c944ba1f4161edb68cb535052fe7db8ac06e613516c4a658d                                                                                           0.1s
 => [internal] load build context                                                                                                                                                   1.0s
 => => transferring context: 115.87MB                                                                                                                                               1.0s
 => [2/4] RUN apk add --no-cache ca-certificates                                                                                                                                    2.7s
 => [3/4] RUN wget https://get.helm.sh/helm-v3.5.2-linux-amd64.tar.gz &&     tar xvf helm-v3.5.2-linux-amd64.tar.gz &&     rm helm-v3.5.2-linux-amd64.tar.gz &&     mv linux-amd64  4.7s
 => [4/4] COPY  ks-apiserver /usr/local/bin/                                                                                                                                        0.2s
 => exporting to oci image format                                                                                                                                                   5.9s
 => => exporting layers                                                                                                                                                             4.6s
 => => exporting manifest sha256:d7eb2a90496678d11ac5c363b7743ffe2b8e23e7071b94556a5e3231f50f5a6e                                                                                   0.0s
 => => exporting config sha256:8eb6a5187ce958e76c8d37e18221d88f25b48dd7e6672021d0fce21bb071f284                                                                                     0.0s
 => => sending tarball                                                                                                                                                              1.3s
unpacking docker.io/library/ks-apiserver:latest (sha256:d7eb2a90496678d11ac5c363b7743ffe2b8e23e7071b94556a5e3231f50f5a6e)...done
unpacking overlayfs@sha256:d7eb2a90496678d11ac5c363b7743ffe2b8e23e7071b94556a5e3231f50f5a6e (sha256:d7eb2a90496678d11ac5c363b7743ffe2b8e23e7071b94556a5e3231f50f5a6e)...done

查看構建好的鏡像:

🐳  → nerdctl images
REPOSITORY                                                   TAG       IMAGE ID        CREATED          SIZE
alpine                                                       3.11      bf5fa774f08a    3 seconds ago    2.7 MiB
ks-apiserver                                                 latest    d7eb2a904966    6 minutes ago    57.7 MiB

關於 nerdctl 的更多用法,可以參考官方倉庫的 README

總結

從行業趨勢來看,Docker 已經和 Kubernetes 社區漸行漸遠,以 Containerd 為代表的實現了 CRI 接口的容器運行時將會受到 Kubernetes 的青睞。但純粹使用 Containerd 還是有諸多困擾,比如不方便通過 CLI 來創建管理容器,有了 nerdctl 這個 CLI 工具,就就可以填補 Containerd 易用性的空缺,讓你在單機上也能愉快地使用 Containerd。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM