Kylin V10 ARM64 部署 K8S 1.26.15 集群:外部 etcd 与 containerd 实战
简介这份资源合集面向需要在国产化信创环境中落地容器编排的运维与云原生工程师聚焦Kylin V10操作系统搭配ARM架构服务器、采用外部etcd与containerd运行时部署Kubernetes 1.26.15一主多从集群的完整场景。包内共41个文件以gz离线镜像包、rpm系统依赖、sh加载脚本、yaml与yml编排清单为主另含kubeadm、kubectl、kubelet等二进制组件及kubelet.service、10-kubeadm.conf等配置项压缩包约645.74MB覆盖从运行时、网络插件到控制面组件的离线安装素材。资源同时提供etcd、Calico、CoreDNS等关键组件的镜像与部署文件便于在无外网环境下完成集群初始化与节点加入。目前已有130人学习下载适合希望掌握ARM平台K8S部署流程、排查离线安装依赖问题的技术人员参考实践。1. 麒麟V10 ARM集群落地为什么这套组合值得你花一个下午如果你手里正好有几台飞腾、鲲鹏或者倚天芯片的服务器系统装的是银河麒麟V10 SP2/SP3 ARM64现在要把它们组成一个能跑生产业务的 Kubernetes 集群那你大概率已经发现网上绝大多数 K8S 部署教程都是 x86 CentOS 的照着敲到一半就会卡在镜像架构不匹配、etcd 起不来、containerd 配置对不上这些地方。这套方案要解决的就是这个问题——在 Kylin V10 ARM64 上用 containerd 作为容器运行时把 etcd 独立部署在集群外部搭一套一主多从的 K8S 1.26.15 集群。为什么是 1.26.15 这个版本因为它是 1.26 系列里比较靠后的补丁版本修了不少 CVE同时 containerd 1.6.x 和它配合最稳。为什么 etcd 要外置因为 ARM 服务器通常内存和磁盘 IO 都比较金贵把 etcd 和 master 节点混部etcd 的 WAL 刷盘会和 kube-apiserver 抢 IO集群一忙就出玄学问题。外置 etcd 之后master 节点只跑控制面组件etcd 单独用 SSD 或者高性能云盘扛稳定性提升非常明显。这套方案适合谁适合那些已经在 ARM 环境里踩过坑、不想再被镜像架构问题反复折磨的运维和平台工程师。如果你还没装过 Kylin V10建议先把系统装好、网络调通再往下看。下面从环境准备开始一步步把集群拉起来。2. Kylin V10 ARM64 环境准备内核参数、时间同步与 containerd 安装2.1 系统基线检查与内核模块加载在动手装任何组件之前先把所有节点的系统基线对齐。Kylin V10 默认内核是 4.19 系列对 K8S 1.26 来说够用但有几个模块必须确认已加载。登录每台机器执行# 确认系统架构和内核版本 uname -m uname -r # 输出应为 aarch64 和 4.19.x # 检查 br_netfilter 和 overlay 模块 lsmod | grep -E br_netfilter|overlay # 如果没有输出手动加载 modprobe br_netfilter modprobe overlay # 写入开机自动加载 cat /etc/modules-load.d/k8s.conf EOF br_netfilter overlay EOFbr_netfilter是让 iptables 能过滤桥接流量的关键模块K8S 的 Service 和 NetworkPolicy 都依赖它。overlay是 containerd 默认的存储驱动不加载的话容器起不来。这两条如果漏了后面 kube-proxy 会报错Pod 网络不通排查起来很费时间。接下来配置内核参数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 net.ipv4.tcp_tw_reuse 1 vm.swappiness 0 EOF sysctl --systemnet.ipv4.ip_forward 1是必须的否则跨节点 Pod 通信直接断。vm.swappiness 0是为了让 K8S 的 kubelet 不因为 swap 触发驱逐虽然 1.26 已经支持 swap 了但生产环境还是建议关掉避免内存压力下出现不可预期的调度行为。2.2 时间同步与主机名解析ARM 服务器如果时间不同步etcd 集群选主会出问题kube-apiserver 的证书校验也会失败。Kylin V10 默认可能没开 chronyd手动确认# 所有节点执行 systemctl enable --now chronyd chronyc sources -v # 确认有一个 ^* 标记的同步源 # 设置主机名按角色区分 hostnamectl set-hostname k8s-master01 # 从节点依次设为 k8s-node01、k8s-node02... # 所有节点写入 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主机名不要用下划线K8S 的 DNS 解析对主机名格式有要求。etcd 节点单独列出来因为后面证书签发要用到。2.3 安装 containerd 并配置 ARM64 镜像加速Kylin V10 的软件源里可能有 containerd但版本通常偏旧。建议从 containerd 官方 release 下载 ARM64 二进制包。截至 1.26.15 配套的 containerd 版本1.6.31 是比较稳的选择# 下载 ARM64 二进制包 wget https://github.com/containerd/containerd/releases/download/v1.6.31/containerd-1.6.31-linux-arm64.tar.gz # 解压到 /usr/local tar Cxzvf /usr/local containerd-1.6.31-linux-arm64.tar.gz # 下载 systemd 单元文件 wget https://raw.githubusercontent.com/containerd/containerd/main/containerd.service -O /etc/systemd/system/containerd.service systemctl daemon-reload systemctl enable --now containerd systemctl status containerd如果服务器不能直连外网就在能上网的机器上下好用 scp 传过去。ARM64 的包和 x86 的不通用别下错了。containerd 默认配置需要生成并调整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 systemctl restart containerdSystemdCgroup true必须改否则 kubelet 和 containerd 的 cgroup 驱动不一致Pod 会反复重启。sandbox_image换成国内能拉到的 pause 镜像ARM64 架构的 pause 镜像在阿里云仓库里有。注意Kylin V10 自带的 iptables 可能是 nft 后端containerd 和 kube-proxy 在 nft 模式下有兼容性问题。执行iptables -V确认如果是 nft切换成 legacyupdate-alternatives --set iptables /usr/sbin/iptables-legacy。3. 外部 etcd 集群部署证书签发、静态 Pod 与 ARM64 二进制选型3.1 etcd 证书体系与 cfssl 签发流程外部 etcd 意味着它不跑在 K8S 集群里而是独立进程。etcd 之间用 TLS 通信客户端kube-apiserver也要用证书认证。先在一台机器上生成 CA 和证书。安装 cfsslwget https://github.com/cloudflare/cfssl/releases/download/v1.6.4/cfssl_1.6.4_linux_arm64 wget https://github.com/cloudflare/cfssl/releases/download/v1.6.4/cfssljson_1.6.4_linux_arm64 chmod x cfssl_1.6.4_linux_arm64 cfssljson_1.6.4_linux_arm64 mv cfssl_1.6.4_linux_arm64 /usr/local/bin/cfssl mv cfssljson_1.6.4_linux_arm64 /usr/local/bin/cfssljson生成 CA 配置{ signing: { default: { expiry: 87600h }, profiles: { etcd: { expiry: 87600h, usages: [signing, key encipherment, server auth, client auth] } } } }保存为ca-config.json然后生成 CA 证书cfssl gencert -initca ca-csr.json | cfssljson -bare caca-csr.json里 CN 写etcd-caO 写etcd。生成的ca.pem和ca-key.pem后面所有节点都要用。接着签发 etcd 服务端证书。etcd-csr.json的 hosts 字段要包含所有 etcd 节点 IP 和 127.0.0.1{ CN: etcd, hosts: [ 127.0.0.1, 192.168.1.20, 192.168.1.21, 192.168.1.22 ], key: { algo: rsa, size: 2048 }, names: [ { C: CN, L: Beijing, O: etcd, OU: etcd } ] }执行cfssl gencert -caca.pem -ca-keyca-key.pem -configca-config.json -profileetcd etcd-csr.json | cfssljson -bare etcd得到etcd.pem和etcd-key.pem。把ca.pem、etcd.pem、etcd-key.pem分发到所有 etcd 节点的/etc/etcd/ssl/目录。3.2 etcd 二进制部署与 systemd 管理ARM64 的 etcd 二进制从官方 release 下载wget https://github.com/etcd-io/etcd/releases/download/v3.5.15/etcd-v3.5.15-linux-arm64.tar.gz tar xzvf etcd-v3.5.15-linux-arm64.tar.gz cp etcd-v3.5.15-linux-arm64/etcd /usr/local/bin/ cp etcd-v3.5.15-linux-arm64/etcdctl /usr/local/bin/创建 systemd 单元文件/etc/systemd/system/etcd.service[Unit] Descriptionetcd Afternetwork.target [Service] Typenotify ExecStart/usr/local/bin/etcd \ --name etcd01 \ --data-dir /var/lib/etcd \ --listen-client-urls https://192.168.1.20:2379 \ --advertise-client-urls https://192.168.1.20:2379 \ --listen-peer-urls https://192.168.1.20:2380 \ --initial-advertise-peer-urls https://192.168.1.20:2380 \ --initial-cluster etcd01https://192.168.1.20:2380,etcd02https://192.168.1.21:2380,etcd03https://192.168.1.22:2380 \ --initial-cluster-token etcd-cluster \ --initial-cluster-state new \ --cert-file/etc/etcd/ssl/etcd.pem \ --key-file/etc/etcd/ssl/etcd-key.pem \ --peer-cert-file/etc/etcd/ssl/etcd.pem \ --peer-key-file/etc/etcd/ssl/etcd-key.pem \ --trusted-ca-file/etc/etcd/ssl/ca.pem \ --peer-trusted-ca-file/etc/etcd/ssl/ca.pem Restartalways RestartSec5 [Install] WantedBymulti-user.target每个节点的--name、IP 相关参数按实际改。--initial-cluster-state new只在第一次启动时用后续重启改成existing否则会尝试重新加入集群导致数据混乱。启动并验证systemctl daemon-reload systemctl enable --now etcd systemctl status etcd # 验证集群健康 ETCDCTL_API3 etcdctl \ --endpointshttps://192.168.1.20:2379 \ --cacert/etc/etcd/ssl/ca.pem \ --cert/etc/etcd/ssl/etcd.pem \ --key/etc/etcd/ssl/etcd-key.pem \ endpoint health三个节点都返回healthy才算成功。如果某个节点报context deadline exceeded先检查防火墙 2379/2380 端口再检查证书里的 IP 是否匹配。3.3 etcd 性能调优与 ARM 平台注意事项ARM 服务器的磁盘 IO 和 x86 有差异etcd 的--heartbeat-interval和--election-timeout可以适当放宽。默认 100ms/1000ms 在 ARM 上偶尔会触发不必要的选主改成 250ms/2500ms 更稳--heartbeat-interval250 --election-timeout2500另外etcd 的--quota-backend-bytes默认 2GB生产环境建议调到 8GB避免频繁 compaction--quota-backend-bytes8589934592 --auto-compaction-retention1--auto-compaction-retention1表示保留 1 小时的历史版本配合定时 defrag 可以控制 db 文件大小。ARM 平台的内存带宽通常比同代 x86 低etcd 的--max-request-bytes保持默认 1.5MB 即可调大反而增加 OOM 风险。4. K8S 1.26.15 控制面与工作节点部署kubeadm 配置与 ARM64 镜像仓库4.1 kubeadm、kubelet、kubectl 安装与版本锁定Kylin V10 的 yum 源里可能没有 K8S 1.26.15 的包需要添加 Kubernetes 官方源。但 ARM64 的源地址和 x86 不同cat /etc/yum.repos.d/kubernetes.repo EOF [kubernetes] nameKubernetes baseurlhttps://pkgs.k8s.io/core:/stable:/v1.26/rpm/ enabled1 gpgcheck1 gpgkeyhttps://pkgs.k8s.io/core:/stable:/v1.26/rpm/repodata/repomd.xml.key excludekubelet kubeadm kubectl cri-tools kubernetes-cni EOF yum install -y kubelet-1.26.15 kubeadm-1.26.15 kubectl-1.26.15 --disableexcludeskubernetes systemctl enable kubelet--disableexcludeskubernetes是为了绕过 exclude 限制否则 yum 会找不到包。安装完先别启动 kubelet等 kubeadm init 之后再启。确认版本kubeadm version kubelet --version三个组件版本必须一致否则 kubeadm init 会报版本偏差警告。4.2 kubeadm 配置文件与外部 etcd 对接kubeadm 支持通过配置文件指定外部 etcd。创建kubeadm-config.yamlapiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.26.15 controlPlaneEndpoint: 192.168.1.10:6443 etcd: external: endpoints: - https://192.168.1.20:2379 - https://192.168.1.21:2379 - https://192.168.1.22:2379 caFile: /etc/kubernetes/pki/etcd/ca.crt certFile: /etc/kubernetes/pki/etcd/client.crt keyFile: /etc/kubernetes/pki/etcd/client.key networking: serviceSubnet: 10.96.0.0/12 podSubnet: 10.244.0.0/16 dnsDomain: cluster.local --- apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration cgroupDriver: systemd --- apiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration mode: ipvscontrolPlaneEndpoint如果只有一台 master就写 master 的 IP。etcd.external里的证书路径是 kube-apiserver 访问 etcd 用的客户端证书需要提前把 etcd 的 CA 和客户端证书放到 master 节点的/etc/kubernetes/pki/etcd/下。生成客户端证书# 在 etcd 证书目录下执行 cfssl gencert -caca.pem -ca-keyca-key.pem -configca-config.json -profileetcd etcd-client-csr.json | cfssljson -bare clientetcd-client-csr.json的 CN 写kube-apiserver-etcd-clientO 写etcd。生成的client.pem和client-key.pem重命名为client.crt和client.key连同ca.pem一起放到 master 节点的/etc/kubernetes/pki/etcd/。4.3 初始化 master 节点与网络插件选型执行初始化kubeadm init --configkubeadm-config.yaml --upload-certs --v5--upload-certs会把证书加密上传到 kubeadm-certs Secret方便后续加 master 节点。--v5输出详细日志卡住的时候能看出在哪一步。初始化成功后按提示配置 kubectlmkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config然后安装网络插件。ARM64 环境推荐 Calico 或者 Flannel。Flannel 更轻量ARM64 镜像齐全kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml如果拉不到镜像把 yml 里的image字段换成registry.aliyuncs.com/google_containers/flannel:v0.24.0之类的国内地址。ARM64 的 flannel 镜像在阿里云仓库有。检查节点状态kubectl get nodes kubectl get pods -n kube-flannelmaster 节点默认有污点不会被调度业务 Pod。如果只是测试可以去掉kubectl taint nodes k8s-master01 node-role.kubernetes.io/control-plane:NoSchedule-生产环境别去控制面节点只跑系统组件。4.4 工作节点加入集群与 containerd 配置对齐工作节点上同样要装 containerd、kubelet、kubeadm版本和 master 一致。然后执行 master 初始化时输出的kubeadm join命令kubeadm join 192.168.1.10:6443 --token abcdef.1234567890abcdef \ --discovery-token-ca-cert-hash sha256:xxxxx如果 token 过期了在 master 上重新生成kubeadm token create --print-join-command加入后回到 master 查看kubectl get nodes -o wide所有节点状态为Ready才算成功。如果某个节点一直是NotReady先看 kubelet 日志journalctl -u kubelet -f常见原因是 containerd 的SystemdCgroup没开或者 pause 镜像拉不下来。ARM64 的 pause 镜像一定要确认是arm64架构的用docker manifest inspect或者crictl inspecti看。注意Kylin V10 的 SELinux 默认可能是 enforcingkubelet 挂载卷时会报 permission denied。执行setenforce 0临时关闭并在/etc/selinux/config里改成permissive。生产环境如果必须开 SELinux需要给 kubelet 和 containerd 单独写策略比较麻烦建议先关。5. 避坑与排查ARM64 Kylin V10 部署 K8S 最常见的 5 个翻车点5.1 镜像架构不匹配导致 Pod 一直 ImagePullBackOff现象kubectl describe pod 看到Failed to pull image或者no match for platform in manifest。原因很多官方镜像默认只推 amd64ARM64 的 tag 可能不存在或者 manifest 里没有 arm64 条目。Kylin V10 的容器运行时是 containerd它不会自动回退到 amd64 模拟运行。解决用crictl inspecti或者docker manifest inspect确认镜像有没有 arm64。没有的话找替代镜像比如registry.aliyuncs.com/google_containers/下的 ARM64 版本或者自己用 buildx 构建。K8S 核心组件kube-apiserver、kube-controller-manager、kube-scheduler、kube-proxy、pause、etcd的 ARM64 镜像在官方 registry 都有但网络插件、Ingress Controller、监控组件不一定全。5.2 etcd 启动报tls: bad certificate或context deadline exceeded现象etcd 服务起不来日志里反复报证书错误或者客户端连不上。原因证书里的 hosts 字段没有包含实际 IP或者 CN 写错了。etcd 对证书的 SAN 校验很严格IP 和 DNS 必须完全匹配。解决用openssl x509 -in etcd.pem -text -noout查看 SAN 列表确认所有节点 IP 都在。缺的话重新签发证书然后滚动重启 etcd。另外--initial-cluster-state第一次是new后续重启必须改成existing否则会尝试重新组集群。5.3 kubelet 反复重启日志报failed to run Kubelet: misconfiguration: kubelet cgroup driver: cgroupfs is different from docker cgroup driver: systemd现象kubelet 起不来节点 NotReady。原因containerd 的SystemdCgroup没开或者 kubeadm 配置文件里cgroupDriver没写 systemd。解决确认/etc/containerd/config.toml里SystemdCgroup true然后systemctl restart containerd。kubeadm 配置文件里KubeletConfiguration的cgroupDriver: systemd也要加上。两边必须一致。5.4 Flannel 或 Calico Pod 一直 CrashLoopBackOff报failed to set bridge addr: could not add ip addr to bridge现象网络插件 Pod 起不来节点间 Pod 不通。原因Kylin V10 的 iptables 是 nft 后端和 flannel 的 bridge 操作不兼容。或者br_netfilter模块没加载。解决iptables -V确认后端如果是 nft切换到 legacyupdate-alternatives --set iptables /usr/sbin/iptables-legacy然后重启 kube-proxy 和网络插件。同时确认lsmod | grep br_netfilter有输出。5.5 加入节点时卡在[preflight] Running pre-flight checks不动现象kubeadm join 执行后长时间无输出最后超时。原因工作节点到 master 的 6443 端口不通或者时间不同步导致 token 校验失败。解决telnet 192.168.1.10 6443确认端口通。chronyc sources确认时间同步。如果都正常看 master 上 kube-apiserver 的日志可能是证书 SAN 里没有 master 的 IP。重新签发 apiserver 证书或者用--discovery-token-unsafe-skip-ca-verification临时跳过不推荐生产用。6. 集群验证与日常运维用 kubeadm 证书续签和 etcd 备份兜底集群拉起来只是开始真正让这套 ARM64 环境跑得久靠的是两件事证书别过期etcd 有备份。K8S 1.26 的证书默认有效期是 1 年kubeadm 部署的集群可以用kubeadm certs check-expiration查看剩余时间。快到期时执行kubeadm certs renew all systemctl restart kubeletrenew all会重新签发所有控制面证书包括 apiserver、controller-manager、scheduler、etcd 客户端等。执行完必须重启 kubelet 和静态 Pod否则旧证书还在内存里。ARM 平台上的静态 Pod 重启比 x86 慢一些耐心等 30 秒再kubectl get nodes。etcd 备份更关键。外部 etcd 的好处是备份不影响集群运行ETCDCTL_API3 etcdctl \ --endpointshttps://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 snapshot status /backup/etcd-20250101-1200.db --write-outtable备份文件建议放到独立的 NFS 或者对象存储别和 etcd 数据目录放同一块盘。恢复的时候用etcdctl snapshot restore指定新的 style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;" />