尧图精选

openEuler 24.03 SP4部署Rancher管理K8s:cgroupv2与iptables后端排障实录

🕒 发布时间:2026/9/16 9:18:28 📁 来源:尧图网络
先说结论openEuler 24.03 SP4上装Rancher管K8s真正折腾人的不是Rancher本身而是系统底层的cgroupv2和iptables后端。我在三台openEuler 24.03 SP4节点上把Rancher 2.8.5跑起来前前后后踩了kubelet一直报stopped posting node status、跨节点Pod不通、Service ClusterIP丢包这些典型坑。这篇文章就把排障过程、根因分析和最终的可用配置完整记录下来给要在openEuler上部署Rancher/K8s的朋友做参考。1. 项目背景与问题全景1.1 为什么是openEuler 24.03 SP4 Rancher这套组合openEuler 24.03 SP4是社区长期维护的企业级Linux发行版在电信、政务、金融等场景里出现频率越来越高。选择它作为Kubernetes集群的操作系统有几个实际考量一是社区和企业支持周期长适合拿来当生产底座二是内核版本新对硬件的兼容性比老版本好不少三是官方仓库内置了容器运行时需要的常用依赖离线部署时不用到处找包。Rancher这边我选的是2.8.x版本通过“Rancher ServerDocker部署 下游自定义集群containerd kubeadm”的方式管理。也就是说Rancher Server独立跑在Docker容器里不依赖K8s本身下游的K8s集群用kubeadm初始化出来之后通过Rancher生成的注册命令把节点纳管进去。这类架构在中小规模集群里非常常见踩坑样本足够多排查思路也更有代表性。1.2 两个核心问题现象第一个现象出现在用kubeadm初始化控制平面节点之后。kubelet不断重启kubectl get nodes要么看不到节点要么节点一直NotReady节点详情里报kubelet stopped posting node status。这个报错在K8s排障里属于经典款但这次的问题源头比普通证书或apiserver连通性问题隐蔽得多核心在cgroup驱动配置错位。第二个现象是集群好不容易起来了部署完Flannel网络插件之后Pod之间通信时通时不通Service的ClusterIP完全无法访问CoreDNS不停重启。用iptables查规则时发现openEuler的iptables命令默认走的是nftables后端而Flannel和kube-proxy期望的是传统iptables语义两套规则交叉作用网络的转发链路直接乱套。这两个问题单独拆开都不算特别难但叠加在openEuler这套组合上排查过程非常绕。下面我就从cgroupv2和iptables两条线分别展开。2. cgroupv2系统默认给Kubernetes埋的隐性雷2.1 什么是cgroupv2为什么openEuler 24.03 SP4默认开启cgroup是Linux内核用来限制、记录和隔离进程组资源CPU、内存、IO等的机制。cgroupv2是第二版相比老的cgroupv1它用统一层级结构替代了原来CPU、内存、IO各自分离的层级资源控制更严格也没有v1各种子系统之间状态不一致的问题。openEuler 24.03 SP4默认开启cgroupv2这是紧跟上游内核趋势的决策主流发行版都在往v2迁移。但Kubernetes在cgroup驱动上有个非常容易错位的点kubelet和容器运行时containerd都需要知道用哪种cgroup driver。现代Linux系统推荐用systemd驱动因为系统cgroup由systemd统一管理如果容器运行时和kubelet也按systemd的方式操作cgroup资源回收时就不会和systemd打架。反过来如果kubelet配的是cgroupfs而systemd同时也在管理同一批cgroup路径就会出现两边争抢、状态互相覆盖的问题。这个冲突在cgroupv1时代不太明显到了cgroupv2下会被直接放大常常表现为kubelet启动失败或者节点状态上报中断。2.2 现象还原kubelet反复重启节点状态迟迟不上报我第一次初始化时kubeadm init命令本身是成功的控制平面的静态Pod也都拉起来了但kubelet一直处于CrashLoopBackOff状态。用journalctl -u kubelet -f追踪日志能看到类似这样的报错Failed to run kubelet errfailed to run Kubelet: failed to create kubelet: misconfiguration: kubelet cgroup driver: \cgroupfs\ is different from docker cgroup driver: \systemd\如果用的是containerd报错可能不会直接指明cgroup driver而是只给出一句非常笼统的kubelet stopped posting node status.这句话本身只说明kubelet和apiserver之间的心跳断了完全不会告诉你根因。我当时先检查了kubelet服务状态、证书有效期、apiserver地址全都没问题最后是翻/var/lib/kubelet/kubelet.log和/var/log/messages才在底层日志里看到cgroup driver不匹配的真相。还有一个隐蔽点kubeadm在1.28版本之后如果配置里没有明确指定cgroup driver它会尝试自动探测。但自动探测经常被系统现有的初始化脚本干扰结果选了cgroupfs而容器运行时却按systemd驱动在跑两边直接错位。2.3 根因分析与修复操作根因就是kubelet的cgroup driver与容器运行时的cgroup driver不一致。系统层面openEuler 24.03 SP4使用systemd管理cgroup但kubelet默认配置却走了cgroupfs这个矛盾在cgroupv2下被放大成节点无法注册。我的修复操作分两步。第一步修改kubelet配置。在/var/lib/kubelet/config.yaml或kubeadm初始化配置里把cgroupDriver明确指定为systemdkind: KubeletConfiguration apiVersion: kubelet.config.k8s.io/v1beta1 cgroupDriver: systemd如果是在kubeadm-config.yaml里通过kubeletExtraArgs传参也可以这样写kind: JoinConfiguration nodeRegistration: kubeletExtraArgs: cgroup-driver: systemd但要注意版本差异。kubeadm 1.29及以上版本更推荐在KubeletConfiguration里配置cgroupDriver字段而不是通过kubeletExtraArgs传参后者在某些版本下会被直接忽略。第二步确保containerd的cgroup驱动也是systemd。containerd配置文件默认在/etc/containerd/config.toml检查并调整[plugins.io.containerd.grpc.v1.cri] systemd_cgroup true如果这个字段是falsecontainerd创建的沙箱和容器会按cgroupfs驱动来和kubelet仍然不一致。两处改完后记得重启服务systemctl restart containerd systemctl restart kubelet改完后kubelet启动正常等一两分钟节点状态自动变为Ready。这个坑给我的教训是在cgroupv2环境下kubelet、containerd、systemd三者的cgroup驱动必须全部统一到systemd没有中间态。2.4 补充如何快速验证cgroup版本和驱动如果你不确定当前环境到底用的cgroupv1还是v2可以用下面的命令快速确认# 查看cgroup版本v2输出为cgroup2fs stat -fc %T /sys/fs/cgroup/ # 查看kubelet实际生效的cgroup driver cat /var/lib/kubelet/config.yaml | grep -i cgroup # 查看containerd CRI配置 containerd config dump | grep -i systemd_cgroup我在后面第二次部署时直接先把systemd_cgroup true和cgroupDriver: systemd在初始化前就配好后面完全没有再因为cgroup问题翻车。3. iptablesopenEuler的nftables后端与集群网络的缠斗3.1 问题现象Pod间通信时通时不通Service完全不可用cgroup问题解决后我把所有节点加入集群开始部署网络插件。由于环境里没有外部网络我选了Flannel的host-gw模式少一层VXLAN封装转发性能更好。部署完成后Pod状态倒是Running但一测网络就露馅了同一台宿主机上的Pod可以互相ping通跨节点的Pod直接超时CoreDNS解析服务时好时坏大部分情况是超时后走fallbackkubectl get svc能看到Service存在但从Pod里访问ClusterIP完全不通。用ip route检查路由表Pod网段和节点网段没有冲突路由也都写进去了。用ethtool检查网卡状态也正常。最后把目光放在iptables规则上。执行iptables -t nat -L PREROUTING发现Kubernetes Service的转发规则链顺序和Flannel期望的完全对不上而且系统里明显存在两套规则集在互相覆盖。3.2 为什么openEuler的iptables命令会“骗人”openEuler 24.03 SP4默认启用了firewalld而firewalld的底层是nftables。当系统同时装了iptables-nft和iptables-legacy相关组件时/usr/sbin/iptables这个命令实际指向哪套后端直接决定你加的规则进到哪张表里。openEuler默认把iptables指向了iptables-nft也就是nftables的兼容层。这意味着你执行iptables命令加的规则实际落到了nftables的规则集里而用iptables-legacy查看时看到的完全是另一套空白规则集。Flannel的host-gw模式本身对iptables的依赖不像VXLAN模式那么深但Kubernetes的Service/NAT规则kube-proxy是100%依赖iptables语义的。kube-proxy默认使用iptables模式通过iptables命令写入Service的DNAT规则。而openEuler这套nftables兼容层写出的规则在实际转发路径上时灵时不灵具体取决于内核netfilter的执行顺序和firewalld规则的冲突情况。结果就是用iptables -t nat -S查看时能看到KUBE-SERVICES链和对应的DNAT规则但用nft list ruleset查看时这些规则又出现在一张名为nat的nft表里。理论上兼容层会自动翻译但翻译后的规则与firewalld本身的nft规则放在一起时钩子顺序经常被firewalld抢先DROP或REJECT导致ClusterIP变成黑洞。3.3 修复统一iptables后端并正确配置bridge-nf-call-iptables这个环境的处理方案是彻底统一iptables语义禁用firewalld改用纯iptables-legacy后端让Kubernetes所有网络组件都基于同一条规则链工作。具体步骤如下。第一步关闭firewalld并屏蔽开机启动systemctl stop firewalld systemctl disable firewalldfirewalld不参与网络管理之后nftables里那一套动态规则链就不会再主动改动避免和kube-proxy写入的规则互相覆盖。第二步确认iptables命令的实际指向必要时切换到legacy后端# 查看当前指向 ls -l /usr/sbin/iptables # 切换为legacy版本如果系统装有iptables-legacy update-alternatives --set iptables /usr/sbin/iptables-legacy如果update-alternatives不好用可以手动调整/usr/sbin/iptables的软链接指向但改完一定记得重启kube-proxy相关组件让规则在新后台上重建。这一步的目的是让所有手动iptables命令和组件内部调用的iptables命令走同一套内核接口避免规则被写进“看不见”的另一张表。第三步确保关键内核参数正确modprobe br_netfilter modprobe overlay sysctl -w net.bridge.bridge-nf-call-iptables1 sysctl -w net.bridge.bridge-nf-call-ip6tables1 sysctl -w net.ipv4.ip_forward1net.bridge.bridge-nf-call-iptables是Kubernetes网络链路里极其关键的一个参数。Pod之间走网桥转发时如果这个参数为0iptables规则对桥接流量完全不起作用Service的DNAT规则根本不会被处理。这就是很多人网卡、路由、Pod状态全正常但Service就是不通的常见原因。建议把参数写入/etc/sysctl.d/k8s.conf然后执行sysctl --system让所有节点生效。第四步重启kube-proxy相关组件。如果是kubeadm方式部署的集群kube-proxy是DaemonSet删除Pod让它重建即可kubectl delete pod -n kube-system -l k8s-appkube-proxy重建后重新验证Service连通性。如果一切正常再重新部署一次Flannel或Calico的Pod让它们重新写入规则集防止旧规则残留。3.4 关于Calico与Flannel的选择补充排障过程中我还顺手试过Calico。Calico对iptables的依赖比Flannel更直接BGP模式和iptables模式混合使用时对后端要求极高。如果系统iptables后端混乱Calico会直接报错比如Felix启动失败、BPF模式无法切换等。我的个人建议是在openEuler这类非传统CentOS/Ubuntu生态的发行版上第一次部署K8s网络时先把iptables后端统一好再选网络插件不要等插件部署完再临时切换后端那样要清理的残留规则非常多甚至需要重置整个集群才能恢复干净。我这次最终保留了Flannel的host-gw模式配置简单一个kube-flannel.yml丢进去就行。但在生产环境我更推荐Calico因为它支持NetworkPolicy对流量管控更细腻。只要底层iptables链路统一Calico在openEuler上的稳定性是可以信任的。4. 从零落地openEuler 24.03 SP4部署Rancher集群完整操作4.1 系统初始化与基础依赖不管踩多少坑真正落地还是要有一套干净的流程。以我的环境为例三台openEuler 24.03 SP4节点2C4G一台作为控制平面另外两台作为Worker。下面是每一步经过验证的可靠操作。第一步关闭swapKubernetes要求节点必须关闭swap才能正常工作swapoff -a sed -i / swap / s/^/#/ /etc/fstab第二步加载内核模块并写入sysctl参数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 vm.swappiness 0 EOF sysctl --system第三步安装containerd。openEuler的仓库自带containerd直接用dnf安装dnf install -y containerd安装完成后生成默认配置文件containerd config default /etc/containerd/config.toml然后修改两个关键项SystemdCgroup改为truesandbox_image改为实际能拉到的镜像地址。如果环境内没有外部镜像源这一步建议提前准备好离线镜像否则后面K8s组件会一直ImagePullBackOff。第四步安装kubeadm、kubelet、kubectl。openEuler的软件源可以提前配置Kubernetes官方源或镜像源这里不展开源配置过程只说版本选择。我选用的是Kubernetes 1.29系列它和Rancher 2.8.x匹配良好而且kubelet对cgroupv2的支持已经很完善。dnf install -y kubeadm-1.29.* kubelet-1.29.* kubectl-1.29.* systemctl enable --now kubelet4.2 初始化控制平面节点在控制平面节点上创建kubeadm配置文件核心是在初始化阶段就把cgroup驱动指定为systemd避免后面手动改。配置如下apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration nodeRegistration: kubeletExtraArgs: cgroup-driver: systemd --- apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.29.x networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 --- apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration cgroupDriver: systemd执行初始化kubeadm init --configkubeadm-config.yaml --upload-certs初始化成功后按照输出设置kubectl的kubeconfig记录生成的join命令。如果前面所有步骤都做对了这一步不会出现kubelet stopped posting node status的报错。4.3 部署Rancher ServerRancher Server我这里选择用Docker容器方式部署。如果你有单独的机器最好没有的话也可以复用控制平面节点但要注意安装Docker时不要和之前的containerd冲突。我这里是在单独的机器上安装Dockerdnf install -y docker-ce docker-ce-cli containerd.io systemctl enable --now docker启动Rancher Server容器docker run -d --name rancher-server \ --restartunless-stopped \ -p 80:80 -p 443:443 \ --privileged \ rancher/rancher:v2.8.5启动后访问https://server-ip设置admin密码和server-url。Rancher 2.8版本的默认CA证书在首次启动时会自动生成如果环境里不强制要求合规证书直接用HTTPS访问即可。4.4 创建下游集群并加入节点在Rancher界面中创建下游集群选择“自定义集群”。Rancher会生成一段注册命令指示在K8s节点上安装agent并把节点纳管进集群。这里要注意注册命令中通常会附带一组环境变量包括CATTLE_AGENT_IP、CATTLE_SERVER等在openEuler上执行前建议先确认节点的hostname能够被其他节点解析否则agent和kubelet通信时会出现节点状态反复震荡。在每个下游节点上执行注册命令后回到Rancher界面确认节点状态。如果节点一直是Active说明集群正常如果节点是Waiting或Reconnecting优先检查节点到Rancher Server的443和80端口连通性。4.5 部署网络插件并最终校验下游集群里的网络插件可以在Rancher的集群配置里直接安装也可以等集群Ready后用kubectl手动部署。我习惯手动部署Flannel因为可以指定镜像地址自建镜像仓库时比较方便。kubectl apply -f kube-flannel.yml部署完检查所有Pod状态然后做一轮冒烟测试创建一个测试Pod并curl CoreDNS的Service地址再跨节点创建两个Pod互相ping确认网络链路全部正常。到这里Rancher管理K8s集群的核心链路就走通了。5. 常见问题与排查技巧实录5.1 问题速查表把这次部署过程中遇到的所有问题和对应解法整理成一张表方便后面直接对照问题现象根因解决方式kubelet日志报cgroup driver冲突节点NotReadykubelet使用cgroupfscontainerd/systemd使用systemd统一cgroupDriver为systemdcontainerd开启SystemdCgroupkubectl get nodes看不到节点journalctl报stopped posting node statuskubelet无法连接apiserver或cgroup配置错误检查apiserver地址、证书、cgroup driver配置Pod启动正常但ClusterIP访问不通net.bridge.bridge-nf-call-iptables0或iptables后端不统一开启bridge-nf-call-iptables统一iptables后端跨节点Pod ping不通CNI规则被firewalld冲突或底层网络未就绪关闭firewalld重启网络插件PodCoreDNS频繁重启CNI网络插件未Ready或Service规则失效检查CNI Pod状态清空iptables残留后重新部署containerd拉取镜像一直ImagePullBackOffsandbox_image与所在环境镜像不匹配替换sandbox_image或配置离线镜像仓库5.2 独家避坑心得几条踩了很多次才总结出来的经验。第一版本强绑定意识。openEuler 24.03 SP4的默认内核是6.x系列与Kubernetes 1.26及以下版本存在cgroupv2适配问题。如果坚持用老版K8s可能连kubelet都无法正常运行。生产环境建议锁定K8s 1.28以上版本Rancher 2.8和2.9都能良好适配。第二修改任何网络相关内核参数后不要只在一台节点上改。kube-proxy、Flannel、Calico这些组件的规则依赖所有节点一致的内核行为。最稳妥的办法是用一套脚本批量下发swap、sysctl、内核模块和iptables后端配置从源头保证集群节点配置不漂移。第三排查iptables问题时要养成“先看后端”的习惯。遇到K8s网络不通先执行iptables -V查看版本再执行nft list ruleset看看是否存在同名规则如果两者同时存在基本可以断定后端混乱。不要一上来就慌着重置kubeadm、重建节点那样成本太高。第四Rancher的agent和K8s节点的时钟同步特别重要。如果节点时间偏差超过几十秒K8s证书校验会失败现象同样表现为节点状态反复横跳。建议提前配置chrony所有节点统一使用同一时间源。5.3 如果网络还是不通可以试试这条排查路径最后分享一个我自己常用的排查路径按顺序执行能覆盖绝大多数K8s网络问题检查节点状态kubectl get nodes -o wide确认所有节点Ready。检查Pod状态kubectl get pods -A -o wide确认核心组件Running。检查CNI配置查看/etc/cni/net.d/下是否存在正确的配置文件比如10-flannel.conflist。检查路由ip route确认节点间Pod网段路由是否通过CNI写入。检查iptablesiptables -t nat -L KUBE-SERVICES -n看是否有对应Service的DNAT规则。检查内核转发参数sysctl net.ipv4.ip_forward net.bridge.bridge-nf-call-iptables看是否为1。最后回到容器内测试kubectl exec -it pod -- ping target-ip逐层缩小范围。这套路径配合前面的速查表基本可以解决openEuler环境下K8s网络80%以上的问题。我在实际部署中体会到openEuler和Rancher这套组合真正难的不是某一个组件装不上而是底层系统行为与Kubernetes的预期之间存在细微偏差。cgroupv2要求所有运行时统一systemd驱动iptables要求链条语义彻底一致这两点搞清楚整个集群的稳定性会有质的提升。以后再在openEuler上部署K8s我会先把cgroup驱动、iptables后端、内核转发参数这三项作为前置巡检项卡住这关后面的路会顺畅很多。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →