简介本资源面向需要在国产化信创环境中落地容器编排的运维与云原生工程师聚焦Kylin V10操作系统搭配ARM架构服务器、采用外部etcd与containerd运行时部署Kubernetes 1.26.15一主多从集群的完整离线方案。压缩包共41个文件约645.74MB涵盖gz离线镜像与组件包、rpm系统依赖、sh自动化脚本、yaml与yml集群配置清单以及kubeadm、kubectl、kubelet等核心二进制与service、conf配置文件覆盖etcd、containerd、Calico网络、CoreDNS等关键组件。资源提供kubeadm初始化与节点加入配置、镜像批量加载与拉取脚本以及libseccomp、ipvsadm、conntrack等系统依赖便于在无外网或受限网络下完成集群搭建与验证。已有131人学习下载适合希望掌握ARM平台K8S部署流程、排查组件兼容与网络插件问题的技术人员参考。1. 麒麟 V10 ARM 上跑 K8S 1.26为什么这套组合值得折腾手里有一批鲲鹏或飞腾的 ARM 服务器系统装的是银河麒麟 V10现在要把 K8S 1.26.15 集群拉起来而且 etcd 要独立在外部、容器运行时用 containerd 而不是 Docker——这个场景在信创交付里越来越常见。很多人第一次上手会卡在镜像架构不对、二进制包找不到、kubelet 起不来这几个地方网上一搜全是 x86 的教程直接照抄必然翻车。这篇笔记就是把这套组合从零到集群 Ready 的完整路径拆开包括 ARM 架构下每个组件的选型理由、具体命令、参数含义以及我实际踩过的坑。适合正在做信创环境 K8S 交付的运维和平台工程师也适合想在自己 ARM 机器上搭一套学习环境的人。读完你至少能拿到一份可复现的部署清单知道每一步为什么这么做、出错该看哪里。2. 动手之前ARM 麒麟 V10 的环境底座怎么定2.1 确认 CPU 架构和系统版本别拿错包麒麟 V10 在 ARM 上有多个版本nkvers或cat /etc/kylin-release先看清楚。CPU 架构用uname -m确认ARM64 会输出aarch64。这一步看起来废话但我见过有人把 x86 的 kubelet 二进制拷到 ARM 机器上systemctl start kubelet直接报Exec format error排查半天才反应过来是架构不对。# 确认系统版本 cat /etc/kylin-release # 确认 CPU 架构必须是 aarch64 uname -m # 确认内核版本K8S 1.26 要求 4.x 以上 uname -r逻辑说明/etc/kylin-release给出的是麒麟的发行版本号uname -m给出硬件架构。两个信息缺一不可因为后续下载所有二进制包都要按aarch64去找。参数方面没有需要调的纯粹是信息采集。如果uname -m输出x86_64那这套方案里的 ARM 相关步骤就不适用需要换对应的 amd64 包。2.2 关掉 swap、加载内核模块、调 sysctlK8S 对系统环境有硬性要求麒麟 V10 默认开了 swap 和防火墙这些都要处理。另外 ARM 平台上br_netfilter和overlay这两个内核模块必须确认加载containerd 的 overlayfs 存储驱动依赖它们。# 关闭 swap注释掉 fstab 里的 swap 行 swapoff -a sed -i /swap/s/^/#/ /etc/fstab # 加载内核模块 modprobe overlay modprobe br_netfilter cat /etc/modules-load.d/k8s.conf EOF overlay br_netfilter EOF # 设置网络转发和桥接参数 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 # 关闭防火墙和 SELinux麒麟 V10 默认可能是 permissive systemctl stop firewalld systemctl disable firewalld setenforce 0 sed -i s/^SELINUXenforcing/SELINUXpermissive/ /etc/selinux/config逻辑说明swapoff -a是临时关闭注释 fstab 是永久生效两者都要做。modprobe加载模块后写入/etc/modules-load.d/是为了重启后自动加载。sysctl里的bridge-nf-call-iptables让 iptables 能处理桥接流量ip_forward开启 IP 转发这两个是 K8S Service 和 Pod 网络通信的基础。防火墙和 SELinux 在实验环境可以关生产环境如果必须开需要额外放行大量端口建议先关掉跑通再逐步收紧。2.3 时间同步和主机名集群节点之间不能有偏差etcd 对时间敏感节点之间时间偏差过大会导致选主异常。麒麟 V10 一般自带 chronyd确认它正常运行。主机名也要规划好后续 kubelet 注册节点时用的就是主机名。# 确认时间同步 timedatectl status systemctl enable --now chronyd chronyc sources -v # 设置主机名每台机器不同 hostnamectl set-hostname k8s-master01 # 写入 hosts所有节点保持一致 cat /etc/hosts EOF 192.168.1.10 k8s-master01 192.168.1.11 k8s-node01 192.168.1.12 k8s-node02 192.168.1.20 etcd01 EOF逻辑说明chronyc sources -v能看到当前同步的时间源和偏差值^*开头的那行就是正在用的源。hosts 文件里把 master、node、etcd 的 IP 和主机名都写进去后续 kubeadm 初始化时可以直接用主机名。参数上注意 etcd 节点如果独立部署也要加入 hosts。3. 外部 etcd 集群ARM 上从二进制到健康检查3.1 为什么把 etcd 拆出来而不是让 kubeadm 自己管kubeadm 默认会把 etcd 以静态 Pod 的形式跑在 master 节点上但生产环境里更常见的做法是外部 etcd。原因有三个第一etcd 对磁盘 I/O 敏感独立部署可以挂 SSD 并单独调优第二master 节点故障时 etcd 不受影响恢复更灵活第三ARM 环境下如果 master 节点资源有限把 etcd 拆出去能降低单点压力。代价是多了一层网络通信etcd 节点之间的延迟要控制在毫秒级。3.2 下载 ARM64 版 etcd 并配置 systemd 管理etcd 官方 release 页面有 linux-arm64 的包选 3.5.x 版本和 K8S 1.26 兼容。下载后解压把二进制放到/usr/local/bin然后写 systemd unit 文件。# 下载 etcdARM64 cd /tmp wget https://github.com/etcd-io/etcd/releases/download/v3.5.12/etcd-v3.5.12-linux-arm64.tar.gz tar xzf etcd-v3.5.12-linux-arm64.tar.gz cp etcd-v3.5.12-linux-arm64/etcd /usr/local/bin/ cp etcd-v3.5.12-linux-arm64/etcdctl /usr/local/bin/ chmod x /usr/local/bin/etcd /usr/local/bin/etcdctl # 创建数据和配置目录 mkdir -p /var/lib/etcd /etc/etcd逻辑说明etcd 的 ARM64 包和 x86 包在文件名上只差arm64和amd64下载时注意别选错。/var/lib/etcd是数据目录生产环境建议单独挂一块盘。/etc/etcd放配置文件。etcdctl 是命令行工具后面健康检查要用。# 创建 etcd systemd unit cat /etc/systemd/system/etcd.service EOF [Unit] Descriptionetcd Afternetwork.target [Service] Typenotify ExecStart/usr/local/bin/etcd \\ --name etcd01 \\ --data-dir /var/lib/etcd \\ --listen-client-urls http://0.0.0.0:2379 \\ --advertise-client-urls http://192.168.1.20:2379 \\ --listen-peer-urls http://0.0.0.0:2380 \\ --initial-advertise-peer-urls http://192.168.1.20:2380 \\ --initial-cluster etcd01http://192.168.1.20:2380 \\ --initial-cluster-token etcd-cluster \\ --initial-cluster-state new Restartalways RestartSec5 [Install] WantedBymulti-user.target EOF systemctl daemon-reload systemctl enable --now etcd systemctl status etcd逻辑说明--name是节点名集群内唯一。--listen-client-urls监听所有地址的 2379 端口--advertise-client-urls是告诉客户端连哪个地址。--initial-cluster列出所有 etcd 节点单节点就写自己。多节点时每个节点的--initial-cluster内容相同但--name和--initial-advertise-peer-urls不同。--initial-cluster-state new表示新建集群如果是加入已有集群要改成existing。3.3 验证 etcd 健康状态和基本读写etcd 起来之后用 etcdctl 检查健康状态。K8S 1.26 对应的 etcd 3.5 版本etcdctl 默认用 v3 API不需要额外设环境变量。# 健康检查 etcdctl --endpointshttp://192.168.1.20:2379 endpoint health # 查看成员列表 etcdctl --endpointshttp://192.168.1.20:2379 member list # 写入和读取测试 etcdctl --endpointshttp://192.168.1.20:2379 put testkey hello etcdctl --endpointshttp://192.168.1.20:2379 get testkey逻辑说明endpoint health返回is healthy说明 etcd 正常。member list能看到集群成员和状态。put和get是最简单的读写验证确认数据能正常存取。如果 health 检查失败先看journalctl -u etcd -f的日志常见问题是端口被占用或数据目录权限不对。4. containerd 在 ARM 麒麟上的安装与配置4.1 选 containerd 而不是 Docker 的理由K8S 1.24 之后移除了 dockershimDocker 不再是默认支持的容器运行时。containerd 更轻量直接和 K8S 的 CRI 对接少了一层调用。ARM 平台上 containerd 的二进制包也比较好找麒麟 V10 的软件源里可能自带但版本不一定满足 K8S 1.26 的要求建议手动装官方二进制。# 下载 containerd ARM64 版本 cd /tmp wget https://github.com/containerd/containerd/releases/download/v1.7.13/containerd-1.7.13-linux-arm64.tar.gz tar xzf containerd-1.7.13-linux-arm64.tar.gz cp bin/* /usr/local/bin/ # 下载 runc ARM64 wget https://github.com/opencontainers/runc/releases/download/v1.1.10/runc.arm64 mv runc.arm64 /usr/local/bin/runc chmod x /usr/local/bin/runc # 下载 CNI 插件 wget https://github.com/containernetworking/plugins/releases/download/v1.4.0/cni-plugins-linux-arm64-v1.4.0.tgz mkdir -p /opt/cni/bin tar xzf cni-plugins-linux-arm64-v1.4.0.tgz -C /opt/cni/bin/逻辑说明containerd 的 tar 包里bin/目录下是 containerd、containerd-shim 等二进制全部拷到/usr/local/bin。runc 是实际创建容器的工具containerd 调用它。CNI 插件负责 Pod 网络解压到/opt/cni/bin这是 K8S 默认的 CNI 路径。三个组件的 ARM64 版本都要确认任何一个拿成 x86 的都会导致容器起不来。4.2 生成默认配置并修改关键参数containerd 需要一个配置文件用containerd config default生成默认配置再改。关键改动有两处SystemdCgroup要改成true和 K8S 的 cgroup 驱动保持一致sandbox_image要改成能拉到的 pause 镜像地址。# 生成默认配置 mkdir -p /etc/containerd containerd config default /etc/containerd/config.toml # 修改关键参数 sed -i s/SystemdCgroup false/SystemdCgroup true/ /etc/containerd/config.toml sed -i s|sandbox_image .*|sandbox_image registry.aliyuncs.com/google_containers/pause:3.9| /etc/containerd/config.toml # 创建 systemd unit cat /etc/systemd/system/containerd.service EOF [Unit] Descriptioncontainerd container runtime Afternetwork.target [Service] ExecStartPre/sbin/modprobe overlay ExecStart/usr/local/bin/containerd Restartalways RestartSec5 Delegateyes KillModeprocess OOMScoreAdjust-999 LimitNOFILE1048576 LimitNPROCinfinity LimitCOREinfinity [Install] WantedBymulti-user.target EOF systemctl daemon-reload systemctl enable --now containerd systemctl status containerd逻辑说明SystemdCgroup true是必须的否则 kubelet 和 containerd 的 cgroup 驱动不一致Pod 会反复重启。sandbox_image是 Pod 的沙箱镜像默认是registry.k8s.io/pause国内环境可能拉不到换成阿里云镜像源。systemd unit 里的Delegateyes让 containerd 能管理自己的 cgroupLimitNOFILE调大文件描述符上限生产环境容器多了默认值不够用。4.3 配置 crictl 并验证 containerd 可用crictl 是 CRI 的命令行工具kubelet 通过 CRI 和 containerd 通信crictl 可以用来排查。需要告诉 crictl 连哪个 containerd 端点。# 配置 crictl cat /etc/crictl.yaml EOF runtime-endpoint: unix:///run/containerd/containerd.sock image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 debug: false EOF # 验证 crictl version crictl info逻辑说明runtime-endpoint指向 containerd 的 socket 文件这是 containerd 默认的监听地址。crictl version能看到 RuntimeName 和 RuntimeVersioncrictl info输出 containerd 的详细配置。如果连不上检查 containerd 是否在跑、socket 文件是否存在。5. kubeadm 初始化 master参数怎么给、报错怎么看5.1 安装 kubeadm、kubelet、kubectl 的 ARM64 二进制K8S 官方为 ARM64 提供了二进制包直接下载对应版本的 kubeadm、kubelet、kubectl放到/usr/local/bin。注意版本号要一致1.26.15 就全部用 1.26.15。# 下载 K8S 1.26.15 ARM64 二进制 cd /tmp wget https://dl.k8s.io/v1.26.15/bin/linux/arm64/kubeadm wget https://dl.k8s.io/v1.26.15/bin/linux/arm64/kubelet wget https://dl.k8s.io/v1.26.15/bin/linux/arm64/kubectl chmod x kubeadm kubelet kubectl mv kubeadm kubelet kubectl /usr/local/bin/ # 创建 kubelet systemd unit cat /etc/systemd/system/kubelet.service EOF [Unit] Descriptionkubelet Aftercontainerd.service Requirescontainerd.service [Service] ExecStart/usr/local/bin/kubelet Restartalways RestartSec10 EnvironmentKUBELET_KUBECONFIG_ARGS--bootstrap-kubeconfig/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig/etc/kubernetes/kubelet.conf EnvironmentKUBELET_CONFIG_ARGS--config/var/lib/kubelet/config.yaml EnvironmentFile-/var/lib/kubelet/kubeadm-flags.env ExecStart ExecStart/usr/local/bin/kubelet \$KUBELET_KUBECONFIG_ARGS \$KUBELET_CONFIG_ARGS \$KUBELET_EXTRA_ARGS [Install] WantedBymulti-user.target EOF systemctl daemon-reload systemctl enable kubelet逻辑说明kubelet 的 systemd unit 里引用了两个文件/etc/kubernetes/bootstrap-kubelet.conf和/etc/kubernetes/kubelet.conf这两个文件在 kubeadm init 之前不存在所以 kubelet 启动会失败这是正常的。/var/lib/kubelet/kubeadm-flags.env也是 kubeadm 生成的。先 enable 不 start等 kubeadm init 跑完再启动。5.2 准备 kubeadm 配置文件指定外部 etcdkubeadm 的配置文件用 YAML 格式关键字段包括kubernetesVersion、controlPlaneEndpoint、etcd.external、networking.podSubnet、imageRepository。外部 etcd 的 endpoints 要写对。# kubeadm-config.yaml apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.26.15 controlPlaneEndpoint: 192.168.1.10:6443 imageRepository: registry.aliyuncs.com/google_containers networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 etcd: external: endpoints: - http://192.168.1.20:2379 --- apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration cgroupDriver: systemd逻辑说明controlPlaneEndpoint是 API Server 的地址单 master 就写 master 的 IP 和 6443 端口。imageRepository换成阿里云镜像源否则拉不到镜像。podSubnet是 Pod 网段后面装 CNI 时要对应。etcd.external.endpoints指向外部 etcd 的地址。cgroupDriver: systemd和 containerd 的配置保持一致。5.3 执行 kubeadm init 并处理常见报错配置文件准备好之后执行kubeadm init --config kubeadm-config.yaml。这一步最容易出的问题是镜像拉不下来和 API Server 健康检查超时。# 先拉镜像确认能拉下来 kubeadm config images pull --config kubeadm-config.yaml # 初始化 kubeadm init --config kubeadm-config.yaml --upload-certs # 初始化成功后配置 kubectl mkdir -p $HOME/.kube cp /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config # 验证 kubectl get nodes kubectl get pods -n kube-system逻辑说明kubeadm config images pull先单独拉镜像能把镜像问题提前暴露出来。--upload-certs把证书上传到 etcd方便后续加 master 节点。初始化成功后kubectl get nodes应该能看到 master 节点状态是NotReady因为还没装 CNI。kubectl get pods -n kube-system能看到 etcd 相关的 Pod 不存在因为是外部 etcd但 apiserver、controller-manager、scheduler、kube-proxy 应该都在跑。如果报the api server is not healthy after 4m0.00747357s按这个顺序排查先看journalctl -u kubelet -f有没有报错再crictl ps -a看 apiserver 容器状态然后crictl logs container-id看 apiserver 日志。常见原因是 etcd 连不上、证书问题、或者镜像架构不对。6. 避坑与排查ARM 麒麟 V10 部署 K8S 的 5 个血泪教训6.1 镜像架构不对导致 Pod 一直 ImagePullBackOff现象kubeadm init 之后kube-system 里的 Pod 一直ImagePullBackOffkubectl describe pod显示no match for platform in manifest。原因拉到的镜像是 amd64 架构的ARM 节点跑不了。K8S 1.26 的默认镜像仓库registry.k8s.io在国内可能被重定向到 x86 镜像或者阿里云镜像源里对应 tag 只有 amd64。解决用docker manifest inspect或crane manifest确认镜像支持的架构。如果只有 amd64换一个支持 multi-arch 的镜像源或者手动拉 ARM64 镜像后重新 tag。kubeadm 配置文件里的imageRepository要选确认有 ARM64 的源。6.2 cgroup 驱动不一致导致 kubelet 反复重启现象kubelet 启动后不断重启journalctl -u kubelet报failed to run Kubelet: misconfiguration: kubelet cgroup driver: cgroupfs is different from docker cgroup driver: systemd。原因containerd 的SystemdCgroup没改成true默认是false用的是 cgroupfs而 kubelet 配置里写的是 systemd。解决确认/etc/containerd/config.toml里SystemdCgroup true然后systemctl restart containerd。kubelet 的配置里cgroupDriver: systemd也要对应。两边必须一致改完重启 kubelet。6.3 etcd 证书或 endpoint 写错导致 API Server 起不来现象kubeadm init 卡在waiting for the kubelet to boot up the control plane as static Pods from directory /etc/kubernetes/manifests最后报the api server is not healthy after 4m0.00747357s。原因外部 etcd 的 endpoint 写错或者 etcd 需要 TLS 但配置里没给证书。麒麟 V10 上如果 etcd 是手动部署的很容易漏掉证书配置。解决先用etcdctl endpoint health确认 etcd 本身健康。如果 etcd 开了 TLSkubeadm 配置里要加etcd.external.caFile、certFile、keyFile。如果 etcd 没开 TLSendpoints 用http://而不是https://。改完配置重新 init 之前先kubeadm reset清理。6.4 麒麟 V10 自带 iptables 版本和 K8S 不兼容现象集群起来后 Service 不通kubectl get svc能看到 ClusterIP但 Pod 里 curl 不通。iptables -t nat -L看不到 KUBE-SERVICES 链。原因麒麟 V10 自带的 iptables 可能是 nftables 后端而 K8S 1.26 的 kube-proxy 默认用 iptables 模式两者不兼容。解决确认iptables --version如果显示nf_tables切换到 legacy 模式update-alternatives --set iptables /usr/sbin/iptables-legacy。或者把 kube-proxy 改成 ipvs 模式但 ARM 上 ipvs 需要额外加载模块。6.5 节点加入集群时 token 过期或证书 hash 不对现象kubeadm join报couldnt validate the identity of the API Server: abort connecting to API servers after timeout of 5m0s。原因token 过期了默认 24 小时或者--discovery-token-ca-cert-hash的值不对。解决在 master 上kubeadm token create --print-join-command重新生成 join 命令直接用输出的完整命令。如果还是不行检查 node 节点能不能通 master 的 6443 端口telnet 192.168.1.10 6443试一下。7. 集群 Ready 之后CNI 选型与节点加入的收尾技巧CNI 我一般用 CalicoARM64 支持好配置也成熟。但 Calico 的 manifest 默认用docker.io的镜像ARM 环境要确认镜像有 arm64 tag。装之前先把podSubnet和 Calico 的CALICO_IPV4POOL_CIDR对齐不然 Pod 拿不到 IP。# 安装 Calico确认镜像支持 arm64 kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml # 等待 Pod 起来 kubectl get pods -n kube-system -w # 确认 master 节点 Ready kubectl get nodes逻辑说明Calico 的 manifest 里有个 DaemonSet 会在每个节点跑一个calico-nodePod负责网络配置。kubectl get pods -n kube-system -w持续观察等所有 Pod 变成 Running。然后kubectl get nodes应该看到 master 状态从NotReady变成Ready。如果 Calico Pod 起不来大概率是镜像拉不到kubectl describe pod看事件。节点加入用 master 上生成的 join 命令注意 ARM 节点上 kubelet、containerd、runc、CNI 插件都要提前装好和 master 一样的步骤。join 成功后在 master 上kubectl get nodes能看到新节点。如果节点一直 NotReady先看 node 上systemctl status kubelet再看journalctl -u kubelet -f常见问题是 CNI 配置没同步或者证书没签下来。最后说一个我自己的习惯每次kubeadm init之前先kubeadm reset -f清理一遍把/etc/kubernetes、/var/lib/etcd如果是本地 etcd、/var/lib/kubelet都清掉避免残留配置干扰。这个习惯帮我省了很多次「明明配置对了但就是起不来」的玄学排查时间。希望帮到你。本文还有配套的精品资源点击获取