简介面向Kylin V10操作系统与ARM架构CPU的Kubernetes部署场景这套资源合集提供基于containerd运行时搭建K8S 1.26.15多主多从集群的完整物料涵盖离线镜像包、依赖RPM包、部署脚本、配置模板和二进制工具适合云原生运维、麒麟系统管理员以及国产化硬件上的K8S落地实践者。包体共47个文件压缩后约622MB主要包含13个gz镜像包、11个rpm依赖包、5个shell脚本以及yml与yaml配置清单、systemd服务文件、kubeadm、kubectl、kubelet二进制和高可用keepalived配置可支撑从环境准备、依赖安装到集群验证的整个流程。已有255人浏览学习。借助离线镜像导入脚本能快速加载apiserver、etcd、coredns、calico等组件镜像并附nginx、busybox等测试镜像多主多从的keepalived配置模板与kubeadm初始化、加入示例有助于在ARM加麒麟环境中规避典型踩坑快速复制出一套高可用集群并校验运行状态对国产化基础设施实践有较高参考价值。1. 多主多从集群在 Kylin V10 与 ARM64 上的真正门槛不是命令复杂是运行时与镜像Kylin V10 配上 ARM 架构 CPU听起来就是装个 Linux 再跑 kubeadm但真把 1.26.15 的多主多从拉起来时你会发现每一步都可能卡住containerd 的沙箱镜像拉不下来、API Server 证书里没有写 VIP、第二台主节点 join 时报 certificate-key 过期。这些都不是 kubeadm 命令本身的问题而是 ARM 环境里运行时、镜像架构和网络模型叠加出来的组合坑。这篇文章把我在模拟项目X里用 containerd 部署三主多从的整套方案整理出来从版本匹配、kubeadm 配置模板到 worker 接入、Calico 镜像适配再到五个高频翻车点和一组体检命令。适合正在 ARM 服务器上做高可用交付、手里只有 Kylin V10 机器、需要直接上手复现的读者。如果你以为多主多从就是多跑几遍 join那看完第 5 章的排查顺序应该能少加一个月的班。2. 控制面运行时选型与系统准备为什么必须用 containerd以及三个容易忽略的关键参数2.1 为什么 K8S 1.26 把 containerd 当成默认运行时CRI 链路与版本匹配Kubernetes 从 1.24 开始移除内置的 dockershim到了 1.26 这个版本里如果还想用 Docker 当运行时就必须额外装 cri-dockerd 把 Docker 的接口翻译回 CRI。等于 kubelet 和容器之间多了一层翻译x86 环境里多一层可能还能忍ARM 环境里排查问题就多一个黑匣子。containerd 本身就实现了 CRI 插件kubelet 通过 unix socket 直接和它通信链路最短。多主多从集群里每个节点上的 kubelet 都要负责拉沙箱、拉业务镜像、给 CNI 提供网络命名空间运行时越简单出问题的环节就越少。所以我在服务器上一律直接装 containerd宿主机上不保留 Docker。版本匹配我是这么选的kubeadm、kubelet、kubectl 三者严格锁在 1.26.15-0用 apt-mark hold 防止误升级containerd 选 1.7 系列这是 1.26 时代 kubeadm 官方验证过的稳定分支pause 沙箱镜像用 3.9。crictl 也建议用 1.26 系列因为排查时 crictl ps 和 crictl logs 的输出格式和 kubelet 最匹配。组件建议版本说明kubeadm / kubelet / kubectl1.26.15-0严格一致并锁版本containerd1.7 系列1.26 的 CRI 主流配套pause 沙箱镜像3.9kubeadm 1.26 默认 sandbox_imagecrictl1.26 系列排查容器状态最顺手Calicov3.26 系列官方 manifest 同时带 amd64/arm64离线交付时以上所有二进制都以 deb 包或镜像包形式先落到内网。不要贪新比如 containerd 2.x 在没有充分验证前任性上手只会把问题变成别人都看不懂的玄学。2.2 在 Kylin V10 ARM64 上安装 containerdsandbox 镜像、crictl 与 systemd_cgroupKylin V10 默认不一定带 /etc/containerd 目录先创建它再生成默认配置。安装完成别急着启动先改三个地方sandbox_image、SystemdCgroup、确认 cri 插件没被禁用。# 解压 containerd 与 CNI 插件二进制路径按你实际介质调整 tar -C /usr/local -xzf containerd-1.7*.tar.gz mkdir -p /opt/cni/bin tar -C /opt/cni/bin -xzf cni-plugins-linux-arm64-*.tgz # 生成默认配置 mkdir -p /etc/containerd containerd config default /etc/containerd/config.toml # 替换沙箱镜像registry.example.com 是你内网镜像仓库地址也可以是公共镜像站 sed -i s#registry.k8s.io/pause:3.9#registry.example.com/k8s/pause:3.9# /etc/containerd/config.toml # 打开 systemd_cgroup和 kubelet 保持一致 sed -i s#SystemdCgroup false#SystemdCgroup true# /etc/containerd/config.toml # 注册系统服务并启动 systemctl enable --now containerd这里最容易被忽略的是 SystemdCgroup。kubelet 默认用 systemd cgroup driver如果 containerd 保持 cgroupfskubelet 起来后健康检查会一直不过表现是 init 卡在等待 kubelet 启动。另一个重点是 config.toml 里不要照抄网上某些教程去改disabled_plugins [io.containerd.grpc.v1.cri]那是跑纯镜像仓库用的关掉 cri 插件后 kubelet 根本连不上运行时。再写一个 crictl 配置否则 crictl 默认去找 Docker 的 socketcat /etc/crictl.yaml EOF runtime-endpoint: unix:///run/containerd/containerd.sock EOF验证安装是否正常不要跳过ctr version ctr -n k8s.io images pull docker.io/library/nginx:1.25-alpine ctr -n k8s.io images list | grep nginx-n k8s.io是 kubelet 实际使用的命名空间这个细节后面排查会反复用到。如果在ctr images list不带命名空间能看到镜像但在ctr -n k8s.io images list看不到说明镜像根本没进 kubelet 的视野。2.3 三个 master 都要做的系统准备内核参数、swap 与端口放行多主多从最容易犯的错是只在 master01 上做了系统准备就开跑。三个控制面节点都要跑同样的内核模块、sysctl 和 swap 关闭任何一台不一致后面都会变成间歇性故障。cat /etc/modules-load.d/containerd.conf EOF overlay br_netfilter EOF modprobe overlay modprobe br_netfilter cat /etc/sysctl.d/k8s.conf EOF net.bridge.bridge-nf-call-iptables 1 net.bridge.bridge-nf-call-ip6tables 1 net.ipv4.ip_forward 1 EOF sysctl --system swapoff -a sed -i /[[:space:]]swap[[:space:]]/d /etc/fstabsysctl 参数的作用是让 iptables 规则能穿透 bridgePod 之间的流量才不会被桥接层绕过ip_forward 是所有 CNI 做 NAT 的前提。swap 必须关kubeadm 的预检会直接报错生产环境如果实在不能关 swap需要在 kubelet 配置里加--fail-swap-onfalse但这不是好习惯能不碰就不碰。角色必须放行的端口master6443、2379、2380、10250、10257、10259worker10250、179、4789、30000-32767所有节点179Calico BGP、4789VXLANKylin V10 默认防火墙可能是 firewalld产线基线如果要求不能关就把上表端口加入白名单否则 etcd 集群在三个 master 之间做选举时2380 被墙会让你看到非常诡异的“时好时坏”。3. kubeadm 初始化第一个主控节点1.26.15 的配置模板、证书 SAN 与 join 命令的生成3.1 先设计控制面VIP 的 controlPlaneEndpoint 与 certSANs 怎么写多主多从和单主最大的区别是 kubeadm 需要一个 controlPlaneEndpoint。裸机环境我一般用 keepalived 在三个 master 上漂一个 VIP云环境就直接接负载均衡器。本文默认 VIP 已经可用地址用 192.0.2.100注意这是文档保留地址真实环境要换成你自己的网段。kubeadm 1.26 使用的配置 schema 是 v1beta3网上很多旧模板还是 v1beta1直接抄过来会在预检阶段报 schema 解析错误。一个能直接用的模板长这样apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.26.15 controlPlaneEndpoint: 192.0.2.100:6443 networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 apiServer: certSANs: - 192.0.2.100 - 192.0.2.11 - 192.0.2.12 - 192.0.2.13 - master01 - master02 - master03 clusterName: k8s-arm imageRepository: registry.example.com/k8s --- apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration nodeRegistration: criSocket: unix:///run/containerd/containerd.sock name: master01certSANs 这一节是最容易漏的。多主多从里 kubectl、kubelet 全都会通过 VIP 访问 apiserver证书里没写 VIPTLS 校验直接失败。这里我把三个 master 的 IP 和主机名都加进去确保后续控制面扩容不会踩证书坑。imageRepository 指向内网仓库前提是这个仓库里已经同步了 K8S 1.26.15 的 arm64 镜像。如果暂时没有内网仓库也可以临时保留官方默认地址但生产交付建议至少把镜像包提前拷到每台节点上。3.2 初始化 master01镜像预热、kubeadm init 与 join 命令的重新生成先把三个 deb 包装好Kylin V10 上用 dpkg 安装即可装完立刻锁版本防止系统更新把 kubelet 拉走dpkg -i kubelet*.deb kubeadm*.deb kubectl*.deb apt-mark hold kubelet kubeadm kubectl初始化之前先拉镜像预热。这一步很多人跳过结果 kubeadm init 一开始就卡在拉镜像上还以为是网络断了# 列出并逐个拉取提前暴露镜像仓库的 arm64 匹配问题 kubeadm config images list --config /etc/kubeadm/kubeadm-config.yaml ctr -n k8s.io images pull --platform linux/arm64 registry.example.com/k8s/kube-apiserver:v1.26.15实际操作可以写个循环把列表里的镜像全拉一遍。确认全部成功后执行初始化kubeadm init --config /etc/kubeadm/kubeadm-config.yaml --upload-certs | tee kubeadm-init.log--upload-certs会把控制面证书的加密密钥上传到集群后续加 master02、master03 时必须用到对应的 certificate-key。初始化完成后把 admin.conf 放到 kubeconfig 默认路径mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/configkubeadm init 输出的 join 信息不建议直接复制因为 token 和 certificate-key 都可能过期。更稳的做法是后面需要时现查现用# 查 worker 的 join 命令 kubeadm token create --print-join-command # 重新生成带 --control-plane 的证书密钥 kubeadm init --phase upload-certs --config /etc/kubeadm/kubeadm-config.yaml 2/dev/null | tail -1要注意 certificate-key 的有效期只有两小时加后续控制面节点最好在一个维护窗口内连续完成。3.3 加入 master02 与 master03--control-plane 的证书密钥第二个和第三个主节点的 join 命令和 worker 完全不同必须带--control-plane否则它会被当成普通 worker 加进集群之后想再提升为控制面节点就非常麻烦。kubeadm join 192.0.2.100:6443 \ --token token \ --discovery-token-ca-cert-hash sha256:hash \ --control-plane \ --certificate-key cert-key \ --cri-socket unix:///run/containerd/containerd.sockjoin 完成后master02 和 master03 上同样需要拷贝 admin.confmkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config三个 master 都加入后etcd 会自动组成三成员集群不需要手动去配 member 列表。你可以先不装 CNI直接在 master01 上看一眼节点状态正常的话三个 master 应该是 NotReady原因是 NetworkPluginNotReady这是预期的等第 4 章 Calico 部署完会恢复。4. 把三主多从接成一张网Calico 的 ARM 镜像适配、worker 接入与调度角色规划4.1 Calico 在 ARM64 上的部署Pod 网段、IP_AUTODETECTION_METHOD 与架构确认Calico 的 v3.26 系列官方 manifest 是同时支持 amd64 和 arm64 的只要镜像仓库没有做过架构拦截直接 apply 通常能跑起来。但有两个地方必须按环境改。第一是 Pod 网段。kubeadm 配置里写了 10.244.0.0/16Calico 默认却是 192.168.0.0/16不一致会导致跨节点 Pod 路由完全不通。下载 calico.yaml 后先改网段# 把 Pod 网段替换成 kubeadm 配置里的一致性网段 sed -i s#192.168.0.0/16#10.244.0.0/16# calico.yaml kubectl apply -f calico.yaml第二是网卡自动检测。ARM 服务器常常不止一张网卡Calico 自带的 IP_AUTODETECTION_METHOD 选错接口后节点之间 BGP 会话建不起来表现是 Pod 能创建但跨节点 ping 不通。我一般直接在 calico-node 容器的 env 段加一行限制它用业务网卡- name: IP_AUTODETECTION_METHOD value: interfaceeth0eth0 要按你实际业务网卡名改改完保存重新 apply再删除所有 calico-node Pod 让 DaemonSet 重建kubectl delete pod -n kube-system -l k8s-appcalico-node部署完确认镜像架构这一步在 ARM 环境里必须养成本能ctr -n k8s.io images list | grep calico ctr -n k8s.io images inspect docker.io/calico/node:v3.26.4 | grep -A5 platform如果 inspect 出来 architecture 不是 arm64说明你的镜像仓库把 amd64 的 manifest 推给了节点。后面第 5 章会专门讲这个坑。4.2 接入 worker 节点并规划角色label、污点与 etcd 成员验证worker 节点的 join 命令比控制面简单关键是不能带--control-plane。很多翻车就是同一份命令复制到 worker 上执行结果 worker 变成半个 master。kubeadm join 192.0.2.100:6443 \ --token token \ --discovery-token-ca-cert-hash sha256:hash \ --cri-socket unix:///run/containerd/containerd.sockworker 加入后给它打上角色标签后续再配合污点做精细化调度kubectl label node worker01 node-role.kubernetes.io/worker kubectl label node worker02 node-role.kubernetes.io/worker默认情况下kubeadm init 会给控制面节点打上node-role.kubernetes.io/control-plane的 NoSchedule 污点这是保护机制别为了省事去掉它。业务负载应该落到 worker 上。想给某个 worker 专门跑大数据任务就给它单独加污点然后在工作负载的 Pod 模板里写对应 tolerationkubectl taint node worker01 bigdatatrue:NoSchedule三主多从的最后一步验证是看 etcd 成员不能只看 kubectl get nodesetcdctl --endpointshttps://192.0.2.11:2379,https://192.0.2.12:2379,https://192.0.2.13:2379 \ --cacert/etc/kubernetes/pki/etcd/ca.crt \ --cert/etc/kubernetes/pki/etcd/server.crt \ --key/etc/kubernetes/pki/etcd/server.key \ member list -w table看到三个成员都是 started 状态控制面才算真正成型。这里证书用的是 etcd 目录下的 server.crt 和 server.key路径别写成 apiserver 的证书。5. 部署过程中最容易翻车的 5 个问题从 containerd 沙箱到 etcd 选举的排查顺序5.1 kubeadm init 卡在等待 kubelet先查沙箱镜像再查 cgroup现象kubeadm init 一直停留在[wait-control-plane] Waiting for the kubelet to boot最后超时退出。原因最常见的是 containerd 没有打开 SystemdCgroupkubelet 健康检查一直失败其次是 sandbox_image 指向的 pause 镜像拉不下来。还有一个隐蔽情况有些人手动执行过ctr images pull没有带-n k8s.io镜像落在默认命名空间kubelet 根本看不到。解决先看 kubelet 日志确认方向journalctl -u kubelet -l --no-pager | tail -50日志里如果反复出现container runtime is down或failed to connect to containerd回到第 2 章检查 config.toml 的 cri 插件是否被禁用、SystemdCgroup 是否打开、containerd 是否重启成功如果日志在报拉取超时就手动拉一次沙箱镜像验证仓库可达性不要把问题留给集群去猜。5.2 控制面节点 join 报证书问题certificate-key 只有两小时寿命现象master02 执行kubeadm join --control-plane时报 certificate-key 不匹配、过期或者证书校验失败退出前提示去 master01 重新提取。原因--upload-certs生成的 certificate-key 有效期是两小时。如果你在 master01 上用kubeadm token create --print-join-command拿到的命令默认只生成 worker join 的信息里面根本没有--control-plane和--certificate-key直接照抄必然失败。解决在 master01 上重新执行kubeadm init --phase upload-certs --config /etc/kubeadm/kubeadm-config.yaml拿新 key并确认 join 命令里同时带--control-plane和--certificate-key两个参数。如果反复失败先把目标节点kubeadm reset清干净再重试避免残留的 etcd 成员数据干扰加入流程。5.3 节点反复 NotReadyNetworkPluginNotReady 的通用排查路径现象集群所有节点都是 NotReadykubectl describe node显示NetworkReadyfalse reason:NetworkPluginNotReady。原因CNI 没装成功。Calico 部署失败时节点会一直卡在这个状态原因可能是 Pod 网段不一致、镜像架构不对、或者网卡自动检测选错接口。注意 containerd 下 kubelet 拉取镜像的命名空间是 k8s.io用ctr -n k8s.io images list查不到任何 calico 镜像说明拉取一直没有成功不是集群在装傻。解决按顺序看 Calico Pod 状态和日志kubectl get pods -n kube-system -o wide | grep calico kubectl logs -n kube-system calico-node-xxxxx -c calico-node | tail -50日志里出现Felix is not ready多半是网卡检测问题用第 4 章的 IP_AUTODETECTION_METHOD 限制接口后重建 calico-node。5.4 镜像拉起但容器起不来Exec format error 与 ARM 架构匹配现象Pod 能创建但容器启动即失败Events 里出现standard_init_linux.go:xxx exec format error或者应用起来后执行 uname -m 显示 x86_64。原因节点是 ARM64但拉下来的镜像是 amd64。公共镜像仓库有些 tag 只有 x86 manifest内网代理仓库更麻烦第一次被 amd64 节点请求后代理会把 amd64 manifest 缓存住后面 ARM 节点来拉拿到的还是 amd64。解决先确认镜像服务器对该镜像到底有没有 arm64 manifestctr -n k8s.io images pull --platform linux/arm64 docker.io/library/nginx:1.25-alpine如果这条命令报 manifest 不匹配说明该镜像源本来就没有 arm64 版本换支持多架构的镜像或者在部署清单里明确写带 arm64 后缀的 tag。这个检查要覆盖 pause、calico、metrics-server 等所有附加组件不能只检查 kube-apiserver。5.5 三个 master 只有两个健康etcd 选举异常与端口放行现象三主都显示 Ready但访问 apiserver 时好时坏kubectl get nodes偶尔超时某个 master 上的 apiserver 一直重启。原因三台 master 之间的 2379 和 2380 端口被防火墙拦截etcd 选举无法正常完成。另一种是 /etc/hosts 里节点名解析不一致etcd 成员对不上地址。解决先做两个统一动作三台 master 的 /etc/hosts 写完全一致的主机名映射防火墙放行 2379、2380。然后用 etcdctl 查哪个端点不健康etcdctl --endpointshttps://192.0.2.11:2379,https://192.0.2.12:2379,https://192.0.2.13:2379 \ --cacert/etc/kubernetes/pki/etcd/ca.crt \ --cert/etc/kubernetes/pki/etcd/server.crt \ --key/etc/kubernetes/pki/etcd/server.key \ endpoint health -w tableunhealthy 的端点对应节点先看它的 kubelet 和 etcd 服务状态不要直接重启这一台因为 etcd 三节点宕掉两台的仲裁风险是集群事故级别的问题。6. 集群交工前的体检清单与证书维护用一组命令证明高可用是真的6.1 交工前跑一遍这条命令链节点、组件、etcd 三张表我每次交付前一定会把下面这组命令完整跑一遍输出截图放进交付文档后续维保也按这套节奏走。# 节点状态 kubectl get nodes -o wide # 控制面与插件的 Pod 分布确认没有全部挤在一个 master 上 kubectl get pods -A -o wide | grep -E kube-system|calico # apiserver 健康检查直接打 VIP验证证书和负载均衡链路 kubectl get --raw/healthz?verbose --serverhttps://192.0.2.100:6443 # etcd 三端点的健康状态 etcdctl --endpointshttps://192.0.2.11:2379,https://192.0.2.12:2379,https://192.0.2.13:2379 \ --cacert/etc/kubernetes/pki/etcd/ca.crt \ --cert/etc/kubernetes/pki/etcd/server.crt \ --key/etc/kubernetes/pki/etcd/server.key \ endpoint health -w table看输出时要确认三件事第一三个 master 的 kube-apiserver 和 etcd 都在 Running第二calico-node 在每个节点上都有一个 Ready 副本第三etcd 三个端点全部 healthy。任何一张表里出现单点都不叫高可用。6.2 把证书轮换与故障演练写进维护习惯证书过期是集群宕机里最常见也最不体面的原因。我用过一个教训模拟项目X 交付后某天接到告警打开一看控制面证书只剩 7 天现场没有维护窗口最后在凌晨做了 renew 并重启了所有控制面组件虽然没有丢数据但那一晚基本没睡。从那以后kubeadm certs check-expiration就是每月巡检的固定动作。# 在任一 master 上查看所有证书到期时间 kubeadm certs check-expiration如果发现即将到期执行续期后必须重启 kubelet 和控制面静态 Pod才能真正生效kubeadm certs renew all systemctl restart kubelet再往后我会每半年演练一次故障切换挑一台 master 执行systemctl stop kubelet观察 VIP 是否漂移、apiserver 是否继续可用然后恢复。这套动作做完多主多从才不是拓扑图上的摆设。希望帮到你。本文还有配套的精品资源点击获取