containerd 镜像管理实战:从基础命令到企业级私有仓库
containerd 镜像管理实战:从基础命令到企业级私有仓库
摘要:containerd 已经成为 Kubernetes、边缘计算、IoT 的默认容器运行时(CNCF 毕业项目,Docker 也已把镜像管理交给它)。本文系统讲解 containerd 镜像管理的完整链路:CLI 工具选型(nerdctl / ctr / crictl)、镜像拉取 / 标签 / 推送、离线导入导出(save/load)、私有仓库配置(含自签名证书)、镜像清理与 GC、命名空间隔离。命令均实测可重复,并给出 8 个实战踩坑点。
适用版本:containerd 2.3.x(兼容 1.7+)、nerdctl 2.3.x
一、为什么是 containerd
containerd 是 CNCF 毕业项目,从 Docker 项目剥离出来的工业级容器运行时。Kubernetes 自 1.24 起默认就用它(替代 dockershim),目前:
- k8s 默认 CRI:kubelet 直接调用 containerd
- 边缘计算首选:轻量(< 100 MB 内存占用)
- 企业稳定性:Apache 2.0 许可,被 AWS/Google/Red Hat/阿里云等采用
但它没有原生的 Docker 风格 CLI——你不能用 docker pull。要管理镜像,得选一个 CLI 工具。
二、CLI 工具选型
| CLI | 用途 | 是否推荐镜像管理 |
|---|---|---|
| ctr | containerd 内置调试 CLI | ❌ 体验差,无 tag 概念 |
| crictl | Kubernetes CRI 专用 | ❌ 不推荐(k8s 用) |
| nerdctl | Docker 兼容 CLI for containerd | ✅ 强烈推荐 |
强烈建议:所有镜像管理用 nerdctl。它和 Docker CLI 几乎一致(nerdctl pull、nerdctl images、nerdctl rmi),但跑在 containerd 上。
安装 nerdctl:
# Debian/Ubuntu
sudo apt install -y nerdctl
# RHEL/CentOS(推荐 GitHub release)
NERDCTL_VERSION="2.3.5"
curl -fsSL "https://github.com/containerd/nerdctl/releases/download/v${NERDCTL_VERSION}/nerdctl-${NERDCTL_VERSION}-linux-amd64.tar.gz" \
| tar -xz -C /usr/local/bin nerdctl
nerdctl --version
三、镜像基础操作
3.1 配置镜像仓库凭据(私有仓库前置)
私有仓库的认证放在 ~/.config/containerd/config.toml 或 daemon 级别 /etc/containerd/config.toml。最简单的做法是用 nerdctl login:
# 登录私有仓库(凭据写入 ~/.docker/config.json,与 Docker 兼容)
nerdctl login registry.example.com -u admin -p 'MyPass123'
# 登录公共仓库(拉取速率限制需要)
nerdctl login docker.io -u myuser
# 登出
nerdctl logout registry.example.com
3.2 拉取镜像
# 默认从 docker.io 拉
nerdctl pull nginx:latest
# 指定仓库
nerdctl pull registry.example.com/team/app:1.2.3
# 拉所有 tag
nerdctl pull --all-tags redis
# 用特定平台(多架构镜像)
nerdctl pull --platform linux/arm64 alpine
3.3 打标签
# 给已有镜像加新 tag
nerdctl tag nginx:latest registry.example.com/team/nginx:v1.0
# 多架构标签(manifest list)
nerdctl tag --platform linux/amd64,linux/arm64 nginx:v1 registry.example.com/team/nginx:v1
3.4 列出与查看
# 列出镜像(类似 docker images)
nerdctl images
# 只看镜像 ID
nerdctl images -q
# 看镜像详细信息
nerdctl inspect nginx:latest
# 看镜像历史(分层信息)
nerdctl history nginx:latest
3.5 推送镜像
# 推送到私有仓库
nerdctl push registry.example.com/team/app:1.2.3
# 推送所有 tag
nerdctl push --all-tags registry.example.com/team/app
# 跳过签名验证(CI 环境)
nerdctl push --skip-verify registry.example.com/team/app
3.6 删除镜像
# 按引用删
nerdctl rmi nginx:latest
# 强制删除(即使有容器在用)
nerdctl rmi -f nginx:latest
# 清理 dangling 镜像
nerdctl image prune -a
四、离线镜像导入导出(air-gapped 环境必备)
场景:内网、工厂、保密项目不允许联网下载镜像,必须在能上网的机器拉取,导出文件后传到内网。
containerd 镜像导出两种格式:
| 格式 | 命令 | 用途 |
|---|---|---|
| OCI 镜像规范(推荐) | nerdctl save -o xxx.tar | 通用、与 docker save 兼容 |
| 原生 moby 格式 | ctr image export | containerd 专用 |
4.1 导出镜像
# 导出单个镜像(单 tag)
nerdctl save -o /tmp/nginx.tar nginx:1.27
# 导出多个 tag(推荐,节省文件数)
nerdctl save -o /tmp/app.tar \
registry.example.com/team/app:1.2.3 \
registry.example.com/team/app:1.2.4 \
nginx:1.27
# 压缩输出(传输更快)
nerdctl save -o /tmp/app.tar.gz --compress=true \
registry.example.com/team/app:1.2.3
4.2 导入镜像
# 导入到 containerd
nerdctl load -i /tmp/app.tar
# 验证导入
nerdctl images | grep app
# 导入到指定命名空间
nerdctl load -i /tmp/app.tar --namespace k8s.io
4.3 用 ctr 跨命名空间导入(高级)
# containerd 默认命名空间是 default
# k8s 用的是 k8s.io 命名空间
# 在普通 containerd 主机上导入后给 k8s 用,必须带 namespace
ctr -n k8s.io images import /tmp/app.tar
ctr -n k8s.io images tag registry.example.com/team/app:1.2.3 \
registry.example.com/team/app:stable
坑点 1:在 k8s 节点上手动导入镜像时,必须用
ctr -n k8s.io才能让 kubelet 看见!用nerdctl load默认导入到default命名空间,k8s 看不到,pod 起不来。
4.4 直接同步(registry-to-registry,最快)
# nerdctl 2.0+ 内置 registry mirror 同步
nerdctl pull docker.io/library/redis:7
nerdctl tag docker.io/library/redis:7 registry.example.com/mirror/redis:7
nerdctl push registry.example.com/mirror/redis:7
# 大批量同步推荐用 skopeo,比 nerdctl 更快
skopeo copy \
docker://docker.io/library/redis:7 \
docker://registry.example.com/mirror/redis:7
五、命名空间(Namespace)管理
containerd 的命名空间不是 k8s 的 namespace——是更底层的隔离机制。每个命名空间有独立的镜像 / 容器 / 快照池。
# 列出所有命名空间
ctr namespaces list
# NAME LABELS
# default
# k8s.io
# moby
# 创建新命名空间
ctr namespaces create dev
# 在指定命名空间操作
ctr -n dev images pull alpine:latest
ctr -n dev containers list
典型应用:
default—— 普通用户k8s.io—— kubelet 用moby—— docker bridge 兼容dev/test/prod—— 团队隔离
六、镜像清理与垃圾回收(GC)
6.1 清理 dangling 镜像(无 tag 的中间层)
nerdctl image prune
nerdctl image prune -a # 包括所有未被使用的镜像
6.2 查看磁盘占用
# 看每个镜像占的空间
nerdctl images --format '{{.Repository}}:{{.Tag}}\t{{.Size}}'
# 看 containerd 整体占用
du -sh /var/lib/containerd/
# 看 metadata 占用
du -sh /var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/
6.3 手动 GC(关键!)
containerd 不会自动 GC 已删除但仍被引用的 blob,需要手动触发:
# 列出所有 blob
ctr -n default content ls | head
# 删除被孤儿引用的 blob(删镜像后)
ctr -n default images prune
# 强制 GC(会清理所有无引用的 layer)
ctr -n default blobs compact
坑点 2:磁盘满了
du -sh /var/lib/containerd/显示很大但nerdctl images没几个镜像——多半是历史拉取留下的孤儿 layer。ctr images prune+ctr blobs compact一次能省几十 GB。
6.4 配置自动 GC(生产必备)
在 /etc/containerd/config.toml 的 [plugins."io.containerd.gc.v1.scheduler"] 段:
[plugins."io.containerd.gc.v1.scheduler"]
pause_threshold = 0.02
deletion_threshold = 0.6
mutation_threshold = 100
schedule_delay = "5m0s"
startup_delay = "0s"
配置后重启:
systemctl restart containerd
七、私有仓库 HTTPS 与自签名证书
7.1 自签名证书配置
坑点 3:私有仓库用自签名证书时,nerdctl 默认会报 x509: certificate signed by unknown authority。
两种解决方法:
方法 A(推荐):把证书加入系统信任
# 假设 registry.example.com.crt 在当前目录
sudo mkdir -p /usr/local/share/ca-certificates/
sudo cp registry.example.com.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
# 验证
curl https://registry.example.com/v2/_catalog # 应该 200
方法 B:在 containerd 配置里信任
编辑 /etc/containerd/config.toml,在每个 endpoint 加 ca_cert:
[plugins."io.containerd.dialerRegistry.v1.remote"]
endpoint = ["https://registry.example.com"]
ca_cert = "/path/to/registry.example.com.crt"
然后重启:
systemctl restart containerd
7.2 镜像仓库 mirror(国内常用)
国内拉 docker.io 很慢,配置镜像加速:
# /etc/containerd/config.toml
[plugins."io.containerd.dialerRegistry.v1.docker"]
endpoint = [
"https://docker.m.daocloud.io",
"https://dockerproxy.com",
"https://docker.mirrors.ustc.edu.cn",
"https://registry-1.docker.io" # 兜底
]
坑点 4:daocloud / dockerproxy 这些 mirror 是第三方,没有 SLA,生产用建议自建 harbor / nexus mirror,更稳更安全。
八、镜像签名与验证(生产可选)
containerd 支持 cosign 签名验证(来自 sigstore 项目):
# 安装 cosign
curl -O -L https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
sudo chmod +x /usr/local/bin/cosign
# 签名镜像
cosign sign --key cosign.key registry.example.com/team/app:1.2.3
# 验证签名
cosign verify --key cosign.pub registry.example.com/team/app:1.2.3
# 在 containerd 拉取时强制验证
# /etc/containerd/config.toml
[plugins."io.containerd.image-verification.v1.cri"]
default_requirements = ["signed"]
九、实战常见错误
9.1 ctr: failed to resolve reference
症状:ctr images pull alpine 报镜像解析失败。 原因:ctr 不支持简写,需要完整路径。 修法:
ctr images pull docker.io/library/alpine:latest
# 或者直接用 nerdctl:nerdctl pull alpine
9.2 image not found 但 nerdctl images 看得到
原因:命名空间错。k8s pod 镜像找不到多半是这个。 修法:
# 看镜像在哪个 namespace
ctr -n k8s.io images ls | grep <image>
# 不在 k8s.io 就重新导
ctr -n k8s.io images import xxx.tar
9.3 open /var/lib/containerd/...: permission denied
原因:containerd 以 root 跑,但 worker 进程 uid 错。 修法:
# 配置文件
sudo chown -R root:root /var/lib/containerd
sudo chmod 755 /var/lib/containerd
systemctl restart containerd
9.4 镜像拉下来但启动后报 exec format error
原因:镜像架构与主机不符(如 ARM64 镜像跑在 x86_64 上)。 修法:
# 拉对应架构
nerdctl pull --platform linux/amd64 image:tag
9.5 no space left on device 但 df 显示有空
原因:containerd 用了独立的 /var/lib/containerd 挂载点满了。 修法:
df -h /var/lib/containerd
ctr -n default blobs compact
nerdctl image prune -a
十、运维工具栈建议
生产环境的 containerd 镜像管理,建议工具组合:
| 场景 | 推荐工具 |
|---|---|
| 本地镜像操作 | nerdctl |
| 镜像跨 registry 同步 | skopeo |
| 镜像签名验证 | cosign |
| 镜像漏洞扫描 | trivy |
| 镜像构建 | buildkit(containerd 内置) |
| 私有仓库 | Harbor / Nexus / Distribution |
| 镜像 GC 监控 | prometheus + cadvisor |
# 一次装齐常用工具
apt install -y nerdctl skopeo cosign trivy
十一、写在最后
containerd 的镜像管理比 Docker 复杂一点(CLI 多、namespace 概念),但更可控、更工业级。一旦配好:
- nerdctl 让日常操作像 docker 一样丝滑
- ctr 给你底层控制能力(命名空间、blob 管理)
- 自建 registry + cosign 形成完整的镜像供应链安全
生产环境三件套:
- 私有仓库 + 镜像签名(Harbor + cosign)
- 统一命名空间管理(k8s.io + 自定义 ns)
- 定期 GC + 磁盘监控(防止 layer 堆积)
把这套部署好,镜像管理就稳了。
参考资源:
- containerd 官方文档:https://github.com/containerd/containerd
- nerdctl 项目:https://github.com/containerd/nerdctl
- OCI 镜像规范:https://github.com/opencontainers/image-spec
- cosign 签名:https://docs.sigstore.dev/
遇到具体问题? 评论区贴 ctr/nerdctl 命令报错信息 + journalctl -u containerd -n 50 输出,我帮你分析。







