K8s 1.28 从Harbor拉镜像总失败?别慌,containerd的config.toml配置保姆级教程来了
最近在Kubernetes 1.28集群中从Harbor私有仓库拉取镜像时频繁遇到ErrImagePull错误?这很可能是因为你还在用Docker时代的配置思路来处理containerd。自从K8s 1.24版本弃用Docker后,containerd成为了默认的容器运行时,而它的认证配置方式与Docker有着本质区别。本文将带你深入理解containerd的config.toml配置文件,特别是mirrors和configs这两个关键部分,彻底解决Harbor镜像拉取失败的问题。
1. 为什么Docker时代的配置不再适用
很多从Docker迁移到containerd的用户会发现,明明在节点上能用docker login成功登录Harbor,但在K8s中却始终无法拉取镜像。这是因为:
- 认证配置位置不同:Docker将仓库认证信息存储在
~/.docker/config.json中,而containerd需要直接在/etc/containerd/config.toml中配置 - 运行时隔离:K8s通过CRI(容器运行时接口)与containerd交互,不会读取Docker的认证信息
- 命名空间差异:containerd默认使用
k8s.io命名空间管理K8s容器,与Docker的默认命名空间隔离
常见错误现象:
Failed to pull image "harbor.example.com/library/nginx:latest": rpc error: code = Unknown desc = failed to pull and unpack image "harbor.example.com/library/nginx:latest": failed to resolve reference "harbor.example.com/library/nginx:latest": failed to authorize: failed to fetch anonymous token: unexpected status: 401 Unauthorized2. containerd核心配置文件解析
containerd的主配置文件/etc/containerd/config.toml采用TOML格式,与Docker的JSON配置不同。我们需要重点关注以下两个部分:
2.1 镜像仓库镜像(mirrors)
mirrors部分用于定义仓库镜像和访问策略,相当于Docker中的registry-mirrors。对于Harbor仓库,典型配置如下:
[plugins."io.containerd.grpc.v1.cri".registry] [plugins."io.containerd.grpc.v1.cri".registry.mirrors] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."harbor.example.com"] endpoint = ["http://harbor.example.com"]关键参数说明:
harbor.example.com:你的Harbor仓库地址endpoint:指定仓库访问协议和地址,支持http/https
2.2 仓库认证配置(configs)
configs部分用于配置私有仓库的认证信息,这是解决401未授权错误的关键:
[plugins."io.containerd.grpc.v1.cri".registry.configs] [plugins."io.containerd.grpc.v1.cri".registry.configs."harbor.example.com"] [plugins."io.containerd.grpc.v1.cri".registry.configs."harbor.example.com".auth] username = "admin" password = "Harbor12345"安全提示:
生产环境中建议将密码存储在K8s Secret中,而不是直接写在配置文件里
3. HTTP与HTTPS场景下的完整配置示例
根据Harbor使用的协议不同,配置方式也有所差异。
3.1 HTTP仓库配置
对于测试环境的HTTP Harbor仓库,需要额外配置跳过TLS验证:
[plugins."io.containerd.grpc.v1.cri".registry] [plugins."io.containerd.grpc.v1.cri".registry.mirrors] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."harbor.example.com"] endpoint = ["http://harbor.example.com"] [plugins."io.containerd.grpc.v1.cri".registry.configs] [plugins."io.containerd.grpc.v1.cri".registry.configs."harbor.example.com"] [plugins."io.containerd.grpc.v1.cri".registry.configs."harbor.example.com".tls] insecure_skip_verify = true [plugins."io.containerd.grpc.v1.cri".registry.configs."harbor.example.com".auth] username = "admin" password = "Harbor12345"3.2 HTTPS仓库配置
生产环境推荐使用HTTPS,配置更简洁但需要确保证书可信:
[plugins."io.containerd.grpc.v1.cri".registry] [plugins."io.containerd.grpc.v1.cri".registry.mirrors] [plugins."io.containerd.grpc.v1.cri".registry.mirrors."harbor.example.com"] endpoint = ["https://harbor.example.com"] [plugins."io.containerd.grpc.v1.cri".registry.configs] [plugins."io.containerd.grpc.v1.cri".registry.configs."harbor.example.com"] [plugins."io.containerd.grpc.v1.cri".registry.configs."harbor.example.com".auth] username = "admin" password = "Harbor12345"4. 多节点配置与验证
在K8s集群中,所有节点都需要相同的containerd配置。以下是推荐的配置分发流程:
- 在主节点上编辑并验证
config.toml - 使用以下命令分发到所有工作节点:
# 使用scp scp /etc/containerd/config.toml root@k8s-worker-01:/etc/containerd/ scp /etc/containerd/config.toml root@k8s-worker-02:/etc/containerd/ # 或者使用Ansible ansible k8s-workers -m copy -a "src=/etc/containerd/config.toml dest=/etc/containerd/"- 在每个节点上重启containerd服务:
systemctl daemon-reload systemctl restart containerd验证配置是否生效:
ctr -n k8s.io images pull --plain-http harbor.example.com/library/nginx:latest5. 高级排错技巧
当配置完成后仍然遇到问题时,可以尝试以下排错方法:
5.1 检查containerd日志
journalctl -u containerd -f常见错误日志分析:
| 错误信息 | 可能原因 | 解决方案 |
|---|---|---|
| "failed to authorize" | 认证信息错误 | 检查config.toml中的auth配置 |
| "certificate signed by unknown authority" | 证书问题 | 添加CA证书或设置insecure_skip_verify |
| "no such host" | DNS解析失败 | 检查网络和DNS配置 |
5.2 使用nerdctl测试
nerdctl是containerd的CLI工具,语法与docker相似:
nerdctl --insecure-registry login harbor.example.com -u admin -p Harbor12345 nerdctl --insecure-registry pull harbor.example.com/library/nginx:latest5.3 K8s层面的检查
如果Pod仍然无法拉取镜像,检查:
- ImagePullSecrets是否配置正确
- 节点上的containerd是否正常运行
- 网络策略是否阻止了到Harbor的连接
kubectl describe pod <pod-name> | grep -i "pull" kubectl get nodes -o wide6. 从Docker到containerd的思维转变
对于长期使用Docker的用户,需要特别注意以下差异点:
镜像管理:containerd使用命名空间隔离镜像,K8s专用镜像存储在
k8s.io命名空间ctr -n k8s.io images ls认证机制:不再依赖
~/.docker/config.json,所有认证必须在config.toml中显式配置CLI工具:
ctr和nerdctl替代了docker命令,但功能略有不同
常用命令对照表:
| Docker命令 | containerd等效命令 |
|---|---|
| docker pull | ctr images pull |
| docker images | ctr -n k8s.io images ls |
| docker run | nerdctl run |
| docker login | 需配置config.toml |
在实际使用containerd管理K8s集群半年后,我发现其稳定性和性能确实优于Docker,只是初期配置需要一些适应。最常遇到的坑是忘记在多节点集群上同步配置,导致部分节点无法拉取镜像。建议将config.toml纳入配置管理系统,确保集群一致性。