离线包必须包含二进制、镜像、配置三类资源:kubeadm/kubelet/kubectl(版本严格匹配)、control plane及网络插件镜像(如etcd、calico/node等tar包)、修改好的init-config.yaml和calico.yaml;目录结构须为k8s-offline/{bin/,images/,manifests/},权限与格式需合规,部署前须docker load镜像、kubeadm config images pull验证、再kubeadm init。

离线包必须包含二进制、镜像、配置三类资源,缺一不可;只打包 kubeadm 或只导出镜像,部署必然失败。
离线包里到底要塞哪些文件
不是“把能想到的都扔进去”就行。生产环境验证过的最小必要集合如下:
-
kubeadm、kubelet、kubectl三个二进制(版本必须严格匹配,例如 v1.32.11) - 所有 control plane 镜像:
k8s.gcr.io/kube-apiserver:v1.32.11、k8s.gcr.io/etcd:3.5.15-0、k8s.gcr.io/pause:3.9等(共约 12–15 个,crictl images可查 init 阶段实际拉取清单) - 网络插件镜像 + YAML:
quay.io/calico/cni:v3.27.2、quay.io/calico/node:v3.27.2,以及对应的calico.yaml(注意:YAML 中的镜像地址需提前替换为内网地址或本地 tag) - 基础配置模板:
init-config.yaml(含podSubnet、criSocket、imageRepository等关键字段),不能依赖默认生成
常见错误:漏掉 etcd 镜像或用错版本,导致 kubeadm init 卡在 “waiting for etcd to become available”;或者 calico.yaml 里还写着 k8s.gcr.io,节点根本拉不到镜像。
怎么打包才不会在离线环境解不开
打包不是简单 tar -czf 就完事。路径结构决定脚本能否自动识别资源:
- 推荐目录结构固定为:
k8s-offline/{bin/, images/, manifests/} -
bin/下只放三个可执行文件,且chmod +x;别混入docker或containerd二进制(除非你明确要替换系统运行时) -
images/下所有镜像必须是docker save导出的 tar 包(不是skopeo copy的 OCI layout),否则docker load会报 “invalid format” -
manifests/下放修改好的calico.yaml、coredns.yaml(如需自定义)等,禁止放未测试的 Helm chart 或 jsonnet 模板
容易踩的坑:images/ 目录里混了不同 Kubernetes 版本的镜像 tar 包,部署脚本按名字匹配失败;或者 bin/ 下文件权限为 644,kubeadm 执行时报 “permission denied”。
针对 Kubernetes 仪表板和 Web UI 的浏览器自动化。适用于与 Kubernetes Dashboard、Grafana、ArgoCD UI 或其他 Web 界面交互。需要设置 MCP_BROWSER_ENABLED=true。
部署脚本里最关键的三行命令
离线部署成败,往往就卡在这三步是否写对、顺序是否正确:
- 先加载镜像:
find images/ -name "*.tar" -exec docker load -i {} \;(必须在kubeadm init前执行,且确保docker已启动) - 再改镜像仓库地址:
kubeadm config images pull --image-repository registry.local --kubernetes-version v1.32.11(验证镜像是否真能被 kubeadm 识别) - 最后初始化:
kubeadm init --config init-config.yaml --ignore-preflight-errors=Swap(--ignore-preflight-errors=Swap是国产化环境刚需,但不能忽略Mem或CPU)
注意:registry.local 不是真实域名,而是你在 init-config.yaml 里设的 imageRepository 值,它只影响 kubeadm 内部镜像拉取逻辑——实际镜像早已 load 进本地 Docker daemon,所以这个值只要和配置文件一致即可,无需真实 DNS 解析。
国产化环境特别要注意的点
麒麟 V10、统信 UOS 等系统不是“换个 ISO 就能跑”,几个硬性动作漏一个就会卡住:
-
setenforce 0必须执行,且写入/etc/selinux/config的SELINUX=disabled行,否则 Calico pod 启动时 mount 失败 -
swapoff -a && sed -i '/swap/d' /etc/fstab要彻底,部分国产系统 swap 分区藏在 LVM 逻辑卷里,得用lvremove清理 - 内核参数必须追加:
net.bridge.bridge-nf-call-iptables = 1和fs.inotify.max_user_watches = 524288,否则 kube-proxy 或 calico-node 报 “too many open files” - 如果用 containerd 而非 Docker,
kubeadm init必须指定--cri-socket /run/containerd/containerd.sock,且containerd的config.toml里systemd_cgroup = true必须开启
最常被忽略的是:离线包里的 init-config.yaml 没适配国产系统 cgroup driver,默认用 systemd,但很多国产 OS 的 Docker 仍用 cgroupfs,结果 kubelet 启不来,日志只显示 “cgroup driver mismatch”。










