简介:本资源合集面向需要在国产化信创环境下搭建云原生平台的运维与开发人员,聚焦Kylin V10操作系统与ARM架构CPU的组合,通过containerd容器运行时部署Kubernetes 1.26.15一主一从集群。内容涵盖系统准备、容器运行时配置、K8S组件安装、主从节点配置、CNI网络插件选型及RBAC安全与持久化存储等关键环节,适合具备一定Linux与容器基础、希望掌握ARM64平台K8S落地实践的中高级读者。资源包共38个文件,约619.64MB,以gz压缩包、rpm安装包、sh脚本、yaml配置及service、conf等文件为主,分别对应镜像归档、依赖组件、自动化脚本与集群配置文件,便于按部署流程分步取用。目前已有264人学习下载。借助其中的配置脚本、二进制文件与自定义YAML,读者可快速复现ARM架构下的集群搭建过程,理解containerd与K8S的协作机制,并积累边缘计算与物联网场景下的排错与调优经验。
1. 麒麟 V10 + ARM 上跑 containerd 版 K8S 1.26.15:一主一从到底能不能落地
手里有一台麒麟 V10 的 ARM 服务器,想搭一套 Kubernetes 做验证或跑内部业务,结果第一步就被卡住——网上教程清一色 x86 + Docker,照着敲apt install docker.io直接报找不到包。这不是你环境的问题,是路线选错了。Kylin V10 基于 RPM 体系,ARM64 架构下 Docker 的官方源覆盖并不完整,而 containerd 作为 K8S 1.26 时代默认推荐的容器运行时,反而在 ARM 上更干净。这套方案要解决的就是:在麒麟 V10 ARM64 机器上,用 containerd 从零拉起一个一主一从的 K8S 1.26.15 集群,不依赖任何外网镜像加速的玄学操作,每一步都能复现。适合手里有国产化 ARM 服务器、需要做信创环境验证或内部小集群部署的运维和开发。下面按「环境准备 → containerd 落地 → 集群初始化 → 从节点加入 → 排错」的顺序讲透。
2. 环境底座:Kylin V10 ARM64 的前置检查与内核参数
2.1 先确认你的机器到底是不是 ARM64 且内核够用
很多人拿到机器直接开干,结果uname -m出来是aarch64却装错了 RPM 包。麒麟 V10 服务器版在 ARM 上通常返回aarch64,这是正常的 ARM64 标识。先做三件事:确认架构、确认内核版本、确认 SELinux 和防火墙状态。K8S 1.26.15 要求内核不低于 4.x,麒麟 V10 SP2/SP3 自带内核一般在 4.19 以上,基本够用。但如果你的内核低于 4.19,cgroup v2 和部分网络特性会缺,后面 kubelet 启动会报failed to get cgroup之类的错。
# 确认架构,aarch64 即 ARM64 uname -m # 确认内核版本,建议 4.19 以上 uname -r # 查看系统版本 cat /etc/kylin-release # 关闭 SELinux(K8S 默认要求) setenforce 0 sed -i 's/^SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config # 关闭 swap,kubelet 强制要求 swapoff -a sed -i '/swap/s/^/#/' /etc/fstab这几条命令的逻辑:setenforce 0是临时关闭,改配置文件是永久生效;swap 必须关,否则 kubelet 直接拒绝启动。参数上注意,/etc/fstab里注释 swap 行时用#开头,别把整行删了,方便回滚。
2.2 内核模块和 sysctl 参数:不设这些后面必翻车
containerd 和 K8S 的网络依赖overlay和br_netfilter两个内核模块。ARM 版麒麟默认可能没加载br_netfilter,导致 Pod 跨节点通信失败,现象是 Pod 能起但 ping 不通另一个节点上的 Pod。这一步是血泪经验,别跳过。
# 加载内核模块 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 --systemnet.ipv4.ip_forward = 1是 Pod 跨节点通信的基础,bridge-nf-call-iptables让 iptables 能处理网桥流量。执行完用sysctl net.ipv4.ip_forward验证返回值为 1。如果modprobe br_netfilter报模块不存在,说明内核裁剪过,需要换内核或找麒麟的 kernel-modules-extra 包补上。
2.3 时间同步和主机名:小细节决定证书能不能过
K8S 的证书和 token 对时间敏感,两台机器时间差超过几分钟,从节点加入时会报x509: certificate has expired。麒麟 V10 默认可能没开 chronyd。
# 启动时间同步 systemctl enable --now chronyd chronyc sources -v # 设置主机名,主从要区分 hostnamectl set-hostname k8s-master # 从节点执行:hostnamectl set-hostname k8s-node1 # 写入 hosts,方便互相解析 cat >> /etc/hosts <<EOF 192.168.1.10 k8s-master 192.168.1.11 k8s-node1 EOF主机名不要用下划线,K8S 的 DNS 规范只认字母数字和连字符。hosts 里的 IP 换成你实际的内网地址。做完这些,两台机器互相ping k8s-master和ping k8s-node1要能通,这是后面 kubeadm join 的前提。
3. containerd 在 ARM64 上的安装与配置:绕开 Docker 的坑
3.1 为什么 ARM 上优先选 containerd 而不是 Docker
K8S 1.24 之后移除了 dockershim,Docker 不再是默认运行时。在 ARM64 的麒麟 V10 上,Docker 的官方仓库对 aarch64 的支持时好时坏,而 containerd 的二进制包直接提供 ARM64 版本,解压即用,不依赖包管理器。另一个原因是 containerd 更轻,调用链短,出问题时ctr和crictl两个工具就能定位,不像 Docker 那样多一层。常见做法是直接从 containerd 官方 release 下载 ARM64 的 tar 包,手动铺到/usr/local。
3.2 下载与安装 containerd 的 ARM64 二进制
假设你已经把 containerd 的 ARM64 压缩包传到了机器上(内网环境用 scp,有外网的用 curl)。版本选 1.6.x 系列,和 K8S 1.26 匹配。
# 解压到 /usr/local tar -C /usr/local -xzf containerd-1.6.24-linux-arm64.tar.gz # 确认二进制存在 ls /usr/local/bin/containerd ls /usr/local/bin/ctr # 生成默认配置 mkdir -p /etc/containerd containerd config default > /etc/containerd/config.toml解压后/usr/local/bin下会有containerd、ctr、containerd-shim-runc-v2等。containerd config default生成的是完整默认配置,后面要改两个关键点。
3.3 改两个必调参数:SystemdCgroup 和 sandbox 镜像
默认配置里SystemdCgroup = false,而 K8S 1.26 的 kubelet 默认用 systemd 管理 cgroup,两边不一致会导致 Pod 启动后反复重启,日志里刷failed to create containerd task: cgroup。必须改成 true。另一个是 sandbox 镜像地址,默认指向registry.k8s.io,国内环境拉不动,需要换成你能访问的镜像仓库。
# 修改 SystemdCgroup sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml # 查看 sandbox 镜像配置行 grep -n "sandbox_image" /etc/containerd/config.tomlsandbox_image那一行默认是registry.k8s.io/pause:3.9,把它改成你内网仓库或可访问的地址,比如your-registry.local/pause:3.9。改完保存。
3.4 配置 containerd 的 systemd 服务并启动
containerd 官方包不带 systemd unit 文件,需要自己写一个。
cat > /etc/systemd/system/containerd.service <<EOF [Unit] Description=containerd container runtime After=network.target [Service] ExecStartPre=/sbin/modprobe overlay ExecStart=/usr/local/bin/containerd Restart=always RestartSec=5 Delegate=yes KillMode=process OOMScoreAdjust=-999 LimitNOFILE=1048576 LimitNPROC=infinity LimitCORE=infinity [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable --now containerd systemctl status containerdDelegate=yes让 containerd 能管理自己的 cgroup 子树,不加这个 kubelet 会报权限问题。LimitNOFILE调大是因为容器多了文件描述符不够。启动后用ctr version验证,能打印出 Server 和 Client 版本就说明 containerd 活了。
3.5 装 crictl 并指向 containerd 的 socket
kubelet 通过 CRI 接口和 containerd 通信,调试时用crictl比ctr更贴近 K8S 视角。
# 下载 crictl 的 ARM64 版本,解压到 /usr/local/bin tar -C /usr/local/bin -xzf crictl-v1.26.0-linux-arm64.tar.gz # 配置 crictl 指向 containerd 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 infocrictl info能返回 JSON 格式的运行时信息就对了。如果报连接拒绝,检查 containerd 是否真的在跑,以及 socket 路径是不是/run/containerd/containerd.sock。有些发行版会放到/var/run,那是/run的软链,不影响。
4. kubeadm 初始化主节点:K8S 1.26.15 的 ARM64 落地
4.1 安装 kubelet、kubeadm、kubectl 三个 RPM 包
麒麟 V10 是 RPM 体系,K8S 官方提供 aarch64 的 RPM 包。如果你有内网 yum 源,直接yum install;没有的话下载 rpm 包用rpm -ivh本地装。三个包版本必须一致,都是 1.26.15。
# 本地安装示例,包名按实际下载的来 rpm -ivh kubelet-1.26.15-0.aarch64.rpm rpm -ivh kubeadm-1.26.15-0.aarch64.rpm rpm -ivh kubectl-1.26.15-0.aarch64.rpm # 设置 kubelet 开机自启(先别启动,等 kubeadm init) systemctl enable kubelet # 验证版本 kubeadm version kubectl version --client装完 kubelet 后它不会自动跑起来,这是正常的,kubeadm init 会生成配置再启动它。如果rpm -ivh报依赖缺失,通常是conntrack、socat、ebtables这几个,用 yum 补上。
4.2 准备 kubeadm 配置文件:把镜像地址和网段一次写对
直接kubeadm init用默认参数会去拉registry.k8s.io的镜像,ARM64 环境下大概率超时。正确做法是写一个配置文件,指定imageRepository指向你的内网仓库或可访问的镜像源,同时定好 Pod 网段。
# 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 imagePullPolicy: IfNotPresent --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.26.15 imageRepository: your-registry.local/k8s networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12advertiseAddress换成你主节点的内网 IP,criSocket必须指向 containerd 的 socket,写错会报failed to get runtime。imageRepository是你实际能拉镜像的地址,podSubnet用 Flannel 的话默认 10.244.0.0/16。
4.3 预拉镜像和正式 init
配置文件写好先预拉镜像,这一步能把网络问题提前暴露。
# 预拉镜像 kubeadm config images pull --config kubeadm-config.yaml # 正式初始化 kubeadm init --config kubeadm-config.yaml --upload-certs--upload-certs会把证书加密上传,方便后面加主节点(虽然一主一从用不上,但留着不碍事)。init 成功后终端会打印一段kubeadm join命令,里面有 token 和 hash,复制下来,从节点要用。如果卡在[wait-control-plane]超过两分钟,多半是镜像没拉全或 containerd 配置有问题,用crictl images看镜像列表。
4.4 配置 kubectl 并验证主节点状态
init 完成后按提示配置 kubectl。
mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/config # 查看节点,此时是 NotReady,因为还没装网络插件 kubectl get nodes # 查看系统 Pod kubectl get pods -n kube-system节点显示NotReady是正常的,CNI 没装之前都这样。kube-system下的kube-apiserver、etcd、kube-scheduler、kube-controller-manager应该是 Running,coredns会 Pending,等网络插件装好就起来。
4.5 装 Flannel 网络插件:ARM64 镜像要选对
Flannel 的默认 manifest 里镜像有 amd64 和 arm64 多架构,但如果你内网仓库只同步了 amd64,ARM 节点会拉失败报exec format error。确认你的仓库里 flannel 镜像是 arm64 的。
# 应用 Flannel kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml # 如果内网,先下载 yml 改镜像地址再 apply # kubectl apply -f kube-flannel-custom.yml # 观察 Pod 起来 kubectl get pods -n kube-flannel -wFlannel 的 DaemonSet 在每个节点跑一个kube-flannelPod,起来后主节点变 Ready。如果 Pod 一直CrashLoopBackOff,看日志kubectl logs -n kube-flannel <pod>,常见是podSubnet和 Flannel 配置里的网段不一致。
5. 从节点加入与集群验证:一主一从的收尾动作
5.1 从节点重复前置步骤再执行 join
从节点不需要再跑kubeadm init,但第 2 章的内核参数、第 3 章的 containerd 安装配置必须一模一样做一遍。containerd 的 socket 路径、SystemdCgroup 配置、crictl 配置都要对齐。做完后在从节点执行主节点 init 时打印的 join 命令。
# 在主节点重新生成 token(如果原来的过期了) kubeadm token create --print-join-command # 在从节点执行,形如: kubeadm join 192.168.1.10:6443 --token abcdef.1234567890abcdef \ --discovery-token-ca-cert-hash sha256:xxxxxxxxtoken 默认 24 小时过期,过期了用kubeadm token create重新生成。join 过程中如果报connection refused,检查主节点 6443 端口是否监听、防火墙是否放行。
5.2 验证两节点 Ready 和跨节点 Pod 通信
回到主节点看节点列表。
kubectl get nodes -o wide两个节点都应该是Ready,INTERNAL-IP显示各自内网地址。然后跑一个跨节点通信测试,这是验证 CNI 是否真正工作的关键。
# 在主节点跑一个 nginx kubectl run nginx --image=nginx --port=80 # 查看它调度到哪个节点 kubectl get pod nginx -o wide # 在另一个节点上用 crictl 进 Pod 网络测试,或直接创建测试 Pod kubectl run test --image=busybox --rm -it --restart=Never -- wget -qO- nginx如果wget能返回 nginx 欢迎页,说明 Pod 跨节点网络通了。如果超时,回到第 2 章检查br_netfilter和ip_forward,这是最常见的两个原因。
5.3 用 kubectl 做一次完整的调度验证
光看节点 Ready 不够,要确认调度器真的能把 Pod 分配到从节点。
# 创建一个带节点亲和性的 Deployment,强制调度到从节点 cat > test-schedule.yaml <<EOF apiVersion: apps/v1 kind: Deployment metadata: name: schedule-test spec: replicas: 2 selector: matchLabels: app: schedule-test template: metadata: labels: app: schedule-test spec: containers: - name: pause image: your-registry.local/pause:3.9 EOF kubectl apply -f test-schedule.yaml kubectl get pods -o wide -l app=schedule-test两个副本应该分别落在两个节点上(或者至少有一个在从节点)。如果全挤在主节点,检查从节点是否有污点kubectl describe node k8s-node1 | grep Taints,新加入的节点默认没有污点,能正常调度。
6. 避坑与排查:ARM + 麒麟环境下最容易翻车的五个点
6.1 镜像 exec format error:拉到了 amd64 镜像
现象:Pod 一直CrashLoopBackOff,kubectl describe pod里事件显示exec format error或no match for platform in manifest。原因是你用的镜像仓库里只有 amd64 版本,ARM 节点拉下来跑不了。解决:确认镜像的 manifest 支持 arm64,用docker manifest inspect或crictl inspecti看架构;内网仓库要同步多架构镜像,或者单独打 arm64 tag。
6.2 kubelet 启动报 failed to get cgroup
现象:systemctl status kubelet显示failed to get cgroup或cgroup driver mismatch。原因是 containerd 的SystemdCgroup还是 false,和 kubelet 的 systemd 驱动不一致。解决:回到/etc/containerd/config.toml把SystemdCgroup = true改对,systemctl restart containerd后重启 kubelet。这个坑在第 3 章已经提前埋了伏笔,但实际做的时候还是有人漏。
6.3 从节点 join 报 x509 证书错误
现象:kubeadm join输出x509: certificate has expired or is not yet valid。原因是两台机器时间不同步,或者 join 用的 token 过期。解决:先chronyc sources确认时间同步,再date对比两台机器;token 过期就回主节点kubeadm token create --print-join-command重新生成。时间差超过证书有效期(默认一年)的情况极少,但几分钟的偏差就足以让 TLS 握手失败。
6.4 Flannel Pod 起不来,日志报 subnet 冲突
现象:kube-flannel的 Pod 反复重启,日志里failed to acquire lease或subnet 10.244.x.x/24 overlaps。原因是 kubeadm 配置的podSubnet和 Flannel 的Network字段不一致,或者两个节点分配到了同一个子网。解决:确认kubeadm-config.yaml里podSubnet: 10.244.0.0/16,Flannel 的 ConfigMap 里Network也是10.244.0.0/16,两者必须完全一致。
6.5 crictl 连不上 containerd
现象:crictl info报connection refused或no such file or directory。原因是 crictl 配置的 endpoint 和 containerd 实际监听的 socket 不一致。解决:systemctl status containerd看它监听的路径,默认是/run/containerd/containerd.sock;检查/etc/crictl.yaml里的runtime-endpoint是否写对。如果 containerd 根本没起来,看journalctl -u containerd的报错,常见是配置文件语法错误。
7. 把一主一从跑稳之后:几个能省时间的进阶习惯
集群跑起来只是开始,真正省时间的是后面这些习惯。第一个是给 kubelet 配--node-labels和污点管理,一主一从的环境里主节点默认有node-role.kubernetes.io/control-plane:NoSchedule污点,如果你想让主节点也跑业务 Pod,得手动去掉,但生产环境不建议。第二个是定期kubeadm certs check-expiration看证书到期时间,K8S 的证书默认一年,到期后 apiserver 直接起不来,这个后悔药不好吃。第三个是把kubeadm-config.yaml、containerd 的config.toml、crictl 配置这三个文件纳入版本管理,下次扩容或重建直接复用,不用再回忆参数。
验证集群是否真的稳,我一般会做三件事:连续创建删除 20 个 Pod 看调度是否正常、重启一次 containerd 看 Pod 是否自动恢复、把从节点关机再开机看它能否重新加入。这三步过了,这套一主一从才算能交付。ARM 环境下的 K8S 没有 x86 那么多现成脚本,但只要 containerd 的 cgroup 驱动、内核模块、镜像架构这三样对齐,剩下的和 x86 没本质区别。我自己踩得最狠的一次是镜像架构没确认,排查了两个小时才发现是 amd64 的包在 ARM 上跑不起来,希望帮到你。
本文还有配套的精品资源,点击获取