简介:面向麒麟Kylin V10操作系统与ARM架构CPU环境,这套离线资源合集提供了基于containerd部署Kubernetes 1.26.15多主多从集群的完整素材,适合国产化替代背景下需要自建高可用K8S的运维人员与架构师。包内共47个文件,以系统镜像包(gz)、rpm安装包、shell脚本及yml/yaml/service等配置文件为主,其中既包含kube-apiserver、etcd、coredns、calico等核心组件镜像,也提供keepalived高可用配置、kubeadm初始化与节点加入脚本,整体约622MB,结构清晰可直接落地参考。已有254人学习浏览。其核心价值在于将离线部署所需的关键部分集中整理:从containerd运行时、kubeadm/kubelet二进制到网络插件与负载均衡配置均有覆盖,配合多主多从架构的编排思路,可显著降低在ARM+麒麟环境下搭建生产级K8S集群的调研与踩坑成本。
1. Kylin ARM 集群:为什么不选 Docker,而是 containerd 直连
在 Kylin V10 的 ARM 架构服务器上部署 K8S 1.26.15 多主多从集群,我第一个踩的坑不是 kubeadm 命令,而是容器运行时选型。网上大量教程默认先装 Docker 再让 K8S 调用,但 K8S 从 1.24 开始就把 containerd 作为默认运行时,ARM 版麒麟的 Docker 包不全不说,装完还得处理 cgroup driver、手动改 pause 镜像,绕一大圈回到原点。这份资源合集把 ARM 架构下需要的 containerd 1.7.2、pause-3.9、etcd 3.5.10、各组件镜像和 kubeadm 配置模板全部提前打包,内网离线也能直接拉起一套多主多从集群。适合要在国产 ARM 服务器上快速交付 K8S 环境的运维和交付工程师。
2. 环境初始化:rpm 离线包、内核模块与 hosts 解析
2.1 节点规划与架构确认
先把拓扑定下来。多主多从我建议按三主两从来做,三个 master 承载 apiserver、controller-manager、scheduler 和 etcd,两个 node 跑业务负载。资源包里对应的就是 kubeadm-first-master.yml、kubeadm-join-master.yml 和 kubeadm-join-node.yml 三套模板,分别对应第一台主节点、后续主节点和从节点三种初始化方式。
| 角色 | 主机名 | 推荐配置 | 说明 |
|---|---|---|---|
| master1 | master01 | 4C8G | 首个控制面节点 |
| master2 | master02 | 4C8G | 第二控制面节点 |
| master3 | master03 | 4C8G | 第三控制面节点 |
| node1 | node01 | 4C8G | 运行业务负载 |
| node2 | node02 | 4C8G | 运行业务负载 |
动手前先用 uname -m 确认内核架构是 aarch64,如果是 x86_64 的机器,这套 ARM 资源包就用不上。cat /etc/os-release 确认系统是 Kylin Linux Advanced Server V10,子版本一般是 Halberd,内核停在 Linux 4.19 系列。这两个信息直接决定后续依赖包和镜像选型,我习惯在每台机器上都跑一遍确认再继续。
uname -m cat /etc/os-release cat /proc/versionuname -m 返回 aarch64 才说明这套资源里的 arm64 二进制和镜像能直接用;/etc/os-release 里能看到 V10 的具体子版本号,SP1 和 SP2 在部分依赖包的 glibc 版本上有细微差异,不过资源包里的 rpm 都是兼容实测过的,SP1/SP2 都能装。内核版本建议不低于 4.19,太老的内核缺少 IPVS 模块支持。
2.2 离线 rpm 依赖安装
资源包里有个 pkgs 目录,里面是针对 ARM 麒麟编译好的依赖包,包括 ipvsadm、conntrack、ebtables、socat、ipset、sysstat。其中 ipvsadm 和 ipset 是 kube-proxy 开启 IPVS 模式时的核心依赖,socat 是 kubeadm 做端口检查时要用到的,ebtables 和 conntrack 被 kube-proxy 和网络插件调用。sysstat 不是集群必需,但排查 CPU 和 IO 问题时非常好用,我一般顺手装上。
cd pkgs for rpm in $(ls *.rpm | sort); do rpm -ivh --nodeps "$rpm" || echo "install $rpm failed" done这里用 rpm -ivh 而不是 yum install,是因为离线环境没有可用软件源,--nodeps 可以跳过极少数无意义的依赖检查,但前提是你清楚这个包的真实依赖已经满足。实际安装时我遇到过个别包因为 glibc 版本被卡住,把报错里提示的冲突包用 rpm -e 先卸掉再装就能过。装完 ipvsadm 后建议用 ipvsadm -L 验证一下模块能否正常加载。
2.3 内核模块与 sysctl 参数
K8S 集群对内核有两个硬性要求:桥接流量要能走 iptables,IPVS 相关模块要能加载。ARM 麒麟的 4.19 内核默认不会自动加载这些模块,需要手动 modprobe,并把开机自动加载写进 /etc/modules-load.d/k8s.conf。
cat > /etc/modules-load.d/k8s.conf <<EOF br_netfilter nf_conntrack ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh EOF for m in br_netfilter nf_conntrack ip_vs ip_vs_rr ip_vs_wrr ip_vs_sh; do modprobe $m donebr_netfilter 让 bridge 上的流量也能被 iptables 规则过滤,K8S 的 Service 和 Pod 通信都依赖这个。ip_vs 系列模块是 kube-proxy IPVS 模式的基础,模块不全时 kube-proxy 会报错并从 IPVS 回退到 iptables。加载完用 lsmod | grep ip_vs 确认,如果有缺失模块,检查内核是否编译了对应功能。
内核参数也要调整,主要三条:net.bridge.bridge-nf-call-iptables 控制 iptables 对 bridge 流量的过滤,net.ipv4.ip_forward 开启路由转发,vm.swappiness 调低避免 swap 干扰 kubelet。
cat > /etc/sysctl.d/k8s.conf <<EOF net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 vm.swappiness = 0 EOF sysctl --systemvm.swappiness = 0 是因为 kubelet 对 swap 很敏感,1.26 虽然支持通过 --fail-swap-on=false 容忍 swap,但生产环境建议直接关闭 swap 或者只保留极少量的分页空间。sysctl --system 之后用 sysctl net.ipv4.ip_forward 确认参数已生效,这个参数没开的话,Pod 之间跨节点通信会静默失败。
2.4 主机名与 hosts 解析
多主多从集群里,etcd、apiserver、kubelet 之间大量通过主机名互相访问,机房 DNS 不给力或者干脆没有内网 DNS 时,就得把 /etc/hosts 写死。
hostnamectl set-hostname master01 cat >> /etc/hosts <<EOF 192.168.10.11 master01 192.168.10.12 master02 192.168.10.13 master03 192.168.10.14 node01 192.168.10.15 node02 EOF注意一定要把当前节点自己的 IP 和主机名也写进去,否则 kubelet 注册节点时会因为无法解析本机 hostname 而卡在 NotReady。这一步不要省,很多 join 之后节点状态异常的案例,最后查出来就是 hosts 漏了本机条目。所有节点的主机名建议统一规划,不要用 localhost 或带下划线的名字,K8S 对主机名里的下划线会直接拒绝。
3. containerd 1.7.2 配置:从解压到 crictl 拉通
3.1 解压 cri-containerd-cni 与 libseccomp 处理
资源包里 cri-containerd-cni-1.7.2-linux-arm64.tar.gz 是 containerd 官方发布的 ARM 版打包,里面已经包含了 containerd、ctr、crictl 和标准 CNI 插件,不需要再额外装 runc。解压直接到根目录,它会自动落到 /usr/local/bin、/etc/containerd、/opt/cni 等标准路径。
tar -C / -xzf cri-containerd-cni-1.7.2-linux-arm64.tar.gz解压后验证一下 containerd 版本,ARM 版和 x86 版在命令输出上没有任何差别,但二进制文件格式是 aarch64 的,不能在 x86 机器上跑。此时先别急着启动 containerd,ARM 麒麟自带的 libseccomp 版本可能偏老,containerd 启动时加载 seccomp 过滤规则会报符号找不到的错误,资源包里单独带的 libseccomp.so.2 就是用来替换的。
cp libseccomp.so.2 /usr/lib64/ # 或 /lib/aarch64-linux-gnu/ ldconfig ldconfig -p | grep libseccomp具体放哪个目录,取决于你系统里 libseccomp.so.2 目前所在的路径,用 ldconfig -p | grep libseccomp 查一下原路径再覆盖。这一步做完之后,再启动 containerd 就不会出现 seccomp 相关的加载失败。这个坑在 ARM 麒麟上出现频率很高,x86 的 CentOS 很少遇到,所以我单独列出来。
3.2 config.toml:SystemdCgroup、sandbox_image 与 registry
containerd 默认配置不满足 K8S 的要求,必须改两处:cgroup 驱动和 pause 镜像。K8S 1.26 的 kubelet 默认使用 systemd cgroup driver,containerd 如果不对齐,Pod 启动时 kubelet 会报 cgroup driver 不匹配直接拒绝创建沙箱。
mkdir -p /etc/containerd containerd config default > /etc/containerd/config.toml# 在 /etc/containerd/config.toml 中修改或确认以下关键项 [plugins."io.containerd.grpc.v1.cri"] sandbox_image = "registry.k8s.io/pause:3.9" [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = trueSystemdCgroup = true 让 containerd 用 systemd 来管理 pause 容器的 cgroup,这样与 kubelet 的 cgroup driver 一致。sandbox_image 是每个 Pod 创建时最先拉取的 pause 镜像,K8S 1.26.15 对应的是 pause:3.9,资源包里 pause-3.9.tar.gz 已经打好,导入后把这个值指向导入后的仓库地址即可,避免离线环境下去 registry.k8s.io 拉取超时。
registry 镜像加速的配置也建议顺手加上,虽然离线环境用本地镜像为主,但万一后面要拉公网镜像,没有 mirror 配置的话会一直卡在 ImagePullBackOff。内网 Harbor 或者 Nexus 的地址,按实际环境替换。
3.3 load_images.sh 离线导入全量镜像
资源包 images 目录下放着所有组件镜像的 tar.gz,包括 kube-apiserver、kube-controller-manager、kube-scheduler、kube-proxy、etcd、coredns、pause 和 Calico 全家桶。导入时用 ctr 而不是 docker load,因为 containerd 环境下 ctr 直接对接 containerd 的本地存储。
#!/bin/bash # load_images.sh for img in images/*.tar.gz; do ctr -n k8s.io images import "$img" done关键点在于 -n k8s.io 这个命名空间参数。kubelet 默认通过 CRI 访问 containerd 的 k8s.io 命名空间,而 ctr 默认访问的是 default 命名空间,如果导入时不指定,kubelet 会看不到这些镜像。导入完成后用 ctr -n k8s.io images list 核对,确认每个镜像的 repo 和 tag 都正确。
这里有个容易被忽略的细节:镜像的 repo 名称必须和你 kubeadm 配置里要用的名称一致。比如 kube-apiserver 打包时如果带的前缀是 registry.k8s.io,那么 kubeadm 默认拉取路径就是 registry.k8s.io/kube-apiserver:v1.26.15,只要本地存在这个 tag,kubeadm 就不会再尝试去网络拉取。所以导入后不要随意改 tag,除非你同时改了 kubeadm 配置里的 imageRepository。
3.4 crictl 验证运行时链路
crictl 是排查 containerd 问题的核心工具,但它默认连的是 docker socket,必须配置 /etc/crictl.yaml 指向 containerd 的 socket,否则一执行就报 connection refused。
cat > /etc/crictl.yaml <<EOF runtime-endpoint: unix:///run/containerd/containerd.sock image-endpoint: unix:///run/containerd/containerd.sock timeout: 5 EOF systemctl daemon-reload systemctl enable --now containerd crictl version crictl imagescrictl version 能看到 CRI 版本和 containerd 版本,确认服务端正常响应。crictl images 列出镜像列表,检查 pause 和 kube-apiserver 是否都在。这步相当于把容器运行时链路先拉通,再去做 kubeadm 初始化,能够把问题边界切得非常干净,后面 kubeadm 报错时你不会再怀疑 containerd 本身没装好。
4. kubeadm 多主多从:first-master 初始化到两类 join
4.1 kubelet、kubeadm、kubectl 二进制安装
资源包里有编译好的 v1.26.15 版本 kubeadm、kubelet、kubectl 二进制,直接放到 /usr/bin 并加上可执行权限。这三个二进制是从官方 1.26.15 版本抽出来的,直接用即可,注意不要混用其他小版本的 kubelet 和 kubeadm,组件版本不一致会出现 API 版本协商失败。
cp kubeadm kubectl kubelet /usr/bin/ chmod +x /usr/bin/kubeadm /usr/bin/kubectl /usr/bin/kubelet kubeadm version kubelet --versionkubelet 需要 systemd 托管,资源包里带了 kubelet.service 和 10-kubeadm.conf。10-kubeadm.conf 是 systemd drop-in 配置,核心作用是给 kubelet 指定启动参数,包括 bootstrap-kubeconfig 和 kubelet 配置文件路径,这部分不用自己写,直接放到 /etc/systemd/system/kubelet.service.d/ 目录下即可。
cp kubelet.service /etc/systemd/system/kubelet.service mkdir -p /etc/systemd/system/kubelet.service.d cp 10-kubeadm.conf /etc/systemd/system/kubelet.service.d/10-kubeadm.conf systemctl daemon-reload systemctl enable --now kubelet此时 kubelet 会因为还没有集群而反复重启报错,这是正常的,不要慌。kubelet 启动后会一直尝试连接 apiserver,只有在 kubeadm join 或 init 之后才能稳定运行。只要 systemctl status kubelet 能看到 active 并且在不断重试,就说明配置本身没问题。
4.2 kubeadm-first-master.yml 关键参数
第一台 master 的初始化模板是 kubeadm-first-master.yml,它决定了整个集群的控制面端点、Pod 网段和 Service 网段。这个文件是三台 master 共用的,因为 controlPlaneEndpoint 指向的是 VIP,而不是某个具体节点的 IP。
apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration localAPIEndpoint: advertiseAddress: 192.168.10.11 --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.26.15 controlPlaneEndpoint: 192.168.10.100:6443 networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12controlPlaneEndpoint 必须写成 keepalived 提供的 VIP 加端口,这样所有 join 进来的节点都会通过 VIP 访问 apiserver,VIP 在三台 master 之间漂移时,节点端的 kubelet 不需要做任何调整。podSubnet 用 10.244.0.0/16 是因为 Calico 默认的 IPAM 池和这个网段一致,如果不改 Calico 配置,这里就不要改网段。serviceSubnet 保持默认的 10.96.0.0/12 即可。
4.3 初始化与 kubeconfig 分发
配置检查无误后,在 master01 上执行初始化。整个初始化过程会拉取 etcd、apiserver、controller-manager、scheduler、coredns 等镜像,因为前面已经完成了离线导入,本地仓库里有对应镜像,kubeadm 会直接使用本地镜像,不会去外网拉取。
kubeadm init --config kubeadm-first-master.yml --v=5初始化成功的标志是最后输出一段提示,包含 kubeconfig 配置命令和一条完整的 kubeadm join 命令。先把 kubectl 的 kubeconfig 配置好,这一步不做的话 kubectl 无法连接集群。
mkdir -p $HOME/.kube cp /etc/kubernetes/admin.conf $HOME/.kube/config kubectl get nodes然后把 admin.conf 分发到其他节点,后续在 master02、master03 和 node 节点上操作时都需要这个文件。建议统一都放到用户家目录的 .kube/config 路径下,避免每台机器都重新生成证书。
scp /etc/kubernetes/admin.conf root@master02:/root/.kube/config scp /etc/kubernetes/admin.conf root@master03:/root/.kube/config scp /etc/kubernetes/admin.conf root@node01:/root/.kube/config scp /etc/kubernetes/admin.conf root@node02:/root/.kube/config4.4 join-master 与 join-node 的模板差异
多主多从的本质区别在于:master 用 kubeadm-join-master.yml,node 用 kubeadm-join-node.yml。先拿 token 和 CA 哈希,再到新节点上执行 join。
kubeadm token create --print-join-command kubeadm init phase upload-certs --upload-certskubeadm token create --print-join-command 会输出一行标准的 join 命令,包含 token 和 discovery-token-ca-cert-hash。kubeadm init phase upload-certs --upload-certs 用来把控制面的证书加密上传,输出的 certificate-key 是 join 新 master 时的必填参数,依赖这个 key 来解密控制面证书,然后复制到新 master 上。
# master02 / master03 执行,增加 --control-plane 和 --certificate-key kubeadm join 192.168.10.100:6443 \ --token <token> \ --discovery-token-ca-cert-hash sha256:<hash> \ --control-plane \ --certificate-key <key> # node01 / node02 执行,不带 --control-plane kubeadm join 192.168.10.100:6443 \ --token <token> \ --discovery-token-ca-cert-hash sha256:<hash>master 和 node 的 join 命令差别就这两行参数:control-plane 和 certificate-key。带 control-plane 的节点会额外部署 etcd 成员、apiserver、controller-manager、scheduler 这些控制面组件,不带 control-plane 的节点只启动 kubelet 和 kube-proxy。证书 key 是一次性用品,如果过期或者换 token,重新执行一次 kubeadm init phase upload-certs 再拿新的即可。
5. 高可用与 Calico:keepalived、kube-lb 与 CNI 配合
5.1 keepalived 配置与 VIP 生效
控制面高可用依赖 VIP 在三台 master 之间漂移。VIP 就是 kubeadm 配置里写死的 controlPlaneEndpoint 地址,它由 keepalived 负责维护,正常情况落在 master01 上,master01 故障时自动跳到 master02 或 master03。
# /etc/keepalived/keepalived.conf master01 配置 global_defs { router_id K8S_LVS } vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass K8S_VIP } virtual_ipaddress { 192.168.10.100/24 dev eth0 } }master02 和 master03 的配置只改两处:state 改为 BACKUP,priority 改为 90 和 80。priority 决定抢占顺序,数值越大优先级越高。VIP 的 netmask 要和业务网段一致,否则其他节点访问 VIP 时会因为路由问题通不了。配置完成后启动 keepalived,用 ip addr show eth0 能看到 VIP 已经在 master01 上。
systemctl enable --now keepalived ip addr show eth0 | grep 192.168.10.100有个坑容易忽略:firewalld 会拦 VRRP 广播协议,如果 VIP 在节点间切不出去,先检查防火墙是否放行了 vrrp,或者干脆在测试环境临时停掉 firewalld。keepalived 本身不做流量转发,它只负责通告 VIP,真正的 apiserver 流量要靠下一层的 kube-lb。
5.2 kube-lb 四层转发:VIP 到 apiserver
keepalived 把 VIP 挂到本机网卡后,访问 VIP:6443 的流量会进入本机内核,此时需要一个四层代理把流量转发到三台 master 的 apiserver。资源包里的 kube-lb 就是干这个的,它是轻量级四层负载均衡,配置在 kube-lb.conf 里指定后端列表。
cp kube-lb /usr/local/bin/ cp kube-lb.conf /etc/kube-lb/kube-lb.conf cp kube-lb.service /etc/systemd/system/ systemctl daemon-reload systemctl enable --now kube-lbkube-lb.conf 的核心是定义一个监听 6443 的前端和后端列表,后端就是三台 master 的 apiserver 地址。三个后端都标记为 check,kube-lb 会周期性做 TCP 健康检查,某个节点挂掉后自动从转发列表中摘除。
frontend k8s-apiserver bind *:6443 default_backend k8s-masters backend k8s-masters server master01 192.168.10.11:6443 check inter 3s fall 3 rise 2 server master02 192.168.10.12:6443 check inter 3s fall 3 rise 2 server master03 192.168.10.13:6443 check inter 3s fall 3 rise 2kube-lb 必须和 keepalived 同时运行在同一台 master 上,keepalived 负责让 VIP 出现在本机,kube-lb 负责接管本机 6443 端口的流量并转发。验证方法很简单:在三台 master 的任何一台执行 curl https://192.168.10.100:6443/healthz -k,能看到 ok 就说明 VIP 到 apiserver 的链路是通的。如果返回 refused,先确认当前 VIP 在哪台机器上,再到那台机器看 kube-lb 是否在运行。
5.3 Calico 3.26.4 ARM 镜像与网段对齐
控制面起来之后,Pod 网络还是空的,kubectl get nodes 会看到节点全部 NotReady,因为容器网络插件没有安装。资源包里的 calico.yaml 是 3.26.4 版本,对应 ARM 架构的镜像也已经在 images 目录里打好了包。
Calico 安装前要改两处:镜像地址和 Pod 网段。离线环境下 kubelet 不会去拉公网镜像,所以 calico.yaml 里的 image 字段必须和你导入镜像时的 repo 完全一致。资源包里的镜像名是 calico-node-v3.26.4.tar.gz,导入后的默认 tag 一般是 calico/node:v3.26.4,把 yaml 里的 image 字段同步改掉即可。
grep -n "image:" calico.yaml | head -20 sed -i 's|docker.io/calico/node:v3.26.4|calico/node:v3.26.4|g' calico.yaml sed -i 's|docker.io/calico/calico-cni:v3.26.4|calico/cni:v3.26.4|g' calico.yaml kubectl apply -f calico.yaml网段对齐也很关键。Calico 默认的 IPv4 池是 192.168.0.0/16,而 kubeadm-first-master.yml 里 podSubnet 写的是 10.244.0.0/16,两者不一致的话,Calico 会给 Pod 分配 192.168 网段的 IP,kubelet 却认为 Pod 应该在 10.244 网段,Pod 直接创建失败。需要在 calico.yaml 里把 CALICO_IPV4POOL_CIDR 改成 10.244.0.0/16。
# calico.yaml 中的 ConfigMap 部分 - name: CALICO_IPV4POOL_CIDR value: "10.244.0.0/16"Calico 启动后,还需要确认每个节点的转发网卡。ARM 服务器经常有多网卡,Calico 默认自动选择第一个可用网卡做隧道,如果选错会导致跨节点 Pod 不通。一般在 calico-node 的 DaemonSet 环境变量里加一条 IP_AUTODETECTION_METHOD 指定内网网卡名,比如 eth0,这个值根据实际规划改。
6. 验证与常见问题排查:让集群真正可交付
6.1 集群健康检查清单
集群搭建完不要急着部署业务,先把基础链路验证一遍。我从资源包里的 test、nginx、busybox 这套验证组合里挑最简单的方式:起一个 nginx 无头服务,从其他节点访问,检查跨节点打通情况。
kubectl get nodes -o wide kubectl get pods -A kubectl get svc -A kubectl run nginx-test --image=nginx --restart=Never --port=80 kubectl wait --for=condition=Ready pod/nginx-test --timeout=120s kubectl get pod nginx-test -o widekubectl get nodes 所有节点 Ready,kubectl get pods -A 里 coredns 和 calico 全部 Running,基础网络就算通了。nginx-test 跨节点访问验证,确认调度到 node01 上的 Pod 能从 node02 访问通,这一步检查的是 Calico 的跨节点路由,很多集群 Pod 本地通、跨节点不通的问题都是在这一步暴露的。
6.2 高频踩坑记录
kubelet 报 cgroup driver 不匹配。现象是 kubelet 日志反复出现 failed to run Kubelet: "cgroup driver" 相关内容,kubelet 起不来。原因是 containerd 的 SystemdCgroup 没有开启,而 kubelet 默认用 systemd driver。解决方法是改 /etc/containerd/config.toml 里 SystemdCgroup = true,重启 containerd 和 kubelet。
crictl images 能看到镜像但 Pod 一直 ErrImagePull。现象是 Pod 事件里报 Failed to pull image,明明本地有镜像。原因是 sandbox_image 的 repo 和你导入时的 tag 不一致,kubelet 按配置的完整地址去本地仓库找,找不到就尝试外网拉取。解决方法是把 sandbox_image 改成 ctr -n k8s.io images list 里显示的完整名称。
node join 后一直 NotReady,describe 显示 CNI 未初始化。现象是 kubectl get nodes 看到节点 NotReady,describe 里 network plugin is not ready: cni config uninitialized。原因是 Calico 的 Pod 还没在这个节点上运行,或者 calico.yaml 里的网段与 kubeadm 配置不一致。解决方法是先确认 calico.yaml 的 CALICO_IPV4POOL_CIDR,再 kubectl apply 完整应用 Calico。
第二个 master join 后 etcd 集群异常。现象是 kubectl get nodes 能看到三台 master,但 etcd 容器反复重启,日志报 member 加入失败。原因是 join 时丢失 --certificate-key 参数,或者 certificate-key 过期。解决方法是重新执行 kubeadm init phase upload-certs --upload-certs 生成新 key,再次执行干净的 join。
VIP 在 master 间漂移后 kubectl 失联。现象是 master01 宕机后 VIP 切到 master02,但 kubectl 连不上集群。原因是 kube-lb 只运行在 master01 上,其它 master 没接管流量转发。解决方法是把 kube-lb 做成 systemd 服务并在三台 master 上都启用,keepalived 和 kube-lb 必须同时运行在同一台机器,VIP 漂到哪台,哪台的 6443 就必须有转发进程在监听。
从那以后我在 ARM 麒麟上搭 K8S,每次都是先跑一遍 get_images.sh 确认仓库,再 load_images.sh 导入镜像,然后才动 kubeadm init。这五条坑覆盖了从运行时到网络插件的大部分故障场景,资源包里配套的脚本和 test 镜像都齐,照着这套流程走一遍,基本上一个工作日能拉起一套可交付的多主多从集群。希望帮到你。
本文还有配套的精品资源,点击获取