kubeadm init镜像拉不下来?两条不用改tag的可靠解决方案
发布时间:2026/10/14 15:44:21 锦皓数字建站

我先把话放在这里kubeadm init 初始化镜像pull不下来绝大多数情况根本不用去手动改tag。很多朋友一遇到这个问题第一个反应就是去网上搜kubeadm 镜像 tag 修改然后把镜像一个个docker tag成 registry.k8s.io 开头的名字再重新init。这样做确实能跑通但后患一堆。这篇内容我主要讲两条不用碰tag的路一条是用--image-repository把镜像仓库前缀换成更稳定可达的地址另一条是给 containerd 配置镜像加速让 k8s 集群初始化时的原始命令一行不动。适合正在被 kube-apiserver、pause、coredns 等镜像拉取问题卡住的朋友以及希望用更干净的方式搭建 Kubernetes 集群的同学。1. 卡在kubeadm init那一刻先看清报错和根因1.1 典型报错长什么样我见过最多的报错长这样[init] Using Kubernetes version: v1.29.0 [preflight] Pulling images required for setting up a Kubernetes cluster [preflight] This might take a minute or two, please dont rush. error execution phase preflight: [preflight] Some fatal errors occurred: [ERROR ImagePull]: failed to pull image registry.k8s.io/kube-apiserver:v1.29.0: output: time... levelfatal msgfailed to pull and unpack image ... failed to copy: reading ... unexpected EOF有些环境下还会出现dial tcp ...: i/o timeout、TLS handshake timeout、connection refused这类更直白的网络报错。很多人一看到ImagePull就以为是自己命令写错了或者集群资源不够其实大多数情况下问题只有一个kubeadm 默认要从指定的镜像仓库拉取那批初始化镜像但当前机器访问这个仓库的链路不稳、不通或者仓库里根本没有对应 tag 的镜像。1.2 根因其实很单一kubeadm 的二进制里内置了一份初始化镜像清单。它不会临时到网上去搜索什么镜像而是严格按照imageRepository 镜像名 tag的规则去拉取。较新版本的默认镜像仓库是registry.k8s.io清单里大约有 7 个镜像kube-apiserver、kube-controller-manager、kube-scheduler、kube-proxy、pause、etcd、coredns。你可以先执行下面这行命令看看当前 kubeadm 版本到底要拉哪些镜像kubeadm config images list输出会类似registry.k8s.io/kube-apiserver:v1.29.0 registry.k8s.io/kube-controller-manager:v1.29.0 registry.k8s.io/kube-scheduler:v1.29.0 registry.k8s.io/kube-proxy:v1.29.0 registry.k8s.io/pause:3.9 registry.k8s.io/etcd:3.5.10-0 registry.k8s.io/coredns/coredns:v1.11.1这些镜像名和 tag 都对应着--kubernetes-version参数。只要仓库地址不可达或者中间网络链路不稳定kubeadm init就会卡在这一步。1.3 动手前先做完这三步自检我不建议一上来就换方案先把问题定位清楚。最基本的自检三步用curl -I https://registry.k8s.io/v2/看仓库连通性。如果这条请求能很快返回HTTP/2 200说明网络链路没问题问题可能出在镜像 tag 上如果超时那基本可以断定是要换仓库地址或加镜像加速。用crictl images看本地是否已经有残留镜像。如果之前中断过一次 init本地可能留了半截镜像下次校验时反而报错需要crictl rmi 镜像ID清理。确认kubeadm version和你要用的--kubernetes-version一致。版本写错时kubeadm 会去拉一个仓库里根本不存在的 tag报错会显示manifest unknown或404 Not Found这种场景同样不需要改 tag。2. 为什么改tag大法能通但我不建议你这么做2.1 旧教程里常见的操作网上能搜到大量教程告诉你怎么绕过这个报错核心操作就是先把镜像从某个能在当前网络下拉到的仓库拉下来然后打一个 tag伪装成 registry.k8s.io 下的镜像docker pull some-mirror.example.com/kube-apiserver:v1.29.0 docker tag some-mirror.example.com/kube-apiserver:v1.29.0 registry.k8s.io/kube-apiserver:v1.29.0按照清单把 7 个镜像都这么处理一遍再重新kubeadm init确实能过。如果你用的是 containerd 运行时而不是 docker操作更麻烦docker 导入的镜像不会自动出现在 CRI 的 k8s.io 命名空间里还得用ctr -n k8s.io images import或crictl load把镜像导入正确位置。2.2 这套做法有三个硬伤第一个坑是镜像太多手工操作容易错。apiserver、controller-manager、scheduler、proxy、pause、etcd、coredns 七个镜像任何一个 tag 打错或者少打一个init 就会卡在下一个镜像上排查起来很痛苦。第二个坑是升级时全部失效。kubeadm upgrade会重新校验本地镜像和期望版本是否一致。你手动打的旧 tag 只对当前版本有效下次升级到 v1.30、v1.31 时又要重新找镜像、重新打 tag、重新导入整个过程没有任何积累价值。第三个坑是掩盖了真正的网络问题。改 tag 只是在某台机器上骗过 kubeadm换一台新节点、换一个网络环境问题马上复现。问题的根子是当前机器到镜像仓库的链路不可靠或者镜像仓库同步不全你不去解决根子只改了本地镜像的标签这个坑会一直跟着你。2.3 什么场景下才需要手动改tag我说清楚不是完全禁止改 tag。如果你拿到的镜像源tag 命名和 kubeadm 期望的不一样比如对方只发布了latest或者离线环境里只有一个带自定义 tag 的 tar 包导入后确实需要重新打 tag。但这种情况属于镜像搬运不是解决初始化镜像 pull 不下来的常规手段。网络问题优先该走的是下面两种方案。3. 不用tag的第一种做法--image-repository换仓库前缀3.1 一个前缀参数省掉一堆tag操作kubeadm 所有镜像的完整地址都可以拆成三部分仓库前缀、镜像名、tag。默认前缀是registry.k8s.io镜像名和 tag 都由 kubeadm 根据版本号自动生成。--image-repository参数做的事情很简单只替换前缀镜像名和 tag 完全不碰。这就像点外卖的时候只换商家地址菜品名字和份量都不变。比如我指定--image-repositorymirror.example.io/kubernetes那么 kubeadm 就会去拉mirror.example.io/kubernetes/kube-apiserver:v1.29.0 mirror.example.io/kubernetes/kube-controller-manager:v1.29.0 ...tag 是 kubeadm 自动拼上去的你不需要自己去处理任何一个 tag。3.2 先预演再拉取别直接冲init很多朋友直接kubeadm init --image-repositoryxxx等 init 跑到最中间才报错来回折腾很浪费时间。我建议先做两步预演kubeadm config images list --image-repositorymirror.example.io/kubernetes这一步会打印出替换前缀后的完整镜像清单。先盯着输出看 10 秒前缀是不是你期望的镜像名是否完整然后再验证仓库里的镜像是否真实存在curl -I https://mirror.example.io/v2/kubernetes/kube-apiserver/manifests/v1.29.0能返回200 OK说明这个镜像在仓库里是存在的。如果返回 404说明前缀路径不对或者该仓库根本没有同步这个组件先换仓库别等到 init 时才发现。确认没问题后再做预拉取kubeadm config images pull --image-repositorymirror.example.io/kubernetes这条命令会按清单逐个拉取全程没有任何报错才继续下一步。3.3 正式初始化与容易忽略的细节初始化命令里加上同一个参数即可kubeadm init \ --image-repositorymirror.example.io/kubernetes \ --kubernetes-versionv1.29.0 \ --pod-network-cidr10.244.0.0/16--pod-network-cidr取决于你选什么网络插件我这里按 Flannel 的习惯写。整个初始化过程里镜像仓库前缀只影响拉镜像这一步后面生成的 kubeconfig、join token 都和它无关。但有一个细节特别容易忽略worker 节点 join 的时候同样需要能拉镜像。尤其是 pause 这类基础镜像如果拉不下来节点加入集群后 kubelet 会一直报错节点状态永远不是 Ready。所以 worker 节点上要么也配置相同的--image-repository要么直接给它的容器运行时配置镜像加速见下一章否则你只是把问题从控制平面搬到了工作节点。3.4 配置文件方式更适合团队命令行参数适合临时验证如果是正式环境、要多人协作我建议用配置文件apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.29.0 imageRepository: mirror.example.io/kubernetes networking: podSubnet: 10.244.0.0/16然后在同目录执行kubeadm init --configkubeadm-config.yaml配置文件的好处是imageRepository 变成了一个显眼的字段评审时一眼能看到以后换版本、换仓库改一个字段就行不会出现某次升级时忘了加命令行参数导致的低级错误。4. 不用tag的第二种做法给containerd配镜像加速命令原封不动4.1 kubeadm、CRI、containerd三者怎么配合kubeadm 其实不直接拉镜像它把拉取某镜像的请求通过 CRIContainer Runtime Interface发给容器运行时默认就是 containerd。kubeadm 给 containerd 的指令是请帮我拉取registry.k8s.io/kube-apiserver:v1.29.0。containerd 收到这个请求后自己会去访问registry.k8s.io拉取镜像。如果这个地址在你当前环境里访问不稳kubeadm 自然就报错。镜像加速的思路是在 containerd 这一层做地址翻译。你可以让 containerd 在拉取registry.k8s.io下的镜像时先去一个可用的镜像加速端点拉取同样名称、同样 tag 的镜像。kubeadm 从头到尾不知道这件事所以它的命令可以保持默认一个参数都不用改。4.2 containerd的mirrors配置实操以 containerd 1.6/1.7 系列为例配置文件在/etc/containerd/config.toml。修改前先备份cp /etc/containerd/config.toml /etc/containerd/config.toml.bak然后在 CRI 插件的 registry 配置段下增加 mirrors 映射[plugins.io.containerd.grpc.v1.cri.registry.mirrors] [plugins.io.containerd.grpc.v1.cri.registry.mirrors.registry.k8s.io] endpoint [https://mirror.example.io/v2/]这里有几个版本相关的注意点containerd 不同小版本的配置结构略有差异如果你不确定自己当前版本的长什么样可以先跑一下containerd config default /tmp/default.toml然后在这个默认配置里面搜索mirrors段落按那个层级去改不要想当然。endpoint 的值要跟你实际使用的镜像加速端点一致有的端点要求带/v2/结尾有的不需要以镜像源提供的说明为准。如果端点只支持 HTTP部分版本还需要额外配置plain_http字段但生产环境强烈建议用 HTTPS别给自己留安全漏洞。配置改完后重启systemctl restart containerd journalctl -u containerd --no-pager | tail -n 30重启后日志里没有ERROR基本就说明配置加载成功了。4.3 验证mirror是否生效先别急着 init用 crictl 手动拉一个镜像验证下crictl pull registry.k8s.io/pause:3.9如果这条命令飞快结束没有任何超时说明 containerd 已经成功走镜像加速端点拉到了原本 registry.k8s.io 路径下的镜像。此时直接执行默认的kubeadm init你会看到它仍然在按registry.k8s.io/...的地址拉镜像但拉取过程非常顺畅。有一个小坑crictl 默认可能没配置运行时端点运行时会报connect: connection refused。此时需要加参数crictl -r unix:///run/containerd/containerd.sock pull registry.k8s.io/pause:3.9或者直接改/etc/crictl.yaml写入runtime-endpoint后面用起来会方便很多。4.4 这个方案的边界要清楚镜像加速并不万能。它只解决镜像名和 tag 都存在但仓库不可达的问题。如果镜像源本身没有同步某个镜像或者同步版本滞后该 404 还是会 404。另外加速端点也可能有故障遇到超时不要只盯着 kubeadm 报错先去journalctl -u containerd --no-pager里看 containerd 到底连了谁、连不连得上。还有一点这个配置是每个节点都要做的。控制平面、工作节点都需要拉镜像尤其是 pause 镜像所有节点都绕不开。所以你要么在每个节点上都配好 mirror要么就把镜像统一放到内部仓库用--image-repository指过去二选一。5. 高频报错自查从日志到仓库连通性的一整套判断方法5.1 报错类型对照表我在帮别人排查的过程中发现很多人分不清网络问题和仓库问题白白改了很多 tag。这里给一个简单的对照表报错关键信息指向原因对应动作i/o timeout/TLS handshake timeout当前机器到镜像仓库网络链路不稳或不通检查连通性换仓库前缀或配置镜像加速404 Not Found/manifest unknown仓库里没有该镜像名或 tag 不存在核对kubeadm config images list输出与实际仓库镜像connection refused仓库端口被防火墙拦掉或仓库服务未运行检查防火墙规则、安全组、仓库状态镜像已存在但接着报校验失败本地残留旧镜像或 tag 混乱用crictl rmi清理干净后再拉5.2 一套可复现的排查命令排查顺序很重要。我一般按这个顺序走kubeadm version kubeadm config images list --image-repository你的仓库前缀 curl -v https://你的仓库地址/v2/ crictl images journalctl -u containerd --no-pager一步步看kubeadm version确认工具版本config images list看它实际要拉哪些完整地址curl手动验证仓库连通性crictl images看本地有没有残留镜像最后如果 init 已经失败过一次翻 containerd 日志能看到真正底层的拉取链路。每次重试之前记得先kubeadm reset -f避免半初始化的状态干扰判断。5.3 一个404案例的完整排查过程举一个我实际遇到过的场景。同事在某一台节点上执行kubeadm config images pull --image-repositorymirror.example.io发现 apiserver 拉不下来其他镜像正常。第一反应是网络问题但我用 curl 验证了一下curl -I https://mirror.example.io/v2/kube-apiserver/manifests/v1.29.0返回 404。再看完整路径发现这个镜像源里镜像的实际路径是mirror.example.io/kubernetes/kube-apiserver而不是mirror.example.io/kube-apiserver。也就是说前缀后面漏了一级路径。把前缀改成mirror.example.io/kubernetes之后再执行 pull 就全部通过了。这个案例说明遇到 404 时不要急着判定仓库不行先用 curl 直接请求 manifest 地址确认前缀路径是否完整、镜像源是否真的有同步这个组件。路径问题在list阶段就能发现完全不用等到 init。5.4 从根上解决把镜像仓库搬进内网如果你管理的不止一台集群我强烈建议走这个路线在可控网络环境里把需要的镜像按 tag 拉取下来推到一个内部镜像仓库之后所有节点统一使用这个内部仓库地址作为imageRepository。节点访问内网仓库速度快、稳定也不依赖外部镜像源的可用性。每次升级 Kubernetes 版本只需要在内部仓库里补充新的 tag 集合然后修改kubeadm-config.yaml里的kubernetesVersion和imageRepository后重新 process 即可。这里的tag是镜像仓库里本来就该存在的版本标识而不是你在节点上手动打出来的别名两者有本质区别。写到这里我的观点已经很明确初始化镜像拉不下来优先从换仓库前缀和运行时镜像加速两个方向解决不要一上来就改 tag。我自己的操作习惯是单机实验用 containerd mirror 方案因为不需要动 init 参数生产环境用自建内部仓库把--image-repository写进 kubeadm 配置因为稳定、可持续。最后分享一条经验所有镜像仓库相关的排查一定要先把kubeadm config images list --image-repository...的输出和实际拉取分开验证很多配置错误在 list 阶段就能发现没必要等到 init 失败后再去翻日志。如果看到这你还在被 pull 不下来的问题卡着先别急着打 tag按上面的思路先验证仓库连通性多半就能解决问题。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。