ARTICLE DETAIL

资讯详情

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

OpenEuler部署Rancher踩坑:cgroup v2与iptables/nftables兼容性排查

OpenEuler部署Rancher踩坑:cgroup v2与iptables/nftables兼容性排查 收到告警的时候是凌晨两点多监控面板上那台OpenEuler 24.03 SP4节点已经NotReady快十分钟了日志里刺眼地刷着kubelet stopped posting node status。我第一反应是节点挂了但SSH上去系统负载很正常Docker也还活着就是kubelet跟API Server彻底失联。折腾了一晚上最后定位到两个非常隐蔽的元凶cgroupv2和iptables的底层行为差异。这篇文章就把整个过程复盘一遍包括原理、排查链路和最终修复方案给准备在OpenEuler上跑Rancher的人省点夜宵钱。先说结论OpenEuler 24.03 SP4默认启用cgroup v2且iptables命令实际转发到了nftables后端这两个特性跟部分版本的Kubernetes组件、容器运行时存在兼容性错位。如果你直接照搬CentOS 7那套部署习惯大概率会踩进同一个坑。下面从环境准备到故障复现一步步拆。1. OpenEuler 24.03 SP4上部署Rancher前先看清这两点1.1 系统的cgroup v2默认策略OpenEuler 24.03 SP4的内核版本是基于6.6系的这个版本上cgroup v2已经是默认且唯一启用的层级结构。你可以在系统里直接验证mount | grep cgroup如果输出里只有cgroup2类型没有cgroup类型的/sys/fs/cgroup挂载那就说明系统已经完整跑在v2上了。这里最关键的区别在于cgroup v2的接口文件、层级规则和控制器行为跟v1完全不同。比如v1里有/sys/fs/cgroup/memory/memory.limit_in_bytes这种针对性很强的文件v2里统一收敛成了/sys/fs/cgroup/路径/memory.max并且no internal process规则意味着非叶子节点不能直接挂进程。这意味着什么Kubernetes从1.25左右开始才把cgroup v2支持标为稳定如果你部署的K3s、RKE2或者手动安装的kubelet版本偏老默认的cgroup驱动还是cgroupfs而OpenEuler上systemd本身用的已经是systemd驱动两边一撞车kubelet要么拒绝启动要么启动了但无法正确管理Pod资源最终表现就是心跳上报失败、节点NotReady。网上很多人遇到的failed to check memory cgroup这类报错根源也在这里。所以在做任何部署之前先确认两件事第一你的Kubernetes发行版是否支持cgroup v2第二kubelet的--cgroup-driver必须跟容器运行时的驱动一致。这个后面实操部分会详细展开。1.2 网络栈iptables与nftables并存现状第二个隐藏问题是防火墙规则。OpenEuler从23.03开始系统自带的iptables命令其实是一个兼容层后端已经切到了nftables。你可以用一条命令确认iptables --version如果输出里带(nf_tables)字样说明你敲的iptables规则最终是交给内核的nftables框架执行的。这本身不是问题问题在于Kubernetes生态里不少组件还在直接操作iptables的legacy后端或者它们判断后端的方式比较粗暴——尝试初始化iptables-legacy和iptables-nft谁不报错用谁。一旦判断错kube-proxy或Rancher的网络组件比如Canal、Flannel写入的规则就可能落到错误的后端结果就是规则明明插进去了但流量走着走着就断了现象极其迷幻。更麻烦的是OpenEuler默认有firewalld在跑它也是nftables后端。你如果为了给Rancher的端口放行手动加了一堆iptables规则但这些规则只在INPUT链生效而容器流量走的是FORWARD链经典的bridge-nf-call-iptables内核参数没开那Pod之间的通信、Service的ClusterIP转发就会时好时坏。这个问题在Ubuntu、Debian上不明显因为它们的网络栈行为跟RHEL系本来就不同而OpenEuler恰恰继承了RHEL系这一整套习惯很多人跨系统迁移时在这里栽跟头。2. 两个故障的根因都在内核与组件之间的版本错位2.1 cgroupv2如何让kubelet“失联”先讲现象。当时我创建了一个下游RKE2集群节点是OpenEuler 24.03 SP4的机器Agent启动后大概一两分钟Kubernetes API里能看到节点注册上来但紧接着状态就变成NotReady事件列表里刷屏的全是kubelet stopped posting node status.这句报错的字面意思是kubelet没有按时上报节点状态。但底层原因千奇百怪我见过磁盘满、API Server过载、证书过期、网络不通而这次是cgroup驱动错位。Kubelet启动的时候会读取一个cgroup-driver参数它告诉kubelet你管理Pod资源限制时用systemd去操作cgroup还是直接往cgroupfs里写。容器运行时比如containerd也有同样的参数二者必须一致。在cgroup v1时代很多人习惯用cgroupfs毕竟直接、透明但到了cgroup v2时代systemd成为了cgroup树的唯一管理者你再绕过它直接写cgroupfs内核会直接用EBUSY拒绝你或者产生竞态。RKE2默认的cgroup驱动是systemd而如果我在启动节点时被某些老教程影响显式指定了cgroupfs或者容器运行时那边配置没跟着同步kubelet就会在创建Sandbox Pod时失败连带着心跳上报一起挂掉。我当时在节点上看了kubelet日志大致是这样的错误Failed to run kubelet: failed to run Kubelet: misconfiguration: kubelet cgroup driver: systemd is different from docker cgroup driver: cgroupfs所以这类问题的判断依据很直接先看报错里是否提到misconfiguration或者cgroup driver is different如果是几乎不用怀疑就是两边的cgroup驱动没对齐。你不需要去改内核参数cgroup v2是内核行为改不了的要改的是Kubernetes组件的配置让它们都滚到systemd驱动上。2.2 iptables的规则写入为什么失败第二个故障是紧跟着出现的。节点状态恢复后我部署了一个测试应用发现Service的ClusterIP怎么都ping不通但节点IP能通。进到容器里看路由默认路由走的是eth0从eth0出去的流量确实到了宿主机但宿主机上的DNAT规则没生效请求就卡死在半路。这个问题出在RKE2内置的kube-proxy和iptables后端选择上。Kube-proxy在初始化的时候会检查当前系统支持哪种iptables后端然后选择一种来操作。如果系统里同时存在iptables-legacy和iptables-nft两个可执行文件而kube-proxy选错了规则就会写到错误的后端。在OpenEuler 24.03 SP4上iptables命令默认指向nftables后端但kube-proxy通过NewLegacy()初始化时检测到legacy后端也能加载两边规则各写各的自然就乱了。更隐蔽的是容器网络插件CNI也在操作iptables。比如Flannel的VXLAN模式会在FORWARD链里加规则Canal的BGP或Policy规则也依赖iptables如果CNI和kube-proxy选的后端不一致它们的规则就互不可见数据平面就断了。我在现场用iptables-save看规则链是空的换成iptables-legacy -t nat -L -n一查DNAT规则全在legacy里而内核实际处理的却是nftables那边的空规则对比极其明显。所以在OpenEuler上解决iptables问题的本质是统一整个系统的iptables后端让kube-proxy、CNI、用户手动操作这三个层面全部走同一个后端。最省事的方式是把iptables-legacy和iptables-nft的优先级调整好或者干脆切换系统的iptables替代工具让所有调用都落到nftables。3. 实操复盘从报错到恢复的完整过程3.1 环境准备镜像源、Docker、关闭swap先说环境。我用的是OpenEuler 24.03 SP4最小化安装。装完第一件事是配国内镜像源不然后面拉包会让人崩溃。OpenEuler官方源在国外国内机器建议直接换成清华或阿里云的镜像。改源的方式比较标准在/etc/yum.repos.d/openEuler.repo里把baseurl里repo.openeuler.org替换成mirrors.tuna.tsinghua.edu.cn/openeuler然后dnf makecache。这里建议至少先更新一下系统把内核和systemd升到仓库最新能少踩很多坑。然后装Docker。OpenEuler的仓库里有自带docker包但版本通常偏老我这次用的是Docker官方源的版本。OpenEuler上装Docker官方源稍微有点讲究因为官方源主要是为Ubuntu、CentOS、Fedora设计的但OpenEuler可以借用CentOS 9的RPM包兼容性实测没问题。配置源后直接dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin systemctl enable --now docker注意一个细节装完Docker后一定要确认它的cgroup驱动。新版本Docker默认用systemd驱动但如果你以前在老系统上改过/etc/docker/daemon.json里面可能还残留着exec-opts: [native.cgroupdriversystemd]以外的配置。检查方式docker info | grep -i cgroup输出应该是Cgroup Driver: systemd如果不是就去改daemon.json并重启Docker。最后关闭swap。Kubernetes在1.28之前默认不允许swap开启否则kubelet直接不干活。OpenEuler上即使你swapoff -a了还要注意/etc/fstab里的swap条目不然重启又回来了。我习惯直接注释掉/etc/fstab里的swap行一劳永逸。还有几个内核模块必须先加载modprobe br_netfilter modprobe overlay然后设置sysctlcat /etc/sysctl.d/99-kubernetes.conf EOF net.bridge.bridge-nf-call-iptables 1 net.bridge.bridge-nf-call-ip6tables 1 net.ipv4.ip_forward 1 EOF sysctl --system这一套做完Kubernetes的基础运行条件才算齐了。我见过很多人在OpenEuler上启动节点失败最后发现就是br_netfilter没加载或者ip_forward为0流量压根过不去。3.2 部署Rancher Server与创建下游集群Rancher Server本身部署不复杂官方给的Docker命令直接跑docker run -d --name rancher \ --restartunless-stopped \ -p 80:80 -p 443:443 \ --privileged \ rancher/rancher:v2.8.5这里注意两点。第一端口80和443最好都映射出来Rancher的Web UI和API都走这两个端口你只映射443会导致部分场景访问异常。第二--privileged不能省Rancher里管理下游集群时需要创建一堆命名空间、Pod、网络策略没有特权模式很多系统级操作会失败。Rancher Server起来后访问https://节点IP就能看到初始化页面拿到默认密码后先改掉然后创建一个下游集群。我这次选择的是RKE2因为RKE2在OpenEuler上的兼容性比老版RKE好很多而且原生支持cgroup v2。创建集群时Rancher会生成一个注册命令会在下次启动时把K3s Agent或RKE2节点拉起来。我的问题就是在这一步复现的——节点加入后一分钟内就失联事件里出现kubelet stopped posting node status。顺带一提Rancher Server本身跑在OpenEuler上它的Docker容器正常端口也通了说明问题不是出在Server端而是出在下游节点的kubelet配置上。3.3 处理cgroupv2与kubelet的兼容问题登录到下游节点上先看kubelet服务状态systemctl status kubelet journalctl -u kubelet --no-pager -n 200日志里出现了前面说的那个经典错误misconfiguration: kubelet cgroup driver: systemd is different from docker cgroup driver: cgroupfs这就很清楚了节点上既有Docker又有RKE2的kubeletDocker那边用的还是老的cgroupfs驱动。修复方式有两个。方式一改Docker的cgroup驱动。编辑/etc/docker/daemon.json{ exec-opts: [native.cgroupdriversystemd] }然后systemctl restart docker方式二如果不想动Docker那就得让kubelet也退回cgroupfs。但我不建议这么干尤其是在cgroup v2的系统上cgroupfs驱动跟systemd的管理模型冲突后面Pod的资源隔离会不稳定。所以老老实实统一到systemd驱动上。RKE2这边如果kubelet还是报错可以直接在/etc/rancher/rke2/config.yaml里显式指定kubelet-arg: - cgroup-driversystemd然后重启RKE2服务systemctl restart rke2-server如果是K3s节点对应配置文件是/etc/rancher/k3s/config.yaml参数名一样。改完后等一两分钟节点状态会从不健康转成Ready。关键是验证一下kubelet日志里不再出现cgroup相关报错然后用kubectl get nodes确认STATUS列是Ready。这里我要多说一句很多人在OpenEuler上遇到这个报错第一反应是卸载Docker或者换运行时。其实不用Docker和RKE2可以共存只要cgroup驱动统一就行。真正的问题只有驱动不一致其他都是表象。3.4 修好iptables与容器网络节点恢复Ready我兴冲冲部署了一个测试Deployment结果Service不通。看Pod里访问ClusterIP超时。查iptables -t nat -L -n没看到KUBE-SVC-链但用iptables-legacy -t nat -L -n一看规则都在。这就实锤了后端不一致。处理思路是让系统的iptables命令统一走nftables。OpenEuler其实提供了iptables-nft和iptables-legacy两个工具系统默认的iptables是通过alternatives机制选择的。可以显式切换默认后端update-alternatives --set iptables /usr/sbin/iptables-nft但光切这个还不够因为kube-proxy和CNI是通过自己的逻辑去探测后端的。在RKE2里最好直接让kube-proxy明确指定iptables模式。修改/etc/rancher/rke2/config.yamlkube-proxy-arg: - proxy-modeiptables同时确保内核模块加载和sysctl设置没问题尤其是br_netfilter我之前那套环境准备命令不是白跑的。还有一种更省心的处理思路清掉所有遗留规则让kube-proxy和CNI重新初始化。方式是把节点上已有的iptables规则全部刷掉iptables-nft -F iptables-nft -t nat -F iptables-nft -t mangle -F iptables-nft -X但要小心这只适合在刚加入集群、还没有生产流量的节点上做。如果集群里已经跑着业务千万别这么干你会把所有正在转发的流量全切断。我的习惯是先把节点从集群中摘除清完规则再重新加入。清完规则后重启网络相关组件systemctl restart rke2-server等一两分钟再用iptables -t nat -L -n检查能看到KUBE-SVC-、KUBE-SEP-链都出现了Service的ClusterIP也能通了。到这里我的Rancher下游集群才算真正稳定下来。4. 常见报错与排查技巧实录4.1 几张速查表排障过程中我整理了几张对照表以后遇到类似问题可以直接对着查。现象根因方向优先排查命令kubelet stopped posting node statuscgroup驱动不一致 / 节点负载高 / API Server失联journalctl -u kubelet -n 200重点看misconfigurationClusterIP不通kube-proxy与系统iptables后端不一致iptables -t nat -L -n与iptables-legacy -t nat -L -n对比Pod无法创建报failed to check cgroupcgroup v2与cgroupfs驱动冲突docker info | grep -i cgroup容器间通信时好时坏bridge-nf-call-iptables未开启sysctl net.bridge.bridge-nf-call-iptables应为1Rancher Web界面打不开防火墙未放行80/443firewall-cmd --list-ports排查cgroup问题时的命令组合也很重要一套走下来基本能定位# 看cgroup版本 mount | grep cgroup # 看docker的cgroup驱动 docker info | grep -i cgroup # 看kubelet的cgroup驱动RKE2环境 cat /var/lib/rancher/rke2/agent/kubelet.kubeconfig 2/dev/null; journalctl -u rke2-server | grep -i cgroup-driver排查iptables问题时我建议把三份输出都打出来对比着看iptables -t nat -L -n iptables-legacy -t nat -L -n iptables-nft -t nat -L -n如果iptables默认和iptables-nft输出一致但和iptables-legacy不一样说明系统默认走nftables如果默认输出和legacy一致那就反过来。关键是要让kube-proxy和CNI的行为跟系统默认对齐。4.2 三条少有人提的经验第一OpenEuler的防火墙默认firewalld是开着的而且对FORWARD链策略比较严格。就算你放行了INPUT链的端口容器流量走FORWARD链照样会被DROP。所以如果你在OpenEuler上跑Kubernetes最省心的是在规划阶段就把firewalld停掉或者至少保证FORWARD链的放行策略覆盖容器网段。我不是说防火墙没用而是Kubernetes的网络模型本身已经提供了南北向和东西向的流量管理叠加系统防火墙只会让排障链路变得很长。第二Rancher创建下游集群时注册命令里会带上--with-node-id之类的参数生成的Agent启动脚本在OpenEuler上偶尔会因为sh兼容性问题报错。如果遇到脚本执行异常先看/var/lib/rancher目录的权限RKE2和K3s都会在系统启动时自动创建目录如果目录属主不对组件就起不来。ls -ld /var/lib/rancher确认属主是root不对就用chown -R root:root /var/lib/rancher修一下。第三OpenEuler上装完Docker后记得去看/etc/containerd/config.toml。新版Docker自带的containerd默认配置里SystemdCgroup可能是false导致容器运行时的cgroup驱动跟Docker的配置不一致。虽然Docker CLI会自动处理一部分但在Kubernetes环境里containerd的SystemdCgroup建议显式设为true否则你在节点上创建的Pod会再次撞上cgroup驱动错位的问题。这个配置在/etc/docker/daemon.json里对应的是exec-opts里的native.cgroupdriversystemd但独立装的containerd需要单独改。5. 几个常见的误区和我的建议5.1 误区一cgroup v2必须禁用才能用Kubernetes这是我在各个社区看到最多的一种误解。很多人一看到cgroup v2跟老版本Kubernetes不兼容就想方设法往内核参数里加systemd.unified_cgroup_hierarchy0把系统退回v1。我不建议这么干原因有三。第一OpenEuler 24.03 SP4的内核和systemd已经围绕v2做了大量优化强行降级属于逆势操作后续系统升级、安全补丁都可能受影响。第二Kubernetes 1.25对cgroup v2的支持已经稳定只要组件版本够新v2完全没问题没必要退回去。第三也是我踩过坑才意识到的降级到v1会让系统上一些新特性的配置失效比如IO延迟控制、CPU权重精细化这些在容器场景里恰恰很实用。与其逆着系统改不如顺着系统升。5.2 误区二iptables和nftables必须二选一另一个常见误区是觉得OpenEuler上nftables和iptables是死对头非要彻底禁用其中一个。实际上OpenEuler保留了iptables命令行工具只是后端可以是legacy或nft。在Kubernetes环境里你要做的不是禁用哪个而是让所有组件指向同一个后端。我用update-alternatives把默认后端切到nft再把kube-proxy和CNI的行为确认清楚就能稳定运行。5.3 真正的建议先在测试环境复现一遍最后给一个比较务实的建议如果你准备在生产环境的OpenEuler上部署Rancher强烈建议先在同样的系统版本上完整复现一遍安装和集群创建流程。因为你不知道哪个组件会在什么环节跟系统行为“摩擦”而这种摩擦只有在真实环境中才会暴露。我自己就是先在测试机上踩了一遍cgroup和iptables的坑摸清规律后才在生产环境动手整个过程从容很多。如果你已经遇到了类似问题按照我在第3节和第4节里的排查路径走一遍基本都能解决。核心就三句话cgroup驱动统一走systemdiptables后端统一走nftables内核模块和sysctl提前配齐。把这三件事做在前面OpenEuler上跑Rancher的体验不会比CentOS差。这轮排障下来我最大的体会是在OpenEuler这类较新的系统上部署老牌中间件最怕的不是系统太新而是你的操作习惯还停在旧系统上。很多问题的根子不在软件本身而在新旧机制交接时的那些细小差异。多花十分钟读日志、对比配置往往比翻几十篇旧教程管用得多。
返回列表