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用途是否推荐镜像管理
ctrcontainerd 内置调试 CLI❌ 体验差,无 tag 概念
crictlKubernetes CRI 专用❌ 不推荐(k8s 用)
nerdctlDocker 兼容 CLI for containerd强烈推荐

强烈建议:所有镜像管理用 nerdctl。它和 Docker CLI 几乎一致(nerdctl pullnerdctl imagesnerdctl 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 exportcontainerd 专用

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,在每个 endpointca_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 foundnerdctl 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 形成完整的镜像供应链安全

生产环境三件套

  1. 私有仓库 + 镜像签名(Harbor + cosign)
  2. 统一命名空间管理(k8s.io + 自定义 ns)
  3. 定期 GC + 磁盘监控(防止 layer 堆积)

把这套部署好,镜像管理就稳了。


参考资源

遇到具体问题? 评论区贴 ctr/nerdctl 命令报错信息 + journalctl -u containerd -n 50 输出,我帮你分析。

containerd #Kubernetes #容器 #镜像管理 #云原生

发表回复

后才能评论