可以像 Docker 一樣方便的使用 Containerd 嗎?
前面我們介紹了可以使用 ctr 操作管理 containerd 鏡像容器,但是大家都習(xí)慣了使用 docker cli,ctr 使用起來可能還是不太順手,為了能夠讓大家更好的轉(zhuǎn)到 containerd 上面來,社區(qū)提供了一個新的命令行工具:nerdctl(https://github.com/containerd/nerdctl)。nerdctl 是一個與 docker cli 風(fēng)格兼容的 containerd 客戶端工具,而且直接兼容 docker compose 的語法的,這就大大提高了直接將 containerd 作為本地開發(fā)、測試或者單機容器部署使用的效率。
安裝
同樣直接在 GitHub Release 頁面下載對應(yīng)的壓縮包解壓到 PATH 路徑下即可:
- # 如果沒有安裝 containerd,則可以下載 nerdctl-full-<VERSION>-linux-amd64.tar.gz 包進行安裝
- ➜ ~ wget https://github.com/containerd/nerdctl/releases/download/v0.11.0/nerdctl-0.11.0-linux-amd64.tar.gz
- # 如果有限制,也可以替換成下面的 URL 加速下載
- # wget https://download.fastgit.org/containerd/nerdctl/releases/download/v0.11.0/nerdctl-0.11.0-linux-amd64.tar.gz
- ➜ ~ mkdir -p /usr/local/containerd/bin/ && tar -zxvf nerdctl-0.11.0-linux-amd64.tar.gz nerdctl && mv nerdctl /usr/local/containerd/bin/
- ➜ ~ ln -s /usr/local/containerd/bin/nerdctl /usr/local/bin/nerdctl
- ➜ ~ nerdctl version
- Client:
- Version: v0.11.0
- Git commit: c802f934791f83dacf20a041cd1c865f8fac954e
- Server:
- containerd:
- Version: v1.5.5
- Revision: 72cec4be58a9eb6b2910f5d10f1c01ca47d231c0
安裝完成后接下來學(xué)習(xí)下 nerdctl 命令行工具的使用。
命令
Run&Exec
nerdctl run
和 docker run 類似可以使用 nerdctl run 命令運行容器,例如:
- ➜ ~ nerdctl run -d -p 80:80 --name=nginx --restart=always nginx:alpine
- docker.io/library/nginx:alpine: resolved |++++++++++++++++++++++++++++++++++++++|
- index-sha256:bead42240255ae1485653a956ef41c9e458eb077fcb6dc664cbc3aa9701a05ce: done |++++++++++++++++++++++++++++++++++++++| manifest-sha256:ce6ca11a3fa7e0e6b44813901e3289212fc2f327ee8b1366176666e8fb470f24: done |++++++++++++++++++++++++++++++++++++++| config-sha256:7ce0143dee376bfd2937b499a46fb110bda3c629c195b84b1cf6e19be1a9e23b: done |++++++++++++++++++++++++++++++++++++++| elapsed: 5.3 s total: 3.1 Ki (606.0 B/s) 6e489777d2f73dda8a310cdf8da9df38353c1aa2021d3c2270b30eff1806bcf8
可選的參數(shù)使用和 docker run 基本一致,比如 -i、-t、--cpus、--memory 等選項,可以使用 nerdctl run --help 獲取可使用的命令選項:
- ➜ ~ nerdctl run --help
- NAME:
- nerdctl run - Run a command in a new container
- USAGE:
- nerdctl run [command options] [arguments...]
- OPTIONS:
- --help show help (default: false)
- --tty, -t (Currently -t needs to correspond to -i) (default: false)
- --interactive, -i Keep STDIN open even if not attached (default: false)
- --detach, -d Run container in background and print container ID (default: false)
- --restart value Restart policy to apply when a container exits (implemented values: "no"|"always") (default: "no")
- --rm Automatically remove the container when it exits (default: false)
- --pull value Pull image before running ("always"|"missing"|"never") (default: "missing")
- --network value, --net value Connect a container to a network ("bridge"|"host"|"none") (default: "bridge")
- --dns value Set custom DNS servers (default: "8.8.8.8", "1.1.1.1")
- --publish value, -p value Publish a container's port(s) to the host
- --hostname value, -h value Container host name
- --cpus value Number of CPUs (default: 0)
- --memory value, -m value Memory limit
- --pid value PID namespace to use
- --pids-limit value Tune container pids limit (set -1 for unlimited) (default: -1)
- --cgroupns value Cgroup namespace to use, the default depends on the cgroup version ("host"|"private") (default: "host")
- --cpuset-cpus value CPUs in which to allow execution (0-3, 0,1)
- --cpu-shares value CPU shares (relative weight) (default: 0)
- --device value Add a host device to the container
- --user value, -u value Username or UID (format: <name|uid>[:<group|gid>])
- --security-opt value Security options
- --cap-add value Add Linux capabilities
- --cap-drop value Drop Linux capabilities
- --privileged Give extended privileges to this container (default: false)
- --runtime value Runtime to use for this container, e.g. "crun", or "io.containerd.runsc.v1" (default: "io.containerd.runc.v2")
- --sysctl value Sysctl options
- --gpus value GPU devices to add to the container ('all' to pass all GPUs)
- --volume value, -v value Bind mount a volume
- --read-only Mount the container's root filesystem as read only (default: false)
- --rootfs The first argument is not an image but the rootfs to the exploded container (default: false)
- --entrypoint value Overwrite the default ENTRYPOINT of the image
- --workdir value, -w value Working directory inside the container
- --env value, -e value Set environment variables
- --env-file value Set environment variables from file
- --name value Assign a name to the container
- --label value, -l value Set meta data on a container
- --label-file value Read in a line delimited file of labels
- --cidfile value Write the container ID to the file
- --shm-size value Size of /dev/shm
nerdctl exec
同樣也可以使用 exec 命令執(zhí)行容器相關(guān)命令,例如:
- ➜ ~ nerdctl exec -it nginx /bin/sh
- / # date
- Thu Aug 19 06:43:19 UTC 2021
- / #
容器管理
nerdctl ps:列出容器
使用 nerdctl ps 命令可以列出所有容器。
- ➜ ~ nerdctl ps
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- 6e489777d2f7 docker.io/library/nginx:alpine "/docker-entrypoint.…" 10 minutes ago Up 0.0.0.0:80->80/tcp nginx
同樣可以使用 -a 選項顯示所有的容器列表,默認只顯示正在運行的容器,不過需要注意的是 nerdctl ps 命令并沒有實現(xiàn) docker ps 下面的 --filter、--format、--last、--size 等選項。
nerdctl inspect:獲取容器的詳細信息。
- ➜ ~ nerdctl inspect nginx
- [
- {
- "Id": "6e489777d2f73dda8a310cdf8da9df38353c1aa2021d3c2270b30eff1806bcf8",
- "Created": "2021-08-19T06:35:46.403464674Z",
- "Path": "/docker-entrypoint.sh",
- "Args": [
- "nginx",
- "-g",
- "daemon off;"
- ],
- "State": {
- "Status": "running",
- "Running": true,
- "Paused": false,
- "Pid": 2002,
- "ExitCode": 0,
- "FinishedAt": "0001-01-01T00:00:00Z"
- },
- "Image": "docker.io/library/nginx:alpine",
- "ResolvConfPath": "/var/lib/nerdctl/1935db59/containers/default/6e489777d2f73dda8a310cdf8da9df38353c1aa2021d3c2270b30eff1806bcf8/resolv.conf",
- "LogPath": "/var/lib/nerdctl/1935db59/containers/default/6e489777d2f73dda8a310cdf8da9df38353c1aa2021d3c2270b30eff1806bcf8/6e489777d2f73dda8a310cdf8da9df38353c1aa2021d3c2270b30eff1806bcf8-json.log",
- "Name": "nginx",
- "Driver": "overlayfs",
- "Platform": "linux",
- "AppArmorProfile": "nerdctl-default",
- "NetworkSettings": {
- "Ports": {
- "80/tcp": [
- {
- "HostIp": "0.0.0.0",
- "HostPort": "80"
- }
- ]
- },
- "GlobalIPv6Address": "",
- "GlobalIPv6PrefixLen": 0,
- "IPAddress": "10.4.0.3",
- "IPPrefixLen": 24,
- "MacAddress": "f2:b1:8e:a2:fe:18",
- "Networks": {
- "unknown-eth0": {
- "IPAddress": "10.4.0.3",
- "IPPrefixLen": 24,
- "GlobalIPv6Address": "",
- "GlobalIPv6PrefixLen": 0,
- "MacAddress": "f2:b1:8e:a2:fe:18"
- }
- }
- }
- }
- ]
可以看到顯示結(jié)果和 docker inspect 也基本一致的。
nerdctl logs:獲取容器日志
查看容器日志是我們平時經(jīng)常會使用到的一個功能,同樣我們可以使用 nerdctl logs 來獲取日志數(shù)據(jù):
- ➜ ~ nerdctl logs -f nginx
- ......
- 2021/08/19 06:35:46 [notice] 1#1: start worker processes
- 2021/08/19 06:35:46 [notice] 1#1: start worker process 32
- 2021/08/19 06:35:46 [notice] 1#1: start worker process 33
同樣支持 -f、-t、-n、--since、--until 這些選項。
nerdctl stop:停止容器
- ➜ ~ nerdctl stop nginx
- nginx
- ➜ ~ nerdctl ps
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- ➜ ~ nerdctl ps -a
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- 6e489777d2f7 docker.io/library/nginx:alpine "/docker-entrypoint.…" 20 minutes ago Up 0.0.0.0:80->80/tcp nginx
nerdctl rm:刪除容器
- ➜ ~ nerdctl rm nginx
- You cannot remove a running container f4ac170235595f28bf962bad68aa81b20fc83b741751e7f3355bd77d8016462d. Stop the container before attempting removal or force remove
- ➜ ~ nerdctl rm -f ginx
- nginx
- ➜ ~ nerdctl ps
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
要強制刪除同樣可以使用 -f 或 --force 選項來操作。
鏡像管理
nerdctl images:鏡像列表
- ➜ ~ nerdctl images
- REPOSITORY TAG IMAGE ID CREATED SIZE
- alpine latest eb3e4e175ba6 6 days ago 5.9 MiB
- nginx alpine bead42240255 29 minutes ago 16.0 KiB
也需要注意的是沒有實現(xiàn) docker images 的一些選項,比如 --all、--digests、--filter、--format。
nerdctl pull:拉取鏡像
- ➜ ~ nerdctl image rm busybox
- Untagged: docker.io/library/busybox:latest@sha256:0f354ec1728d9ff32edcd7d1b8bbdfc798277ad36120dc3dc683be44524c8b60
- Deleted: sha256:5b8c72934dfc08c7d2bd707e93197550f06c0751023dabb3a045b723c5e7b373
- docker.io/library/busybox:latest: resolved |++++++++++++++++++++++++++++++++++++++|
- index-sha256:0f354ec1728d9ff32edcd7d1b8bbdfc798277ad36120dc3dc683be44524c8b60: done |++++++++++++++++++++++++++++++++++++++|
- manifest-sha256:dca71257cd2e72840a21f0323234bb2e33fea6d949fa0f21c5102146f583486b: done |++++++++++++++++++++++++++++++++++++++| config-sha256:69593048aa3acfee0f75f20b77acb549de2472063053f6730c4091b53f2dfb02: done |++++++++++++++++++++++++++++++++++++++| layer-sha256:b71f96345d44b237decc0c2d6c2f9ad0d17fde83dad7579608f1f0764d9686f2: done |++++++++++++++++++++++++++++++++++++++| elapsed: 5.7 s total: 752.8 (132.0 KiB/s)
nerdctl push:推送鏡像
當(dāng)然在推送鏡像之前也可以使用 nerdctl login 命令登錄到鏡像倉庫,然后再執(zhí)行 push 操作。
可以使用 nerdctl login --username xxx --password xxx 進行登錄,使用 nerdctl logout 可以注銷退出登錄。
nerdctl tag:鏡像標簽
使用 tag 命令可以為一個鏡像創(chuàng)建一個別名鏡像:
- ➜ ~ nerdctl images
- REPOSITORY TAG IMAGE ID CREATED SIZE
- busybox latest 0f354ec1728d 6 minutes ago 1.3 MiB
- nginx alpine bead42240255 41 minutes ago 16.0 KiB
- ➜ ~ nerdctl tag nginx:alpine harbor.k8s.local/course/nginx:alpine
- ➜ ~ nerdctl images
- REPOSITORY TAG IMAGE ID CREATED SIZE
- busybox latest 0f354ec1728d 7 minutes ago 1.3 MiB
- nginx alpine bead42240255 41 minutes ago 16.0 KiB
- harbor.k8s.local/course/nginx alpine bead42240255 2 seconds ago 16.0 KiB
nerdctl save:導(dǎo)出鏡像
使用 save 命令可以導(dǎo)出鏡像為一個 tar 壓縮包。
- ➜ ~ nerdctl save -o busybox.tar.gz busybox:latest
- ➜ ~ ls -lh busybox.tar.gz
- -rw-r--r-- 1 root root 761K Aug 19 15:19 busybox.tar.gz
nerdctl rmi:刪除鏡像
- ➜ ~ nerdctl rmi busybox
- Untagged: docker.io/library/busybox:latest@sha256:0f354ec1728d9ff32edcd7d1b8bbdfc798277ad36120dc3dc683be44524c8b60
- Deleted: sha256:5b8c72934dfc08c7d2bd707e93197550f06c0751023dabb3a045b723c5e7b373
nerdctl load:導(dǎo)入鏡像
使用 load 命令可以將上面導(dǎo)出的鏡像再次導(dǎo)入:
- ➜ ~ nerdctl load -i busybox.tar.gz
- unpacking docker.io/library/busybox:latest (sha256:0f354ec1728d9ff32edcd7d1b8bbdfc798277ad36120dc3dc683be44524c8b60)...done
使用 -i 或 --input 選項指定需要導(dǎo)入的壓縮包。
鏡像構(gòu)建
鏡像構(gòu)建是平時我們非常重要的一個需求,我們知道 ctr 并沒有構(gòu)建鏡像的命令,而現(xiàn)在我們又不使用 Docker 了,那么如何進行鏡像構(gòu)建了,幸運的是 nerdctl 就提供了 nerdctl build 這樣的鏡像構(gòu)建命令。
nerdctl build:從 Dockerfile 構(gòu)建鏡像
比如現(xiàn)在我們定制一個 nginx 鏡像,新建一個如下所示的 Dockerfile 文件:
- FROM nginx
- RUN echo '這是一個基于containerd使用nerdctl構(gòu)建的nginx鏡像' > /usr/share/nginx/html/index.html
然后在文件所在目錄執(zhí)行鏡像構(gòu)建命令:
- ➜ ~ nerdctl build -t nginx:nerdctl -f Dockerfile .
- FATA[0000] `buildctl` needs to be installed and `buildkitd` needs to be running, see https://github.com/moby/buildkit: exec: "buildctl": executable file not found in $PATH
可以看到有一個錯誤提示,需要我們安裝 buildctl 并運行 buildkitd,這是因為 nerdctl build 需要依賴 buildkit 工具。
buildkit 項目也是 Docker 公司開源的一個構(gòu)建工具包,支持 OCI 標準的鏡像構(gòu)建。它主要包含以下部分:
- 服務(wù)端 buildkitd:當(dāng)前支持 runc 和 containerd 作為 worker,默認是 runc,我們這里使用 containerd
- 客戶端 buildctl:負責(zé)解析 Dockerfile,并向服務(wù)端 buildkitd 發(fā)出構(gòu)建請求
buildkit 是典型的 C/S 架構(gòu),客戶端和服務(wù)端是可以不在一臺服務(wù)器上,而 nerdctl 在構(gòu)建鏡像的時候也作為 buildkitd 的客戶端,所以需要我們安裝并運行 buildkitd。
所以接下來我們先來安裝 buildkit:
- ➜ ~ wget https://github.com/moby/buildkit/releases/download/v0.9.0/buildkit-v0.9.0.linux-amd64.tar.gz
- # 如果有限制,也可以替換成下面的 URL 加速下載
- # wget https://download.fastgit.org/moby/buildkit/releases/download/v0.9.0/buildkit-v0.9.0.linux-amd64.tar.gz
- ➜ ~ tar -zxvf buildkit-v0.9.0.linux-amd64.tar.gz -C /usr/local/containerd/
- bin/
- bin/buildctl
- bin/buildkit-qemu-aarch64
- bin/buildkit-qemu-arm
- bin/buildkit-qemu-i386
- bin/buildkit-qemu-mips64
- bin/buildkit-qemu-mips64el
- bin/buildkit-qemu-ppc64le
- bin/buildkit-qemu-riscv64
- bin/buildkit-qemu-s390x
- bin/buildkit-runc
- bin/buildkitd
- ➜ ~ ln -s /usr/local/containerd/bin/buildkitd /usr/local/bin/buildkitd
- ➜ ~ ln -s /usr/local/containerd/bin/buildctl /usr/local/bin/buildctl
這里我們使用 Systemd 來管理 buildkitd,創(chuàng)建如下所示的 systemd unit 文件:
- ➜ ~ cat /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
然后啟動 buildkitd:
- ➜ ~ systemctl daemon-reload
- ➜ ~ systemctl enable buildkit --now
- Created symlink /etc/systemd/system/multi-user.target.wants/buildkit.service → /etc/systemd/system/buildkit.service.
- ➜ ~ systemctl status buildkit
- ● buildkit.service - BuildKit
- Loaded: loaded (/etc/systemd/system/buildkit.service; enabled; vendor preset: enabled)
- Memory: 8.6M
- CGroup: /system.slice/buildkit.service
- └─5779 /usr/local/bin/buildkitd --oci-worker=false --containerd-worker=true
- Aug 19 16:03:10 ydzsio systemd[1]: Started BuildKit.
- Aug 19 16:03:10 ydzsio buildkitd[5779]: time="2021-08-19T16:03:10+08:00" level=warning msg="using host network as the default"
- Aug 19 16:03:10 ydzsio buildkitd[5779]: time="2021-08-19T16:03:10+08:00" level=info msg="found worker \"euznuelxhxb689bc5of7pxmbc\", labels>
- Aug 19 16:03:10 ydzsio buildkitd[5779]: time="2021-08-19T16:03:10+08:00" level=info msg="found 1 workers, default=\"euznuelxhxb689bc5of7pxm>
- Aug 19 16:03:10 ydzsio buildkitd[5779]: time="2021-08-19T16:03:10+08:00" level=warning msg="currently, only the default worker can be used."
- Aug 19 16:03:10 ydzsio buildkitd[5779]: time="2021-08-19T16:03:10+08:00" level=info msg="running server on /run/buildkit/buildkitd.sock"
- ~
現(xiàn)在我們再來重新構(gòu)建鏡像:

nerdctl 構(gòu)建鏡像
構(gòu)建完成后查看鏡像是否構(gòu)建成功:
- ➜ ~ nerdctl images
- WARN[0000] unparsable image name "overlayfs@sha256:d5b9b9e4c930f30340650cb373f62f97c93ee3b92c83f01c6e00b7b87d62c624"
- REPOSITORY TAG IMAGE ID CREATED SIZE
- nginx latest 4d4d96ac750a 4 minutes ago 16.0 KiB
- nginx nerdctl d5b9b9e4c930 About a minute ago 24.0 KiB
- d5b9b9e4c930 About a minute ago 24.0 KiB
我們可以看到已經(jīng)有我們構(gòu)建的 nginx:nerdctl 鏡像了,不過出現(xiàn)了一個 WARN[0000] unparsable image name "xxx" 的 Warning 信息,在鏡像列表里面也可以看到有一個鏡像 tag 為空的鏡像,和我們構(gòu)建的鏡像 ID 一樣,在 nerdctl 的 github issue 上也有提到這個問題:https://github.com/containerd/nerdctl/issues/177,不過到現(xiàn)在為止還沒有 FIX,幸運的是這只是一個⚠️,不會影響我們的使用。
接下來使用上面我們構(gòu)建的鏡像來啟動一個容器進行測試:
- ➜ ~ nerdctl run -d -p 80:80 --name=nginx --restart=always nginx:nerdctl
- f8f639cb667926023231b13584226b2c7b856847e0a25bd5f686b9a6e7e3cacd
- ➜ ~ nerdctl ps
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- f8f639cb6679 docker.io/library/nginx:nerdctl "/docker-entrypoint.…" 1 second ago Up 0.0.0.0:80->80/tcp nginx
- ➜ ~ curl localhost
- This is a nerdctl build's nginx image base on containerd
這樣我們就使用 nerdctl + buildkitd 輕松完成了容器鏡像的構(gòu)建。
當(dāng)然如果你還想在單機環(huán)境下使用 Docker Compose,在 containerd 模式下,我們也可以使用 nerdctl 來兼容該功能。同樣我們可以使用 nerdctl compose、nerdctl compose up、nerdctl compose logs、nerdctl compose build、nerdctl compose down 等命令來管理 Compose 服務(wù)。這樣使用 containerd、nerdctl 結(jié)合 buildkit 等工具就完全可以替代 docker 在鏡像構(gòu)建、鏡像容器方面的管理功能了。