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


有追求的工程師一般都是有技術(shù)潔癖的,云原生的世界更是如此,Kubernetes 雖然制定了容器運(yùn)行時(shí)接口(CRI)標(biāo)準(zhǔn),但早期能用的容器運(yùn)行時(shí)只有 Docker,而 Docker 又不適配這個(gè)標(biāo)準(zhǔn),于是給 Docker 開了后門,花了大量的精力去適配它。后來(lái)有了更多的容器運(yùn)行時(shí)可以選擇后,Kubernetes 就不得不重新考量要不要繼續(xù)適配 Docker 了,因?yàn)槊看胃?Kubelet 都要考慮與 Docker 的適配問(wèn)題。

標(biāo)準(zhǔn)這個(gè)東西就是這樣,我定好標(biāo)準(zhǔn),你兼容了就一起玩,不兼容就拜拜,它就像兩個(gè)人在一起的底線,你可以重,你可以丑,你也可以不完善,但是你不兼容標(biāo)準(zhǔn)就真的不能一起玩了,于是 Kubernetes 就把 Docker 踢出了群聊。

最終 Kubernetes 選擇了 Containerd,時(shí)至今日 Containerd 已經(jīng)變成一個(gè)工業(yè)級(jí)的容器運(yùn)行時(shí)了,它足夠簡(jiǎn)單、健壯,可移植性也很強(qiáng)。
現(xiàn)有 CLI 的不足
雖然 Docker 能干的事情,現(xiàn)在 Containerd 都能干,但 Containerd 還有一個(gè)非常明顯的缺陷:CLI 不夠友好。它無(wú)法像 Docker 和 Podman 一樣通過(guò)一條簡(jiǎn)單的命令啟動(dòng)一個(gè)容器,它的兩個(gè) CLI 工具 ctr[1] 和 crictl[2] 都無(wú)法實(shí)現(xiàn)這么一件非常簡(jiǎn)單的需求,而這個(gè)需求是大多數(shù)人都需要的,我總不能為了在本地測(cè)試容器而專門部署一個(gè) Kubernetes 集群吧?

ctr 的設(shè)計(jì)對(duì)人類不太友好,例如缺少以下這些和 Docker 類似的功能:
docker run -p <PORT>docker run --restart=always通過(guò)憑證文件 ~/.docker/config.json來(lái)拉取鏡像docker logs
除此之外還有一個(gè) CLI 工具叫 crictl,和 ctr 一樣不太友好。
為了解決這個(gè)痛點(diǎn),Containerd 官方推出了一個(gè)新的 CLI 叫 nerdctl[3]。nerdctl 的使用體驗(yàn)和 docker 一樣順滑,例如:
?? → nerdctl run -d -p 8080:80 --name=nginx --restart=always nginx
nerdctl 只是 docker 的復(fù)制品?
nerdctl 的目標(biāo)并不是單純地復(fù)制 docker 的功能,它還實(shí)現(xiàn)了很多 docker 不具備的功能,例如延遲拉取鏡像(lazy-pulling[4])、鏡像加密(imgcrypt[5])等。

延遲拉取鏡像功能可以參考這篇文章:Containerd 使用 Stargz Snapshotter 延遲拉取鏡像[6]。
雖然這些功能預(yù)計(jì)最終也會(huì)在 Docker 中實(shí)現(xiàn),但可能需要幾個(gè)月甚至幾年的時(shí)間[7],因?yàn)?Docker 目前的設(shè)計(jì)只使用一小部分 Containerd 子系統(tǒng)。將來(lái) Docker 有可能重構(gòu)代碼以使用完整的 Containerd,但目前還沒(méi)看到什么實(shí)質(zhì)性進(jìn)展。所以 Containerd 社區(qū)決定創(chuàng)建一個(gè)新的 CLI 來(lái)更友好地使用 Containerd。
nerdctl 試用
你可以從 nerdctl 的 release[8] 中下載最新的可執(zhí)行文件,每一個(gè)版本都有兩種可用的發(fā)行版:
nerdctl-<VERSION>-linux-amd64.tar.gz: 只包含 nerdctl。nerdctl-full-<VERSION>-linux-amd64.tar.gz: 包含了 nerdctl 和相關(guān)依賴組件(containerd, runc, CNI, …)。
如果你已經(jīng)安裝了 Containerd,只需要選擇前一個(gè)發(fā)行版,否則就選擇完整版。
安裝好 nerdctl 后,就可以使用 nerdctl 來(lái)運(yùn)行容器了:
?? → 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
查看創(chuàng)建的容器:
?? → 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 也有一個(gè)子命令 network:
?? → nerdctl network ls
NETWORK ID NAME FILE
0 bridge
k8s-pod-network /etc/cni/net.d/10-calico.conflist
host
none
來(lái)看下默認(rèn)的 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 在運(yùn)作,與 docker network 子命令原理不同。
構(gòu)建鏡像
nerdctl 也可以和 buildkit 結(jié)合使用來(lái)構(gòu)建容器鏡像,需要先下載 buildkit 的可執(zhí)行文件:
?? → 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 并設(shè)置開機(jī)自動(dòng)運(yùn)行:
?? → systemctl enable --now buildkit.service
下面以 KubeSphere[9] 項(xiàng)目為例,展示如何使用 nerdctl 來(lái)構(gòu)建鏡像。
首先克隆 KubeSphere 官方倉(cāng)庫(kù):
?? → git clone --depth=1 https://github.com.cnpmjs.org/kubesphere/kubesphere.git
進(jìn)入倉(cāng)庫(kù)目錄,編譯二進(jìn)制文件:
?? → cd kubesphere
?? → make ks-apiserver
將二進(jìn)制文件拷貝到 Dockerfile 目錄:
?? → cp bin/cmd/ks-apiserver build/ks-apiserver
進(jìn)入 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"]
構(gòu)建鏡像:
?? → 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
查看構(gòu)建好的鏡像:
?? → 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
關(guān)于 nerdctl 的更多用法,可以參考官方倉(cāng)庫(kù)的 README[10]。
總結(jié)
從行業(yè)趨勢(shì)來(lái)看,Docker 已經(jīng)和 Kubernetes 社區(qū)漸行漸遠(yuǎn),以 Containerd 為代表的實(shí)現(xiàn)了 CRI 接口的容器運(yùn)行時(shí)將會(huì)受到 Kubernetes 的青睞。但純粹使用 Containerd 還是有諸多困擾,比如不方便通過(guò) CLI 來(lái)創(chuàng)建管理容器,有了 nerdctl 這個(gè) CLI 工具,就就可以填補(bǔ) Containerd 易用性的空缺,讓你在單機(jī)上也能愉快地使用 Containerd。
腳注
ctr: https://github.com/containerd/containerd/tree/master/cmd/ctr
[2]crictl: https://github.com/kubernetes-sigs/cri-tools
[3]nerdctl: https://github.com/containerd/nerdctl
[4]lazy-pulling: https://github.com/containerd/nerdctl/blob/master/docs/stargz.md
[5]imgcrypt: https://github.com/containerd/imgcrypt
[6]Containerd 使用 Stargz Snapshotter 延遲拉取鏡像: https://fuckcloudnative.io/posts/startup-containers-in-lightning-speed-with-lazy-image-distribution-on-containerd/
[7]可能需要幾個(gè)月甚至幾年的時(shí)間: https://github.com/moby/moby/pull/41002
[8]nerdctl 的 release: https://github.com/containerd/nerdctl/releases
[9]KubeSphere: https://github.com/kubesphere/kubesphere
[10]官方倉(cāng)庫(kù)的 README: https://github.com/containerd/nerdctl


你可能還喜歡
點(diǎn)擊下方圖片即可閱讀

云原生是一種信仰 ??
關(guān)注公眾號(hào)
后臺(tái)回復(fù)?k8s?獲取史上最方便快捷的 Kubernetes 高可用部署工具,只需一條命令,連 ssh 都不需要!


點(diǎn)擊 "閱讀原文" 獲取更好的閱讀體驗(yàn)!
發(fā)現(xiàn)朋友圈變“安靜”了嗎?


