简介:本资源面向需要在国产化信创环境中落地容器编排的运维与云原生工程师,提供基于银河麒麟Kylin V10、ARM架构并采用外部etcd与containerd运行时部署Kubernetes 1.26.15一主多从集群的完整离线材料,可解决ARM平台镜像与依赖包获取困难、离线安装步骤繁琐的问题。压缩包共41个文件,约645.74MB,包含16个gz离线镜像与组件包、11个rpm系统依赖、4个sh镜像加载与拉取脚本,以及kubeadm、kubelet、kubectl等二进制文件和yaml、conf、service配置模板,覆盖etcd、containerd、Calico网络与CoreDNS等核心组件。已有131人学习下载。读者可据此在无外网环境下完成集群初始化、节点加入与网络插件部署,脚本与配置可直接复用,便于快速搭建测试环境并排查ARM架构下的兼容性问题。
1. 麒麟 V10 + ARM 上跑 K8S 1.26.15:这套离线包到底值不值得下
如果你手里正好有一台飞腾、鲲鹏或者麒麟的 ARM 服务器,系统是 Kylin V10(Halberd 或 SP1),网络又基本出不去,那你大概率已经体会过那种绝望:kubeadm init卡在拉镜像,crictl pull转圈转到天荒地老,最后给你甩一句the api server is not healthy after 4m0.00747357s。这套资源合集就是冲着这个场景来的——它把 K8S 1.26.15 在 ARM64 上跑起来所需的一整套离线物料打包好了:kubeadm/kubelet/kubectl 二进制、containerd 1.7.2 的 cri-containerd-cni 包、etcd 3.5.10 的 ARM64 二进制、Calico 3.26.4 的镜像 tar 包、CoreDNS 1.9.3、pause 3.9,还有 libseccomp 2.5.4 这种在麒麟上经常缺的底层依赖。适合谁?适合要在内网、信创环境里搭一主多从集群的运维和交付工程师,也适合想搞明白 ARM 上 K8S 离线部署到底和 x86 差在哪的开发者。它不解决“你会不会 K8S”,它解决的是“物料齐不齐、版本对不对、依赖缺不缺”。
2. 拆包看物料:ARM64 离线部署的依赖链与选型逻辑
拿到一个 tar.gz 别急着解压就干,先搞清楚里面每一层是干嘛的。ARM 上的 K8S 离线部署,本质是一条依赖链:内核模块和系统工具 → 容器运行时 → K8S 组件二进制 → 集群网络插件镜像。任何一环版本对不上,后面就是玄学报错。
2.1 为什么是 containerd 1.7.2 而不是 Docker
K8S 1.26 已经正式移除了对 Docker shim 的支持,dockershim 在 1.24 就被砍了。所以这套资源直接用 cri-containerd-cni-1.7.2-linux-arm64.tar.gz,这是正确路线。containerd 1.7.x 对 CRI 的支持已经稳定,而且自带 CNI 插件,省得你单独去凑 CNI 的二进制。
选 1.7.2 这个版本还有个现实原因:它和 K8S 1.26.15 的 CRI 接口兼容性经过大量生产验证,再新的 containerd 2.x 在麒麟 V10 这种偏保守的内核上反而容易出幺蛾子。我一般会先确认containerd --version和runc --version,runc 版本太低会导致容器起不来,报OCI runtime create failed。
2.2 etcd 3.5.10 独立部署的意义
资源里给了 etcd-v3.5.10-linux-arm64.tar.gz 和 etcd-3.5.10-0.tar.gz 两个包,还有 etcd_ssl.tar.gz。这说明作者是打算把 etcd 外置的,不是用 kubeadm 默认的堆叠式 etcd。
外置 etcd 的好处很直接:etcd 是 K8S 的“黑匣子”,所有集群状态都在里面。堆叠式 etcd 和 master 节点绑死,master 挂了 etcd 也悬。外置之后,你可以单独备份、单独扩容、单独调优。ARM 机器磁盘 IO 往往不如 x86 服务器,etcd 对磁盘延迟极其敏感,外置到独立盘或者独立节点是明智的。etcd_ssl.tar.gz 里应该是证书,外置 etcd 必须配 TLS,否则 kube-apiserver 连不上。
2.3 Calico 3.26.4 与镜像清单
calico.yaml 配合 calico-cni-v3.26.4.tar.gz、calico-node-v3.26.4.tar.gz、calico-kube-controllers-v3.26.4.tar.gz 三个镜像包。Calico 3.26 支持 ARM64,而且它的 BGP 模式在信创环境里比 IPIP 更常用,因为很多内网交换机对 IPIP 隧道不友好。
镜像清单里还有 coredns-v1.9.3.tar.gz、pause-3.9.tar.gz、kube-apiserver/controller-manager/scheduler/kube-proxy 的 v1.26.15 tar 包。这些就是kubeadm init时会去拉的东西,离线环境下必须提前 load 进去。
2.4 系统依赖:libseccomp 和那些容易被忽略的包
libseccomp-2.5.4.tar.gz 和 libseccomp.so.2 是重点。麒麟 V10 默认的 libseccomp 版本可能偏低,containerd 1.7.2 依赖较新的 seccomp 支持,缺了会报failed to create seccomp profile或者容器直接起不来。
pkgs 目录下的 sysstat、ipvsadm、conntrack、ebtables、socat、ipset 是 kube-proxy 走 IPVS 模式必需的。如果你不装这些,kube-proxy 会退化成 iptables 模式,在大规模 Service 场景下性能差很多。10-kubeadm.conf、kubelet.service、kubelet 这几个文件是配置 kubelet 开机自启和 cgroup 驱动的。
提示:解压后先别动,用
tar -tzf列一遍文件,对照上面的清单确认没有缺包,再开始装。
3. 从零到集群 Ready:一主多从的实操步骤
这一章是能直接抄作业的部分。假设你已经有一台 master(比如 192.168.1.10)和至少一台 node(192.168.1.11),系统都是 Kylin V10 ARM64,root 权限。
3.1 系统层准备:关 swap、加载内核模块、装依赖
先做所有节点都要做的系统配置。
# 关闭 swap,K8S 强制要求 swapoff -a sed -i '/swap/s/^/#/' /etc/fstab # 加载必要内核模块 cat > /etc/modules-load.d/k8s.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逻辑说明:overlay是 containerd 的存储驱动,br_netfilter让 iptables 能过滤桥接流量,这是 Calico 和 kube-proxy 正常工作的前提。ip_forward=1不打开,Pod 跨节点通信直接废掉。
参数说明:/etc/modules-load.d/k8s.conf是开机自动加载,sysctl --system让配置立即生效。麒麟 V10 的 firewalld 建议先systemctl stop firewalld && systemctl disable firewalld,否则后面端口各种不通。
然后装系统依赖包:
# 进入 pkgs 目录 cd pkgs rpm -ivh *.rpm 2>/dev/null || yum localinstall -y *.rpm # 安装 libseccomp tar -xzf libseccomp-2.5.4.tar.gz cd libseccomp-2.5.4 ./configure --prefix=/usr make && make install ldconfig逻辑说明:pkgs 里的 rpm 直接装,如果依赖报错就用yum localinstall自动解决。libseccomp 从源码编译,装完必须ldconfig刷新动态库缓存,否则 containerd 找不到libseccomp.so.2。
3.2 部署 containerd 1.7.2
# 解压 cri-containerd-cni 包到根目录 tar -xzf cri-containerd-cni-1.7.2-linux-arm64.tar.gz -C / # 生成默认配置 containerd config default > /etc/containerd/config.toml # 修改 cgroup 驱动为 systemd sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml # 修改 sandbox 镜像为国内可用的 pause 地址(离线环境用本地) sed -i 's#sandbox_image = ".*"#sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9"#' /etc/containerd/config.toml systemctl daemon-reload systemctl enable containerd systemctl restart containerd systemctl status containerd逻辑说明:SystemdCgroup = true必须改,否则 kubelet 和 containerd 的 cgroup 驱动不一致,Pod 会反复重启。sandbox_image指向 pause 3.9,离线环境里这个镜像已经通过 load_images.sh 加载到本地了,但配置里写 registry 地址时 containerd 会先查本地。
参数说明:config default生成的是 containerd 官方默认配置,1.7.2 的默认 cgroup 驱动是 false,这是 ARM 上最常见的坑之一。
3.3 安装 kubeadm、kubelet、kubectl
# 拷贝二进制 cp kubeadm kubectl kubelet /usr/local/bin/ chmod +x /usr/local/bin/kubeadm /usr/local/bin/kubectl /usr/local/bin/kubelet # 配置 kubelet 服务 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 kubelet逻辑说明:kubelet 不通过 systemd 启动管理,而是由 kubeadm 在 init 时拉起,所以这里只 enable 不 start。10-kubeadm.conf里定义了 kubelet 的启动参数,比如--container-runtime-endpoint=unix:///run/containerd/containerd.sock。
参数说明:如果你的 containerd socket 路径不是默认的,要改10-kubeadm.conf里的KUBELET_EXTRA_ARGS。
3.4 加载所有离线镜像
# load_images.sh 内容大致如下 for img in *.tar.gz; do echo "Loading $img ..." ctr -n k8s.io images import $img done # 或者用 crictl # ctr 是 containerd 自带的,不需要额外装 ctr -n k8s.io images ls | grep -E "kube-apiserver|calico|coredns|pause"逻辑说明:ctr -n k8s.io指定 namespace 为 k8s.io,这是 kubelet 使用的 namespace。如果你用ctr images import不加-n k8s.io,镜像会进 default namespace,kubelet 看不到,然后kubeadm init还是会去拉镜像然后失败。
参数说明:load_images.sh里如果用的是ctr -n k8s.io images import,直接跑就行。导入完成后用ctr -n k8s.io images ls确认关键镜像都在。
3.5 初始化 master 节点
先准备 kubeadm-config.yaml:
apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration localAPIEndpoint: advertiseAddress: 192.168.1.10 bindPort: 6443 nodeRegistration: criSocket: unix:///run/containerd/containerd.sock kubeletExtraArgs: cgroup-driver: systemd --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.26.15 imageRepository: registry.aliyuncs.com/google_containers networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 etcd: external: endpoints: - https://192.168.1.20:2379 caFile: /etc/etcd/ssl/ca.pem certFile: /etc/etcd/ssl/etcd.pem keyFile: /etc/etcd/ssl/etcd-key.pem逻辑说明:advertiseAddress改成你 master 的实际 IP。podSubnet要和 calico.yaml 里的 CALICO_IPV4POOL_CIDR 一致,默认是 10.244.0.0/16。etcd.external指向你外置 etcd 的地址和证书路径,证书从 etcd_ssl.tar.gz 解出来放到对应目录。
参数说明:imageRepository写阿里云地址是为了让 kubeadm 生成静态 Pod 的 yaml 时引用这个地址,但实际镜像已经在本地了,kubelet 会优先用本地。如果你完全离线,这个地址不会被访问。
然后执行:
kubeadm init --config kubeadm-config.yaml --upload-certs如果卡在the api server is not healthy after 4m0.00747357s,别慌,这是 ARM 离线部署最高频的报错。先看crictl ps -a看 kube-apiserver 容器状态,再看crictl logs <container_id>,大概率是镜像没导入到 k8s.io namespace,或者 etcd 证书路径不对。
3.6 加入 worker 节点
master init 成功后会输出 join 命令,类似:
kubeadm join 192.168.1.10:6443 --token abcdef.1234567890abcdef \ --discovery-token-ca-cert-hash sha256:xxxx资源里的 kubeadm-join-node.yml 就是给 node 节点用的 join 配置文件,内容大致是:
apiVersion: kubeadm.k8s.io/v1beta3 kind: JoinConfiguration discovery: bootstrapToken: apiServerEndpoint: 192.168.1.10:6443 token: abcdef.1234567890abcdef caCertHashes: - sha256:xxxx nodeRegistration: criSocket: unix:///run/containerd/containerd.sock kubeletExtraArgs: cgroup-driver: systemd在 node 节点上执行kubeadm join --config kubeadm-join-node.yml即可。加入后在 master 上kubectl get nodes确认状态为 Ready。
3.7 部署 Calico 网络
# 先加载 calico 镜像 ctr -n k8s.io images import calico-cni-v3.26.4.tar.gz ctr -n k8s.io images import calico-node-v3.26.4.tar.gz ctr -n k8s.io images import calico-kube-controllers-v3.26.4.tar.gz # 修改 calico.yaml 里的 CIDR sed -i 's#value: "192.168.0.0/16"#value: "10.244.0.0/16"#' calico.yaml kubectl apply -f calico.yaml kubectl get pods -n kube-system -w逻辑说明:Calico 的 CALICO_IPV4POOL_CIDR 必须和 kubeadm 的 podSubnet 一致,否则 Pod 拿不到 IP。ARM 上 Calico 的 felix 和 typha 组件对内核版本有要求,麒麟 V10 的内核一般没问题,但如果报iptables: No chain/target/match by that name,检查 br_netfilter 是否加载。
参数说明:kubectl get pods -n kube-system -w持续观察,直到 calico-node 全部 Running,coredns 也 Running,集群才算真正可用。
4. 避坑排查:ARM 离线部署最常见的五个翻车点
这一章全是血泪经验,每条都按“现象 → 原因 → 解决”写,遇到问题直接对号入座。
4.1 现象:kubeadm init 卡 4 分钟报 api server not healthy
原因:kube-apiserver 容器根本没起来。最常见的是镜像没导入到k8s.ionamespace,或者 etcd 连不上。ARM 上还可能是 kube-apiserver 二进制和系统 glibc 不兼容。
解决:先crictl ps -a | grep apiserver看容器状态。如果是 Exited,crictl logs <id>看日志。如果是镜像问题,重新ctr -n k8s.io images import kube-apiserver-v1.26.15.tar.gz。如果是 etcd 问题,在 master 上用openssl s_client -connect 192.168.1.20:2379 -CAfile /etc/etcd/ssl/ca.pem测试连通性。
4.2 现象:node 节点 join 后状态一直是 NotReady
原因:node 上的 containerd 没配好,或者 cgroup 驱动不一致,或者 Calico 没在该节点拉起。
解决:在 node 上systemctl status containerd和journalctl -u kubelet -f看日志。重点检查/etc/containerd/config.toml里SystemdCgroup是否为 true,以及10-kubeadm.conf里 cgroup-driver 是否为 systemd。两边必须一致。
4.3 现象:Pod 一直 ContainerCreating,describe 显示 failed to create seccomp profile
原因:libseccomp 版本太低,containerd 无法为容器生成 seccomp 配置。
解决:确认ldconfig -p | grep seccomp能看到libseccomp.so.2。如果没有,重新编译安装 libseccomp-2.5.4,然后systemctl restart containerd。麒麟 V10 上还要确认/usr/lib64和/usr/lib下都有软链接。
4.4 现象:Calico 节点报 iptables 错误,Pod 跨节点不通
原因:br_netfilter模块没加载,或者net.bridge.bridge-nf-call-iptables没设为 1。
解决:lsmod | grep br_netfilter确认模块加载,sysctl net.bridge.bridge-nf-call-iptables确认值为 1。如果没有,重新执行 3.1 节的内核配置并重启节点。ARM 上有些内核编译时没带 br_netfilter,那就只能换内核或者用 Calico 的 eBPF 模式(但 3.26 的 eBPF 在 ARM 上还不算特别稳)。
4.5 现象:coredns Pod 一直 CrashLoopBackOff
原因:coredns 镜像没加载,或者 serviceSubnet 和 podSubnet 冲突,或者节点上 resolv.conf 有问题。
解决:ctr -n k8s.io images ls | grep coredns确认镜像在。检查 kubeadm-config.yaml 里 serviceSubnet 和 podSubnet 没有重叠。麒麟 V10 的/etc/resolv.conf如果指向了不可用的 DNS,coredns 会启动失败,临时改成nameserver 114.114.114.114再试。
注意:每次改完 containerd 配置或内核参数,都要
systemctl daemon-reload再重启对应服务,否则改动不生效,你会以为改了但实际没改,然后怀疑人生。
5. 验证与进阶:把集群跑稳的几个硬核习惯
集群 Ready 只是开始,真正交付前我一般会走一遍下面的验证流程,这套习惯是从多次翻车之后强制自己养成的。
5.1 用 test 目录做冒烟测试
资源里有个 test 目录,里面是 nginx 和 busybox 的 yaml。别小看这两个,它们是验证集群网络和 DNS 的最快方式。
kubectl create deployment nginx --image=nginx --replicas=2 kubectl expose deployment nginx --port=80 --type=NodePort kubectl run busybox --image=busybox --rm -it --restart=Never -- sh # 在 busybox 里执行 wget -qO- nginx nslookup kubernetes.default逻辑说明:wget -qO- nginx验证 Service 的 ClusterIP 和 kube-proxy 是否正常,nslookup kubernetes.default验证 CoreDNS 是否正常。两个都通,集群基本功能就没问题。
参数说明:--restart=Never让 busybox 以 Pod 形式运行而不是 Deployment,退出后自动清理。-it进入交互模式。
5.2 检查 IPVS 模式是否生效
如果你装了 ipvsadm,可以确认 kube-proxy 是不是真的走了 IPVS:
ipvsadm -Ln | grep -E "10.96|10.244"如果输出为空,说明 kube-proxy 还在 iptables 模式。检查 kube-proxy 的 ConfigMap:
kubectl -n kube-system edit configmap kube-proxy # 把 mode: "" 改成 mode: "ipvs" kubectl -n kube-system rollout restart daemonset kube-proxy逻辑说明:IPVS 模式在 Service 数量超过 1000 时性能优势明显,ARM 机器 CPU 核数通常不多,IPVS 的哈希查找比 iptables 的线性匹配更省资源。
参数说明:改完 ConfigMap 必须重启 kube-proxy 的 DaemonSet,否则旧 Pod 不会重新加载配置。
5.3 外置 etcd 的备份习惯
外置 etcd 最大的好处就是能独立备份。我一般会写个定时任务:
ETCDCTL_API=3 etcdctl \ --endpoints=https://192.168.1.20:2379 \ --cacert=/etc/etcd/ssl/ca.pem \ --cert=/etc/etcd/ssl/etcd.pem \ --key=/etc/etcd/ssl/etcd-key.pem \ snapshot save /backup/etcd-$(date +%Y%m%d%H%M).db逻辑说明:ETCDCTL_API=3必须加,否则 etcdctl 会用 v2 API,报错。快照文件建议保留最近 7 天,etcd 数据目录和快照目录不要放同一块盘。
参数说明:--endpoints指向你的 etcd 地址,证书路径和 kubeadm-config.yaml 里的一致。恢复的时候用etcdctl snapshot restore,但恢复前一定要停掉 kube-apiserver,否则数据会写冲突。
5.4 一个容易忽略的细节:kubelet 的 cgroup 驱动
麒麟 V10 默认可能用 cgroup v1,而 containerd 1.7.2 和 K8S 1.26 对 cgroup v2 的支持更好。检查stat -fc %T /sys/fs/cgroup/,如果输出cgroup2fs就是 v2,tmpfs就是 v1。v1 也能跑,但要在 kubelet 和 containerd 两边都显式指定systemd驱动,不能一个 systemd 一个 cgroupfs,否则 Pod 资源限制会失效,表现为kubectl describe pod里看不到 CPU/内存 limit。
从那以后我每次部署完 ARM 集群,都会强制走一遍kubectl run冒烟测试 +ipvsadm -Ln确认 + etcd 快照验证这三步,少一步心里都不踏实。希望帮到你。
本文还有配套的精品资源,点击获取