ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

麒麟V10 ARM离线部署K8s 1.26.15多主多从实战

麒麟V10 ARM离线部署K8s 1.26.15多主多从实战 简介面向国产化麒麟操作系统与ARM架构CPU以containerd作为运行时部署Kubernetes 1.26.15多主多从集群的完整资源包。压缩包共四十七个文件、约六百二十二兆涵盖十三个离线镜像包含Calico、CoreDNS、Pause等、十一个系统依赖、五个操作脚本、四个配置文件、三个编排清单以及服务管理、高可用负载均衡、集群初始化等核心组件适合在无外网环境下直接安装使用。已有二百五十四人学习下载。资源面向需要在国产化系统上落地K8S的运维开发人员尤其适合在非x86平台实践容器编排的读者。内含高可用keepalived配置、主节点初始化与工作节点加入集群的yml模板、镜像批量加载脚本、Calico网络插件声明式清单等覆盖从环境准备、containerd配置、etcd部署到验证集群状态的完整链路并附带kubelet服务配置与kubeadm初始化参数文件能够帮助读者快速搭建高可用集群规避ARM平台镜像兼容与组件版本不匹配等常见问题。整体目录清晰便于按阶段对照实践。1. 麒麟V10 ARM上部署K8S 1.26.15多主多从从选对运行时开始在麒麟Kylin V10 ARM架构服务器上部署Kubernetes 1.26.15多主多从集群最麻烦的往往不是kubeadm本身而是离线环境下的运行时选型和你手上有没有匹配架构的镜像。这套资源合集把从containerd 1.7.2安装、系统依赖rpm、K8s 1.26.15全套控制面镜像到Calico v3.26.4、keepalived加kube-lb高可用入口都按离线交付的方式打包好了。它对应的是至少三台master加若干worker的真实生产布局不是单节点demo。适合正在做信创迁移、内网隔离环境交付的运维以及想在ARM非x86硬件上验证K8s高可用的云原生工程师。我拆这套包时踩了不少ARM架构特有的坑下面按实际部署顺序讲透。2. 离线资源包盘点镜像包、rpm依赖、配置文件各管哪一段第一次打开这个资源包如果直接解压最直观的感受是“什么都有但不知道先干嘛”。我把包里的物料拆成三类一是用于初始化集群的kubeadm配置文件first-master、join-master、join-node三个yaml二是容器镜像tar包k8s-v1.26.15.tar.gz加上calico镜像tar和pause-3.9.tar.gz三是离线的系统依赖pkgs目录的rpm和libseccomp。建议先把这三类分开因为部署顺序就是“装依赖→导镜像→跑kubeadm”顺序反了后面排查时你会分不清是镜像问题还是依赖问题。2.1 先摸清三类物料文件/目录身份在部署中的作用k8s-v1.26.15.tar.gz控制面组件镜像包包含kube-apiserver、kube-controller-manager、kube-scheduler、kube-proxy、etcd 3.5.10-0、coredns v1.9.3、pause 3.9calico-cni-v3.26.4.tar.gz 等网络插件镜像提供CNI、Pod网络与NetworkPolicypkgs目录rpm依赖ipvsadm、conntrack、ebtables、socat、ipset、libseccomp等kube-proxy ipvs模式与containerd的前置kubeadm-first-master.yml集群引导配置第一个master初始化kubeadm-join-master.yml控制面扩容配置后续master接入kubeadm-join-node.yml工作节点接入配置worker节点接入kube-lb.conf / kube-lb.service高可用入口提供VIP:6443的四层转发keepalived master/backupVIP漂移机制让VIP跟着健康的主节点走get_images.sh / load_images.sh镜像打包/导入脚本离线镜像备份与还原对照k8s-v1.26.15.tar.gz内的镜像文件名v1.26.15配套的etcd是3.5.10-0coredns是v1.9.3pause是3.9这几个tag和kubeadm默认期望是对得上的。如果你从网上的x86教程直接找deb/rpm装kubelet版本一旦差一个小版本kubeadm init时就会出现组件版本不一致或者连不上apiserver的问题。离线环境里版本锁死反而是优势少了很多“顺便升级”的冲动。2.2 get_images.sh 与 load_images.sh镜像怎么打包、怎么还原资源包里的get_images.sh作用是在一台能联网的机器上把镜像打成tarload_images.sh则是在内网节点上批量导入。两个脚本的实际内容大致如下。#!/bin/bash # get_images.sh 在联网机器上拉取并导出镜像 IMAGES( registry.k8s.io/kube-apiserver:v1.26.15 registry.k8s.io/kube-controller-manager:v1.26.15 registry.k8s.io/kube-scheduler:v1.26.15 registry.k8s.io/kube-proxy:v1.26.15 registry.k8s.io/etcd:3.5.10-0 registry.k8s.io/coredns:v1.9.3 registry.k8s.io/pause:3.9 ) for img in ${IMAGES[]}; do ctr -n k8s.io images pull $img done ctr -n k8s.io images export k8s-v1.26.15.tar.gz ${IMAGES[]}这里的-n k8s.io是指定containerd的命名空间。containerd默认命名空间是default而kubelet通过CRI创建容器时用的是k8s.io命名空间。如果拉镜像时没带-n k8s.io后面导入的镜像会落在default里kubelet照样看不到这是非常隐蔽的坑。IMAGES数组里的tag必须和资源包文件名一致不要手滑改成latest离线环境一旦不一致crictl images里看到的列表会让你怀疑人生。#!/bin/bash # load_images.sh 内网节点批量导入离线镜像 for archive in k8s-v1.26.15.tar.gz calico-cni-v3.26.4.tar.gz pause-3.9.tar.gz; do ctr -n k8s.io images import $archive done导入逻辑很简单循环遍历tar包并执行ctr import。需要留意的是import是整体导入重复导入会提示already exists不影响后续使用。导入完成后用ctr -n k8s.io images list看一眼确认每个镜像的repo:tag都出现在列表里。不要用docker load这套环境根本没有docker用了反而会误导自己。2.3 系统依赖先装rpm再动kubeletpkgs目录里的rpm覆盖了以下几类sysstat用于性能排查ipvsadm用于kube-proxy的ipvs模式conntrack和ebtables负责流量追踪相关的内核交互socat是kubeadm做端口连通性检查时依赖的命令ipset是IP集合管理libseccomp则是containerd运行容器时做系统调用过滤的底层库。Kylin V10的base源如果在内网根本连不上直接用本地rpm装是最稳的。# 在每台节点上提前装好假设pkgs与当前目录同级 cd pkgs yum localinstall -y ./*.rpmlocalinstall的意义在于只解析本地目录的rpm依赖不会去访问外网仓库。装完依赖后还要确认内核模块和系统参数。常见做法是检查overlay与br_netfilter模块是否加载并在/etc/sysctl.d/里配置net.ipv4.ip_forward1因为后续Calico的转发依赖这个开关。Kylin V10默认内核一般没问题但离线环境最忌讳想当然每条节点都执行一遍求证掉。3. containerd 1.7.2先把容器运行时这层立住Kubernetes 1.26之后kubelet走CRI直接对接容器运行时docker在那个链条里的位置已经被替代。多主多从环境里每台节点多跑一个docker daemon就多一个进程要盯着也多一层转换失败的可能。这套资源包选用containerd 1.7.2的ARM64版本是我推荐的生产路子之一。3.1 为什么用containerd而不是dockerdocker要接入K8s必须通过cri-dockerd这个中间层把docker的API翻译成CRI多一层服务就多一个黑匣子。containerd从1.1版本开始内置CRI插件kubelet可以直接和它通信少一跳链路意味着少一类“插件版本不匹配”的报错。更重要的是cri-containerd-cni-1.7.2-linux-arm64.tar.gz解压后自带CNI plugin二进制Calico只需要提供配置文件不需要额外装一遍flannel或其他CNI工具。ARM环境下还有个现实问题自己编译containerd要处理Go交叉编译和一堆依赖官方发布ARM64 tar包直接用就行省掉一个巨大编译现场。安装命令很简单但路径要盯住。tar -C /usr/local -xzf cri-containerd-cni-1.7.2-linux-arm64.tar.gz systemctl daemon-reload systemctl enable --now containerdtar包解压会把containerd二进制放到/usr/local/binCNI插件放到/opt/cni/bin并把containerd.service写到/etc/systemd/system。enable --now是开机自启加立即启动。装完后用containerd --version验证版本号如果输出里带的版本不是1.7.2说明系统里可能残留了旧版二进制常见原因是/usr/bin和/usr/local/bin同时存在两份需手动删掉旧的。3.2 config.toml只动两处sandbox_image与SystemdCgroupcontainerd的配置文件默认在/etc/containerd/config.toml有些版本的tar包会把它放在/usr/local/etc/containerd/config.toml。先确认路径再改别用find凭感觉。这份配置里对K8s最关键的就是两个点。version 2 [plugins.io.containerd.grpc.v1.cri] sandbox_image registry.k8s.io/pause:3.9 [plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc] runtime_type io.containerd.runc.v2 [plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc.options] SystemdCgroup truesandbox_image必须和资源包里的pause-3.9.tar.gz保持一致。kubelet创建每个Pod的时候先要拉一个pause容器作为沙箱拉取地址就是这里指定的值。如果tar包里tag是registry.k8s.io/pause:3.9配置里也必须是这个不能写docker.io/pause:3.9。写错的结果是crictl images列表里明明有pausekubelet却一直报image not found因为它在找的是另一个仓库前缀。SystemdCgroup true是让containerd使用systemd的cgroup驱动和kubelet的cgroup driver对齐。K8s 1.26官方推荐这种统一方式。如果不设containerd默认用cgroupfskubelet却按systemd驱动管理节点Node状态可能是Ready但Pod调度上去后会在ContainerCreating卡很久最后以cgroup driver mismatch收场。这是离线部署里出现频率最高的隐性坑改完配置一定要重启containerd。systemctl daemon-reload systemctl restart containerd crictl images重启后立刻用crictl images确认镜像列表。如果列表是空的回看上一章的错误做法导入镜像时没用-n k8s.io。crictl默认连接的正是k8s.io命名空间所以ctr导入时必须带-n k8s.io。3.3 crictl配置与验证crictl是K8s配套的调节工具它不走docker直接对containerd发指令。先把客户端配置文件写好。runtime-endpoint: unix:///run/containerd/containerd.sock image-endpoint: unix:///run/containerd/containerd.sock timeout: 10 debug: false这个yaml写到/etc/crictl.yaml。runtime-endpoint让crictl知道该连哪个socketimage-endpoint与runtime-endpoint保持一致timeout和debug不多讲。配置完后crictl ps -a能看到所有运行中的sandbox容器crictl logs可以代替docker logs看Pod日志。如果某台机器上crictl连不上优先查socket路径是否存在以及containerd是否真的起来了。4. 多主多从控制面kubeadm配置、VIP入口与两个不同的join多主多从的高可用布局核心不是把kubeadm init跑三遍而是让每台master都成为控制面的一员同时对外提供一个统一入口。资源包里的kube-lb和keepalived就是干这件事的。4.1 高可用入口VIP加四层转发标准的布局是三台master节点上都跑apiserver、controller-manager、scheduler和etcd三者通过etcd选举和lease机制形成共识。在它们前面kube-lb承担四层负载均衡角色把VIP上的6443流量转发到各台master的6443。keepalived的两个配置master/backup决定VIP当前落在哪台机器上哪台master的kube-lb不健康VIP就自动漂到下一台。worker节点完全不用关心哪台master在实际服务它们只需要固定访问VIP:6443这就是控制面高可用的入口。kubeadm配置里的controlPlaneEndpoint就是给这个入口用的。4.2 kubeadm-first-master.yml关键字段资源包里的kubeadm-first-master.yml是第一个master的初始化依据。我拆出来核心字段大致如下。apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.26.15 controlPlaneEndpoint: 192.168.10.200:6443 networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 apiServer: certSANs: - 192.168.10.200 - 127.0.0.1controlPlaneEndpoint必须填VIP的地址和端口这个值会写进apiserver的证书SAN里其他组件连接控制面时统一用这个地址。certSANs里除了本机IP必须包含VIP的IP否则kubelet回连apiserver时证书校验会失败表现为节点Ready后过一会儿又NotReady证书错误在日志里非常隐晦。podSubnet是给Calico用的地址池这里设置成10.244.0.0/16后面Calico的IP池要和它一致不一致会出现Pod网络冲突kubectl get nodes倒是正常但Pod互相ping不通。kubernetesVersion必须严格写成v1.26.15。kubeadm init时会用这个字段决定拉取哪套镜像tag离线环境里版本写错它就会去远程仓库找直接卡死在拉镜像阶段。初始化命令kubeadm init --config kubeadm-first-master.yml --upload-certs--upload-certs会把控制面证书加密上传到集群它是后续master join时拿certificate-key的前提。我一般建议加--ttl 0延长证书上传的有效期避免搭到一半还要重新生成。4.3 后续master与worker的join差异第一个master初始化完成后后面两台master和所有worker节点要分别处理。区别只在一个参数但错了就是集群架构性错误。# 后续master加入控制面 kubeadm join 192.168.10.200:6443 \ --token token \ --control-plane \ --certificate-key key \ --discovery-token-ca-cert-hash sha256:hash # worker节点加入 kubeadm join 192.168.10.200:6443 \ --token token \ --discovery-token-ca-cert-hash sha256:hash后续master的join必须带--control-plane同时要提供certificate-keyworker节点完全不需要这两项。token默认有效期24小时超时后join命令直接报token过期。处理办法是用kubeadm token create --print-join-command --ttl 0生成一条永久有效的join指令里面已经包含token和ca-cert-hash直接复制到目标节点执行即可。4.4 kube-lb与keepalived的落地顺序kube-lb.conf的内容以资源包里的模板为准常见做法是定义一个vrrp_instance加健康检查脚本。要注意网卡名在ARM服务器上经常是eno1、ens3或者bond0而不是传统的eth0。keepalived配置里写死eth0的话启动时会直接报Interface not foundVIP自然也起不来。服务启动顺序要保证kube-lb先于keepalived。如果keepalived先拿到VIP流量打到6443时kube-lb还没起来健康检查脚本会判定失败VIP马上漂走形成一种反复横跳的假象。正确做法是把kube-lb设为keepalived启动的前置依赖或者干脆在健康检查脚本里同时检测本机6443端口是否真的监听。5. ARM环境排查实录镜像tag、cgroup驱动与高可用入口的五个坑拆这套包的过程里最经典的翻车都集中在“镜像tag对不齐、cgroup驱动不一致、VIP来回漂移”这几类问题上。下面按现象、原因、解决一段段记录下来。5.1 镜像加载成功但Pod全部ContainerCreating现象ctr import提示导入成功crictl images也能看到pause:3.9但所有调度出来的Pod一直ContainerCreatingkubelet反复报告Failed to create pod sandbox理由是image not found或者直接超时。原因sandbox_image没改对。kubelet创建sandbox用的镜像地址来自containerd config.toml里的sandbox_image不是crictl里能看到就代表能用。多数情况是kubeadm-first-master.yml里配置了国内仓库前缀比如registry.aliyuncs.com/google_containers而tar包里的镜像tag是registry.k8s.io两者前缀不同kubelet按yaml里的地址去找镜像当然找不到。解决统一镜像tag。要么把config.toml里的sandbox_image改成tar包里的实际tag要么用ctr打tag把镜像改成yaml里那个地址。我一般选择打tag因为kubeadm init时拉取控制面组件也需要仓库前缀一致。# 示例把pause镜像改tag使其匹配yaml中的仓库地址 ctr -n k8s.io images tag registry.k8s.io/pause:3.9 registry.aliyuncs.com/google_containers/pause:3.9 systemctl restart containerd打完tag后crictl images里会出现两个相同ID的pause镜像kubelet用哪个取决于config.toml指向哪个。改完最好用crictl pull registry.aliyuncs.com/google_containers/pause:3.9预拉一次拉成功了再继续后面的流程。5.2 keepalived脑裂VIP出现在两台上现象两台master上都用ip addr能看到同一个VIP客户端访问6443时通时不通keepalived日志没有明显报错反复切换。VIP像墙头草一样来回飘。原因keepalived默认走VRRP组播内网交换机或者云环境屏蔽了组播报文两台机器收不到对方的VRRP通告都以为自己是运维才允许的。另一种情况是健康检查脚本判定逻辑有误比如脚本返回0代表正常但脚本写成返回0代表异常结果健康节点被误杀VIP被抢走。如果master和backup的priority差值太小比如一个是100一个是90网络抖动时也会频繁抢占。解决把VRRP切到单播模式在配置里显式指定unicast_peer列表填上各master的实际IP避免依赖组播。vrrp_script的interval适当调大比如2秒脚本内部检测的是本机kube-lb对127.0.0.1:6443的连通性返回0表示正常。主节点priority设150备份节点100差距拉开并开启nopreempt防止正常节点被误抢。5.3 ARM上Calico报autodetect失败或一直CrashLoop现象calico-node的Pod在ARM节点上反复重启日志里看到类似Failed to auto-detect an IP address或者Unable to find a usable interface的信息。另一类表现是启动卡在initContainer阶段提示找不到合适的网卡。原因Calico需要选择本机出网网卡来做IP自动检测。ARM服务器的网卡命名通常是eno1、ens3而Calico默认的IP_AUTODETECTION_METHOD是first-found它会选到一个没有实际路由的接口甚至回环接口。另一个隐藏问题是ARM节点的内存通常不大Calico v3.26的felix进程不限制资源的话内存稍微紧张就被OOM杀掉表现就是Pod反复重启。解决在calico.yaml环境变量里把IP自动检测方式改成接口正则比如IP_AUTODETECTION_METHOD: interfaceens.*或直接写死网卡名interfaceens3。内存小的节点把felix的request降低limit去掉优先保证它能跑起来。# calico.yaml中Deployment/Node的env片段示意 - name: IP_AUTODETECTION_METHOD value: interfaceens.*改完配置重新应用calico.yaml旧的calico-node Pod会被滚动重建。如果还是起不来看InitContainer日志常见的是CNI配置目录权限问题在ARM麒麟上/opt/cni/bin的owner必须是root。5.4 kubeadm init卡在等待控制平面现象kubeadm init执行到[wait-control-plane]阶段就停住systemctl status kubelet显示Active但kubelet日志里不断刷cgroup driver相关报错或一直提示无法连接localhost:10248。原因cgroup驱动不一致。kubelet通过10-kubeadm.conf指定了--cgroup-driversystemd而containerd的config.toml还停在默认的cgroupfs。两者驱动不一致kubelet起不来控制面组件自然无法上报健康状态。解决先停掉kubelet把containerd的SystemdCgroup改成true重启containerd再启动kubelet。如果之前已经反复init失败过执行kubeadm reset清掉残留状态再重新init。不要只改一个组件然后重复init那样只是在原地打转。systemctl stop kubelet # 改好 /etc/containerd/config.toml 后 systemctl restart containerd systemctl start kubelet kubeadm reset -f kubeadm init --config kubeadm-first-master.yml --upload-certsreset是后悔药但它只在最后一步才值得用。前面检查不完整时reset了也没用。5.5 join master与join node用错文件现象第二台master用了kubeadm-join-node.yml去加入命令显示node joined成功kubectl get node里也能看到这台机器是Ready但集群的etcd成员只有一个在跑apiserver还是单实例这台机器一旦宕机集群控制面当场不可用。原因加入控制平面和加入工作节点走的是完全不同的路径。master join需要获取控制面证书并在本地拉起apiserver、etcd等静态Podworker节点join只启动kubelet和kube-proxy。用node的配置加master等于把它当纯worker用了。解决后续master必须使用kubeadm-join-master.yml并携带--control-plane和--certificate-key参数。如果init时没有打印出certificate-key在第一台master上执行kubeadm init phase upload-certs --upload-certs重新上传证书拿新打印的key去join。token过期就重新kubeadm token create --ttl 0 --print-join-command生成。6. 集群起来后先别急着跑业务证书期限与高可用自检集群搭完最怕的是“看起来Ready”真出问题时无路可退。我每次交付多主多从都会先跑一轮自检顺便把证书和token的账算清楚。6.1 三组命令确认集群真的健康kubectl get nodes -o wide kubectl get pods -n kube-system -o wide kubectl get --raw/readyz第一组看节点角色、内核版本和内部IP第二组看每台master上的etcd、apiserver、calico是否都在各自节点上拉起了副本重点检查是否存在大量Evicted或Pending第三组对apiserver做详细健康检查返回ok才是真健康。这三组命令都过了再考虑往集群里跑业务。6.2 证书期限与token过期不靠玄学1.26.15控制面证书默认一年多主多从里每台master的证书独立管理每年最容易踩坑的就是证书过期。检查命令是固定的kubeadm certs check-expiration它会列出apiserver、etcd-server、front-proxy等证书的过期时间和剩余天数。快到期时用kubeadm certs renew all就地续期然后重启这台上master的kubelet以及apiserver、controller-manager、scheduler静态Pod。token过期则用kubeadm token create --ttl 0 --print-join-command重新生成。从那以后我每次搭完多主多从不论环境多赶都会强制走一遍kubectl get nodes、readyz检查以及keepalived VIP手动漂移测试确认答辩完事再交付。这三个动作帮我挡掉了不少事后很难查的隐形故障希望帮到你。本文还有配套的精品资源点击获取
返回列表