1 为什么用 nerdctl ?
随着 Kubernetes 于 2022 年中旬发布首个彻底废除 Dockershim 兼容层的 1.24 版本,Docker 垄断容器技术的时代正式成为历史。而新建的 Kubernetes 集群大多会使用直接兼容 CRI 的 containerd、CRI-O 等容器运行时而非 Docker。
针对我们现有的 containerd 技术,可以使用 ctr 操作管理 containerd 镜像容器,但是大家都习惯了使用 docker cli,ctr 使用起来可能还是不太顺手。为了能够让大家更好的转到 containerd 上面来,社区提供了一个新的命令行工具:<font style="color:#DF2A3F;">nerdctl</font>
。
它与 Docker CLI 具有相似的命令和功能,因此对于已经熟悉 Docker 的开发者来说,迁移到 nerdctl 是比较容易的。
2 nerdctl VS docker
nerdctl 是一个与 docker cli 风格兼容的 containerd 客户端工具,而且直接兼容 docker compose 的语法的。这就大大提高了直接将 containerd 作为本地开发、测试或者单机容器部署使用的效率。
nerdctl 的使用和 docker 一致,与 docker 具有相同的体验,主要特征如下:
- 与 docker 的 UI/UX 相同
- 支持 docker-compose ( 例如:nerdctl compose up)
- [可选] 支持 rootless 模式,无 slirp 开销(bypass4netns)
- [可选] 支持延迟拉取(Stargz、Nydus、OverlayBD)
- [可选] 支持加密镜像(ocicrypt)
- [可选] 支持 P2P 镜像分发 (IPFS) (*1)
- [可选] 支持容器镜像签名和验证(cosign)
- [可选] 支持 containerd 的命名空间查看,nerdctl 不仅可以管理 Docker 容器,也可以直接管理本地的的 Kubernetes pod。
- [可选] 支持将 Docker Image Manifest 镜像转换为 OCI 镜像、estargz 镜像。
3 架构原理
4 实验环境
- Nerdctl 版本:1.6.2
Runc 版本:1.1.8
Containerd 版本:1.6.22
- Kubernetes 版本:1.27.4
5 nerdctl 安装和使用
nerdctl 的安装分为 Minimal 精简安装 和包含一些插件的 Full 完整安装 。
精简版只包含 nerdctl,完整版包含 nerdctl和 CNI 插件等依赖(当然你也可以在精简安装的基础上再自己添加 CNI 等插件)。
Minimal (nerdctl-1.6.2-linux-amd64.tar.gz): nerdctl only
Full (nerdctl-full-1.6.2-linux-amd64.tar.gz): Includes dependencies such as containerd, runc, and CNI
:::info 💡nerdctl 下载安装
:::
官方下载地址:https://github.com/containerd/nerdctl/releases,在 Asset 中选择下载精简或者完全安装包(本例精简安装)。
$ wget https://github.com/containerd/nerdctl/releases/download/v1.6.2/nerdctl-1.6.2-linux-amd64.tar.gz
$ tar -xzvf nerdctl-*-linux-amd64.tar.gz -C /usr/local/bin/
$ nerdctl version
WARN[0000] unable to determine buildctl version: exec: "buildctl": executable file not found in $PATH
Client:
Version: v1.6.2
OS/Arch: linux/amd64
Git commit: e3dc23be348efded17d2cd244397b4f7018e0794
buildctl:
Version:
Server:
containerd:
Version: 1.6.22
GitCommit: 8165feabfdfe38c65b599c4993d227328c231fca
runc:
Version: 1.1.8
GitCommit: v1.1.8-0-g82f18fe
6 nerdctl 基本使用方法
从个人用户及开发者的角度看,nerdctl 客户端跟 docker、podman 大致相同,例如:
操作 | nerdctl 命令 | docker 命令 | podman 命令 |
---|---|---|---|
查看本地镜像 | nerdctl images | docker images | podman images |
删除本地镜像 | nerdctl rmi <镜像名称> | docker rmi <镜像名称> | podman rmi <镜像名称> |
创建容器 | nerdctl run <镜像名称> | docker run <镜像名称> | podman run <镜像名称> |
查看运行中的容器 | nerdctl ps | docker ps | podman ps |
查看所有容器 | nerdctl ps -a | docker ps -a | podman ps -a |
删除容器 | nerdctl rm <容器名称> | docker rm <容器名称> | podman rm <容器名称> |
7 更多 nerdctl 用法
7.1 注意项
Kubernetes 默认使用 <font style="color:rgb(233, 105, 0);">Kubernetes.io(k8s.io)</font>
,而 <font style="color:rgb(233, 105, 0);">nerdctl</font>
默认使用 <font style="color:rgb(233, 105, 0);">default namspace</font>
。如果需要查看 Kubernetes 相关镜像需要加上”<font style="color:rgb(233, 105, 0);">--namespace=Kubernetes.io(k8s.io)</font>
“来指定。
$ nerdctl images --namespace=Kubernetes.io # (--namespace=k8s.io)
$ nerdctl -n=Kubernetes.io images # (-n=k8s.io)
或者在nerdctl 配置文件中指定 nerdctl 默认使用 Kubernetes.io namespace。
$ mkdir -pv /etc/nerdctl/
$ cat >> /etc/nerdctl/nerdctl.toml << EOF
namespace = "k8s.io"
EOF
7.2 更多命令操作
#nerdctl run :创建容器
nerdctl run -d -p 80:80 --name=nginx --restart=always nginx
#nerdctl exec :进入容器
nerdctl exec -it nginx /bin/sh
#nerdctl ps :列出容器
nerdctl ps -a
#nerdctl inspect :获取容器的详细信息
nerdctl inspect nginx
#nerdctl logs :获取容器日志
nerdctl logs -f nginx
#nerdctl stop :停止容器
nerdctl stop nginx
#nerdctl rm :删除容器
nerdctl rm -f nginx
nerdctl rmi -f <IMAGE ID>
#nerdctl images:镜像列表
nerdctl images
nerdctl -n=Kubernetes.io images
nerdctl -n=Kubernetes.io images | grep -v '<none>'
#nerdctl pull :拉取镜像
nerdctl pull nginx
#使用 nerdctl login --username xxx --password xxx 进行登录,使用 nerdctl logout 可以注销退出登录
nerdctl login
nerdctl logout
#nerdctl tag :镜像标签
nerdctl tag nginx:latest harbor.Kubernetes/image/nginx:latest
#nerdctl push :推送镜像
nerdctl push harbor.Kubernetes/image/nginx:latest
#nerdctl save :导出镜像
nerdctl save -o busybox.tar.gz busybox:latest
#nerdctl load :导入镜像
nerdctl load -i busybox.tar.gz
#nerdctl rmi :删除镜像
nerdctl rmi busybox
7.3 buildkitd 镜像构建
镜像构建是平时非常频繁的一个需求,我们知道 ctr 并没有构建镜像的命令,而现在我们不使用 Docker 了,那么如何进行镜像构建了?
幸运的是 nerdctl 就提供了 nerdctl build 这样的镜像构建命令。
buildkit 项目也是 Docker 公司开源的一个构建工具包,支持 OCI 标准的镜像构建。它主要包含以下部分:
- 服务端 buildkitd:当前支持 runc 和 containerd 作为 worker,默认是 runc,我们这里使用 containerd。
- 客户端 buildctl:负责解析 Dockerfile,并向服务端 buildkitd 发出构建请求。
buildkit 是典型的 C/S 架构,客户端和服务端是可以不在一台服务器上,而 nerdctl 在构建镜像的时候也作为 buildkitd 的客户端,所以需要我们安装并运行 buildkitd。
若要使用 nerdctl build 命令自行构建及上传容器镜像,则需要安装 buildkitd 并启用该服务,详情请参考官方 GitHub 仓库 [18] ,我们会安装 0.12.2 版本:
$ wget https://github.com/moby/buildkit/releases/download/v0.12.2/buildkit-v0.12.2.linux-amd64.tar.gz
$ tar -xzvf buildkit-v0.12.2.linux-amd64.tar.gz -C /usr/local/
$ mkdir -p /usr/local/lib/systemd/system/
$ cat << EOF | sudo tee /usr/local/lib/systemd/system/buildkitd.service
[Unit]
Description=BuildKit
Documentation=https://github.com/moby/buildkit
[Service]
ExecStart=/usr/local/bin/buildkitd
[Install]
WantedBy=multi-user.target
EOF
# 启动buildkitd服务
$ systemctl daemon-reload && systemctl enable --now buildkitd.service
$ systemctl status buildkitd.service
● buildkitd.service - BuildKit
Loaded: loaded (/usr/local/lib/systemd/system/buildkitd.service; enabled; vendor preset: disabled)
Active: active (running) since Mon 2023-11-06 13:35:11 CST; 46s ago
Docs: https://github.com/moby/buildkit
Main PID: 30332 (buildkitd)
Tasks: 9
Memory: 6.9M
CGroup: /system.slice/buildkitd.service
└─30332 /usr/local/bin/buildkitd
比如现在我们定制一个 nginx 镜像,新建一个如下所示的 Dockerfile 文件:
cat > Dockerfile <<EOF
FROM nginx:alpine
RUN echo -e "Hello ZHDYA From Containerd" > /usr/share/nginx/html/index.html
EOF
然后在文件所在目录执行镜像构建命令:
$ nerdctl build -t nginx:nerdctl -f Dockerfile .
查看验证:
$ nerdctl images
REPOSITORY TAG IMAGE ID CREATED PLATFORM SIZE BLOB SIZE
nginx nerdctl a0fd917f4722 47 seconds ago linux/amd64 43.1 MiB 16.2 MiB
使用上面我们构建的镜像来启动一个容器进行测试:
$ nerdctl run -d -p 80:80 --name=nginx --restart=always nginx:nerdctl
4af4648264330c1ca46ae97299fb03ff7f3c98e9f627618e679eccf6b9df131d
$ nerdctl ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4af464826433 docker.io/library/nginx:nerdctl "/docker-entrypoint.…" 18 seconds ago Up 0.0.0.0:80->80/tcp nginx
$ curl localhost
Hello ZHDYA From Containerd
7.4 一键部署 nerdctl 和 buildkitd 服务
#!/bin/bash
# FileName: install-nerdctl-buildkit.sh
# Description:Install nerdctl command and buildkitd service
# 安装nerdctl
echo "Install nerdctl"
wget https://github.com/containerd/nerdctl/releases/download/v1.7.1/nerdctl-1.7.1-linux-amd64.tar.gz
tar -zxvf nerdctl-1.7.1-linux-amd64.tar.gz -C /usr/local/bin/
# 查看nerdctl的版本情况
echo "nerdctl Version"
nerdctl version
# 设置默认的命名空间
mkdir -p /etc/nerdctl/
tee /etc/nerdctl/nerdctl.toml << __EOF__
namespace = "k8s.io"
__EOF__
# 测试是否可用
echo "nerdctl images"
# 安装buildkitd服务
echo "Install buildkitd"
wget https://github.com/moby/buildkit/releases/download/v0.12.2/buildkit-v0.12.2.linux-amd64.tar.gz
tar -xzvf buildkit-v0.12.2.linux-amd64.tar.gz -C /usr/local/
echo "Start buildkitd.service"
# 编辑buildkitd服务配置文件
mkdir -p /usr/local/lib/systemd/system/
cat << __EOF__ | sudo tee /usr/local/lib/systemd/system/buildkitd.service
[Unit]
Description=BuildKit
Documentation=https://github.com/moby/buildkit
[Service]
ExecStart=/usr/local/bin/buildkitd
[Install]
WantedBy=multi-user.target
__EOF__
# 启动buildkitd服务
echo "Start the buildkitd service"
systemctl daemon-reload && systemctl enable --now buildkitd.service
systemctl status buildkitd.service
# 删除无用的压缩包
echo "Delete unnecessary compressed packages"
rm -rf ./nerdctl-1.7.1-linux-amd64.tar.gz
rm -rf ./buildkit-v0.12.2.linux-amd64.tar.gz
7.5 nerdctl 管理 Docker Compose
当然现在仍有很多企业在使用 Docker Compose。在 containerd 模式下,我们也可以使用 nerdctl 来兼容该功能。
同样我们可以使用如下命令来管理 Compose 服务:
# nerdctl compose 管理 DockerCompose 应用
$ nerdctl compose
$ nerdctl compose up
$ nerdctl compose logs
$ nerdctl compose build
$ nerdctl compose down`
这样使用 containerd、nerdctl 结合 buildkit 等工具就完全可以替代 docker 在镜像构建、镜像容器方面的管理功能了。
8 基于 containerd & nerdctl 实战
8.1 实战一、快速部署 Redis 数据库服务
1)创建持久化目录&配置文件
mkdir -p /data/redis/data
tee /data/redis/redis.conf <<'EOF'
# 绑定任意接口、服务端口、后台运行。
bind 0.0.0.0
port 6379
# 容器里必须设置为no
daemonize no
supervised auto
# redis服务pid进程文件名
pidfile "/var/run/redis.pid"
# 关闭保护模式,并配置使用密码访问
protected-mode no
requirepass "qigedeKubernetes"
# 数据文件保存路径,rdb/AOF文件也保存在这里
dir "/data"
# 日志文件记录文件(notice / verbose)
# logfile "/logs/redis.log"
# loglevel verbose
# 最大客户端连接数
maxclients 10000
# 客户端连接空闲多久后断开连接,单位秒,0表示禁用
timeout 60
tcp-keepalive 60
# Redis 数据持久化(rdb/aof)配置
# RDB 文件名
dbfilename "dump.rdb"
# 数据自动保存脚本条件例如300s中有10key发生变化
save 300 10
save 60 10000
# 对RDB文件进行压缩,建议以(磁盘)空间换(CPU)时间。
rdbcompression yes
# 版本5的RDB有一个CRC64算法的校验和放在了文件的最后。这将使文件格式更加可靠。
rdbchecksum yes
# RDB自动触发策略是否启用,默认为yes
rdb-save-incremental-fsync yes
# AOF开启
appendonly yes
# AOF文件名
appendfilename "appendonly.aof"
# 可选值 always, everysec,no,建议设置为everysec
appendfsync everysec
# Redis风险命令重命名
# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
rename-command FLUSHDB b840fc02d524045429941cc15f59e41cb7be6c53
rename-command FLUSHALL b840fc02d524045429941cc15f59e41cb7be6c54
rename-command EVAL b840fc02d524045429941cc15f59e41cb7be6c55
rename-command DEBUG b840fc02d524045429941cc15f59e41cb7be6c56
# rename-command SHUTDOWN SHUTDOWN
EOF
2)执行如下命令进行快速创建容器并运行redis服务:
$ nerdctl run -d -p 6379:6379 \
-v /data/redis/redis.conf:/etc/redis/redis.conf \
-v /data/redis/data:/data \
--name redis-server redis:6.2.6-alpine3.15 redis-server /etc/redis/redis.conf
5e854a58087ae1bba5a661b2941474560cbecc37f54c7f4e7a28afbaed6aebf0
3)查看创建的容器并验证redis服务是否正常:
$ nerdctl ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
539dd82dce37 docker.io/library/redis:6.2.6-alpine3.15 "docker-entrypoint.s…" 5 seconds ago Up 0.0.0.0:6379->6379/tcp redis-server
$ nerdctl logs redis-server
1:C 06 Nov 2023 10:34:22.929 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 06 Nov 2023 10:34:22.929 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 06 Nov 2023 10:34:22.929 # Configuration loaded
1:M 06 Nov 2023 10:34:22.934 # You requested maxclients of 10000 requiring at least 10032 max file descriptors.
1:M 06 Nov 2023 10:34:22.934 # Server can't set maximum open files to 10032 because of OS error: Operation not permitted.
1:M 06 Nov 2023 10:34:22.934 # Current maximum open files is 1024. maxclients has been reduced to 992 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'.
1:M 06 Nov 2023 10:34:22.934 * monotonic clock: POSIX clock_gettime
1:M 06 Nov 2023 10:34:22.935 * Running mode=standalone, port=6379.
1:M 06 Nov 2023 10:34:22.936 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 06 Nov 2023 10:34:22.936 # Server initialized
1:M 06 Nov 2023 10:34:22.937 * Ready to accept connections
# 如下返回,表示 redis 服务状态是正常的
$ nerdctl exec -it redis-server redis-cli -a qigedeKubernetes ping
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
PONG
$ nerdctl exec -it redis-server redis-cli -a qigedeKubernetes info
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
# Server
redis_version:6.2.6
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:63421500bb103677
redis_mode:standalone
os:Linux 3.10.0-1160.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:10.3.1
process_id:1
process_supervised:no
run_id:7baff4cbd3c620249034a11a87dc17e42b227132
4)直接用telnet工具连接创建的redis容器中的redis数据库服务:
$ telnet 192.10.192.16 6379
Trying 192.10.192.16...
Connected to 192.10.192.16.
Escape character is '^]'.
auth qigedeKubernetes
+OK
set name woshiqige
+OK
get name
$9
woshiqige
8.2 实战二、nerdctl 全流程操作演示
创建一个 hello-containerd/ 目录并 cd 进去吧,我们将构建一个名为 hello-containerd 的 APP:
$ mkdir -p hello-containerd/ && cd hello-containerd/
然后写一些 HTML 代码,并存到 index.html 文档中:
$ cat > index.html << EOF
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<title>Hello containerd!</title>
</head>
<body>
<h1>Hello containerd!</h1>
<h2>Hello nerdctl!</h2>
<h3>Hello buildkitd!</h3>
</body>
</html>
EOF
接着创建一个 Dockerfile ,该文件将告知 nerdctl 如何一步步构建我们的 APP 镜像:
cat > Dockerfile << EOF
FROM alpine:3.18
RUN apk add -U python3
WORKDIR /app
COPY index.html .
CMD ["python3", "-m", "http.server", "80"]
EOF
然后构建我们的 hello-containerd APP,版本为 0.1.0 :
$ nerdctl build -t harbor-local.kubernets.cn/demo/hello-containerd:0.1.0 .
现在我们可以透过运行该 APP 验证 APP 镜像能正常使用:
$ nerdctl run --name my-hello-server -d -p 8080:80 harbor-local.kubernets.cn/demo/hello-containerd:0.1.0
$ nerdctl ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2d20d221f741 harbor-local.kubernets.cn/demo/hello-containerd:0.1.0 "python3 -m http.ser…" 8 seconds ago Up 0.0.0.0:8080->80/tcp my-hello-server
测试验证:
$ curl localhost:8080
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<title>Hello containerd!</title>
</head>
<body>
<h1>Hello containerd!</h1>
<h2>Hello nerdctl!</h2>
<h3>Hello buildkitd!</h3>
</body>
</html>
先把刚启动的 my-hello-server 容器停掉并删除:
$ nerdctl stop my-hello-server
my-hello-server
$ nerdctl rm my-hello-server
my-hello-server
上传镜像前,以 Harbor 用户名登录 ;若不指明使用特定镜像仓库,nerdctl 会默认使用 docker.io:
$ nerdctl --insecure-registry login harbor-local.kubernets.cn --username admin --password xxx
然后用 nerdctl push 命令把 APP 镜像上传到 Harbor 私仓:
$ nerdctl push --insecure-registry harbor-local.kubernets.cn/demo/hello-containerd:0.1.0
9 升级nerdctl
升级到 nerdctl 1.7.0,首先下载并解压
# 备份当前可执行文件
$ cp /usr/local/bin/nerdctl /usr/local/bin/nerdctlbak
$ wget https://github.com/containerd/nerdctl/releases/download/v1.7.0/nerdctl-1.7.0-linux-amd64.tar.gz
然后去把正在执行的nerdctl进程都停下来,类似以下操作
# 查看正在运行的容器
$ nerdctl container ls
# 停止 nerdctl 服务以及其他服务
拷贝nerdctl到系统二进制命令路径下
$ tar -xzvf nerdctl-*-linux-amd64.tar.gz -C /usr/local/bin/
重新启动 nerdctl 控制的服务!
10 总结
nerdctl 作为一个轻量级的容器运行时工具,旨在提供简洁、高效的容器管理体验。它是基于 Docker CLI 的替代品,并采用了 Containerd 作为底层运行时引擎。同时,文章也提到了 nerdctl 作为一个开源工具,拥有活跃的社区支持和更新的版本发布。
中部位置我解释了 nerdctl 的工作原理。它使用 Containerd 作为底层运行时引擎,通过与Containerd API交互来管理和运行容器。这种架构使 nerdctl 更加轻巧、快速,并且具有更好的性能表现。
文章也详细介绍了 nerdctl 的各种功能和命令。它与 Docker CLI 具有相似的命令和功能,对于已经熟悉 Docker 的开发者来说,迁移到 nerdctl 是比较容易的。同时,nerdctl 还提供了一些额外的功能和改进,如支持通过文件进行镜像构建、创建和管理容器网络、容器日志等。使得容器管理更加便捷、灵活和可控。