线上 K8s 集群 Pod 频繁 OOMKill,排查半天发现元凶在 cgroup v2?节点 load 飙到 CPU 核心数的 3 倍但 CPU 利用率只有 20%,问题居然出在 overlayfs 的写放大?如果你的 K8s 调优还停留在“改大 limits”和“加节点”的阶段,操作系统层的坑迟早会找上你。

我维护过 150+ 节点的生产集群,也经历过升级内核后 conntrack 表被打爆的惊魂时刻。上一篇聊了 K8s 自身的调优,这一篇专门讲运行系统——也就是操作系统和内核层面,到底该怎么调。

⚡ 速查急救包

节点出问题时先跑这几条,快速定位是 OS 层还是 K8s 层的问题:

# 1. 看 iowait 和 load,判断是不是 I/O 阻塞
top -bn1 | head -5
ps -eo pid,stat,comm | grep ' D' | head -20   # D 状态进程过多说明 I/O 阻塞

# 2. 看 conntrack 表是否被打满(高并发场景)
conntrack -C                                    # 当前连接数
cat /proc/sys/net/netfilter/nf_conntrack_max     # 上限,两者比值接近 80% 需警惕
dmesg | grep -i "nf_conntrack: table full"      # 出现此报错说明已打满

# 3. 看 cgroup 版本和资源限制是否生效
stat -fc %T /sys/fs/cgroup/                     # cgroup2fs = v2, tmpfs = v1
cat /sys/fs/cgroup/cpu.stat | grep throttled    # throttled 持续增长 = CPU 限流严重

# 4. 看内存回收压力
cat /proc/pressure/memory                        # avg10 超过 10 表示任务有 10% 时间在等内存回收

# 5. 看 inotify 实例使用量
# 注意:下面统计的是 fd 数量,一个 inotify 实例可能对应多个 fd,此为使用量上界估计
cat /proc/sys/fs/inotify/max_user_instances
find /proc/*/fd -lname anon_inode:inotify 2>/dev/null | wc -l

# 6. RHEL 8 上检查 cgroup v2 swap 参数(仅内核版本足够时可用)
cat /proc/sys/vm/force_cgroup_v2_swappiness 2>/dev/null || echo "not available on this kernel"

# 7. 看文件系统和 inode 是否吃紧
df -i /var/lib/containerd
iostat -x 1 3 | grep -E "Device|nvme|sd"

🧠 先搞懂:为什么 K8s 调优必须从 OS 层开始

很多人把 K8s 调优等同于“调 Pod 的 requests/limits”,但 Pod 的资源最终是由 Linux 内核的 cgroup 和命名空间来隔离的。OS 层没调好,上面怎么调都是空中楼阁。

层级

调优对象

核心问题

典型症状

内核内存

swappiness、min_free_kbytes、dirty ratio

内存回收与写入策略

OOMKill、写入延迟抖动

cgroup

cgroup driver、v1/v2

资源隔离的准确性

资源限制不生效、统计偏差

文件系统

overlayfs、XFS/ext4、IO 调度

容器读写性能

iowait 飙升、Pod 启动慢

网络栈

conntrack、iptables、ARP

连接跟踪与转发

丢包、连接超时、Service 不可用

CPU/内存高级

CPU Manager、NUMA、THP

延迟敏感的负载

跨 NUMA 访存、延迟不稳定

我印象最深的一个坑就是最后一条——CPU 绑核配了但 NUMA 没对齐,推理服务的 P99 延迟波动能到 3 倍以上。

📋 版本适用性速查

特性

最低 K8s 版本

最低内核版本

备注

cgroup v2

1.22 Beta / 1.25 GA

5.8

containerd 1.4+、systemd 244+

CPU Manager static

1.26 GA

5.8(建议,需 cgroup v2)

cgroup v1 下也可用,但 cpuset 子树委派安全性不如 v2

Memory QoS

1.22 Alpha

5.9(推荐)

v1.36 之前没有独立的预留策略控制;v1.36 引入 memoryReservationPolicy,截至 1.36 仍为 Alpha

kube-proxy conntrack 覆盖

1.29

—

升级时需检查 sysctl 是否被覆盖

一、内核基础参数:这些不调好,后面都白搭

先说说操作系统镜像怎么选

Ubuntu 22.04 和 Rocky Linux 9 是目前 K8s 节点最常见的两个选择,但它们的默认配置有差异。Ubuntu 22.04 默认使用 cgroup v2 和 ext4,内核版本为 5.15;Rocky Linux 9 默认使用 cgroup v2 和 XFS,内核版本为 5.14。如果你用 Ubuntu,overlayfs 底层文件系统需要额外确认 d_type 支持——ext4 在格式化时如果没启用 filetype,overlayfs 可能无法正常工作。Rocky Linux 9 的 XFS 默认就带 ftype=1,省心一些。选哪个发行版都可以,但要知道自己踩的是哪个坑。

Swap 到底关不关?

Kubelet 默认要求关闭 Swap,因为 Swap 会导致内存回收行为不可预测,Pod 的 OOM 判定会失真。生产环境的做法很明确:

# 临时关闭
swapoff -a

# 永久关闭,注释 /etc/fstab 中的 swap 行
sudo sed -i '/ swap / s/^/#/' /etc/fstab

如果你用的是 cgroup v2 + Kubernetes 1.28+,支持给 Pod 单独配置 Swap 了(通过 swapBehavior),但仍属 Beta 特性。除非你有明确的内存超卖需求,否则建议关掉。

必调的内核参数清单

# /etc/sysctl.d/99-k8s-os.conf

# --- 网络基础 ---
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1

# --- 内存策略 ---
vm.swappiness = 10              # swap 关闭时此参数影响有限,沿用社区常用值
vm.panic_on_oom = 0             # OOM 时触发 OOM Killer 而不是 panic 整个节点
vm.max_map_count = 524288       # ES/Java/高 mmap 场景必须
vm.min_free_kbytes = 1048576    # 64GB 内存保留 1GB,见下文说明

# --- 文件句柄与 inotify ---
fs.file-max = 2097152
fs.inotify.max_user_instances = 8192
fs.inotify.max_user_watches = 524288
fs.aio-max-nr = 1048576

# --- TCP 队列与复用 ---
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30   # 默认 60s,建议 30s 以加快 FIN-WAIT-2 回收
net.ipv4.ip_local_port_range = 1024 65535

📌 几个容易被忽略的参数解读:

vm.swappiness = 10 这个参数控制的是内核回收匿名页(进程内存)和文件页(page cache)的相对权重,不是“是否使用 swap 的开关”。在 swap 完全关闭的 K8s 节点上,这个参数实际上几乎不影响行为——内核没有 swap 空间来换出匿名页。设 10 还是 0 在实际效果上没有本质区别,沿用社区常用的 10 即可。

⚠️ 但这里有一个隐藏的坑:在 cgroup v2 环境下,每个 cgroup 有独立的 memory.swappiness 值,默认值为 60。如果你发现即使调低了系统级的 vm.swappiness,容器内的 swap 行为仍然不符合预期,需要检查 vm.force_cgroup_v2_swappiness 参数。RHEL 8.7+ 引入了这个新 sysctl,默认值为 0。设为 1 后,所有 cgroup 的 memory.swappiness 值将被弃用,统一反映系统级的 /proc/sys/vm/swappiness 值。

使用前请确认内核版本已更新到包含该参数的版本。Red Hat 官方对不同 RHEL 8 小版本的要求如下:

  • RHEL 8.7:升级到 kernel-4.18.0-425.3.1.el8.x86_64 或更高版本即可
  • RHEL 8.6.z(EUS):需升级到 kernel-4.18.0-372.36.1.el8_6 或更高
  • RHEL 8.4.z(EUS):需升级到 kernel-4.18.0-305.76.1.el8_4 或更高

低版本内核上设置这个参数不会有任何效果。如果你在 RHEL 8 上遇到“内存充足却频繁 swap”的问题,先确认内核版本,再检查这个参数。

vm.panic_on_oom = 0 告诉内核在内存不足时调用 OOM Killer 来终止进程,而不是 panic 整个节点。在 K8s 节点上,这意味着 OOM 影响被限制在单个容器或进程,而不是整个节点重启。

vm.min_free_kbytes 这个参数的本质是给内核的“紧急备用金”。Red Hat 的参考值是:64GB 内存保留 1GB,128GB 保留 2GB,256GB 保留 3GB。Red Hat 同时警告:该值过高(达到系统内存的 5-10%)会导致系统立即进入内存不足状态。设得太低会导致内核来不及回收内存就触发直接回收,设得太高则会直接减少可用内存。建议在上述参考值附近取值,根据节点的实际内存压力观察后微调。

fs.inotify.max_user_instances 限制的是每个用户能创建的 inotify 实例数量。默认值因发行版而异——RHEL/CentOS 系列常见默认值为 128,部分 Ubuntu 版本可能更高,但 128 是最常见的起点。在 K8s 节点上,max_user_instances 更容易先耗尽。Kubespray 的社区 PR 指出,即使在开发集群的小节点上,系统默认的 128 限制也已使用了超过 50%,并出现了 failed to create fsnotify watcher: too many open files 的错误。Kubespray 已将默认值提升至 8192。

设置之后怎么判断够不够用?注意下面的命令统计的是所有进程中打开的 inotify 文件描述符(fd)数量,而 max_user_instances 限制的是实例数量——一个实例可能对应多个 fd。因此该命令给出的数值是使用量的上界估计,不是精确值:

# 当前用量(fd 数量,近似值,一个实例可能对应多个 fd)
find /proc/*/fd -lname anon_inode:inotify 2>/dev/null | wc -l
# 上限
cat /proc/sys/fs/inotify/max_user_instances

如果你需要按用户维度更精确地统计,可以这样:

# 按用户维度统计 inotify fd 数量
for uid in $(awk -F: '$3>=1000 {print $3}' /etc/passwd); do
  count=$(find /proc/*/fd -lname anon_inode:inotify -user "$uid" 2>/dev/null | wc -l)
  [ "$count" -gt 0 ] && echo "uid=$uid: $count"
done

如果 fd 数量接近上限的 70%~80%,就说明该调大了——即使它是上界估计,也足以说明实例数量正在逼近限制。对于节点上运行超过 50 个 Pod 的集群,建议将 max_user_instances 设为 16384 或更高。

📌 以下参数不需要修改:vm.overcommit_memory。网上很多 K8s 调优文章会推荐设为 1,但 K8s 调度器本身不依赖这个参数——调度器只看节点的 Allocatable 和已分配的 requests,与内核 overcommit 策略无关。如果你的集群没有超卖需求,保持默认值 0 即可。

以上是节点级别的全局内核参数。如果你有延迟敏感的负载(推理、数据库、实时计算),还需要在 K8s 层面做更精细的 CPU 和 NUMA 控制,见第五部分。

二、cgroup v2:从 v1 迁到 v2 必看

怎么判断当前用的是哪个版本?

stat -fc %T /sys/fs/cgroup/
# tmpfs → cgroup v1
# cgroup2fs → cgroup v2

Kubernetes 对 cgroup v2 的支持在 1.22 进入 Beta,1.25 正式 GA。Kubernetes 官方文档明确要求 cgroup v2 需要 Linux 内核 5.8 或更高版本。托管平台如 GKE 从 1.26 起将 cgroup v2 作为新节点的默认值。自建集群的 cgroup 版本取决于操作系统发行版和容器运行时。containerd 1.4+ 和 systemd 244+ 是基本要求。

v2 下三个必须知道的变化

第一,内存统计口径变了。 v2 的 memory.current 包含 page cache,而 v1 的 memory.usage_in_bytes 在某些场景下不完全一致。升级后如果发现监控上的 Pod 内存用量“涨了” 20%~30%,先别慌,大概率是统计口径问题。

第二,Java 和 Go 的容器感知需要额外适配。 Java 8 pre-8u372 和 Java 11 pre-11.0.16 无法识别 v2 的资源限制,会读取宿主机内存而不是容器 limit,导致堆内存分配过大直接被 OOMKill。Go 1.19 之前的版本也有类似问题。解决方案是升级运行时,或者加 -XX:+UseContainerSupport 启动参数。

第三,swap 对不同 QoS 的行为不同。 Guaranteed 和 BestEffort Pod 默认不使用 swap,只有 Burstable 可以按比例使用。如果你依赖 swap 做内存缓冲,务必确认 QoS 等级。

迁移检查清单

# 1. 确认内核版本 ≥ 5.8
uname -r

# 2. 确认 systemd ≥ 244
systemctl --version

# 3. 确认 containerd cgroup driver 为 systemd
containerd config dump | grep SystemdCgroup
# 应该输出 SystemdCgroup = true

# 4. 确认 kubelet cgroup driver 与 containerd 一致
ps aux | grep kubelet | grep -o 'cgroup-driver=[^ ]*'
# 应该输出 cgroup-driver=systemd

# 5. 确认节点上没有依赖 cgroup v1 的服务
# 精确匹配 cgroup v1 特有的子路径(memory/cpu/cpuacct/blkio/devices)
lsof 2>/dev/null | awk '$9 ~ /\/sys\/fs\/cgroup\/(memory|cpu|cpuacct|blkio|devices)(\/|$)/ {print $1, $2, $9}' | sort -u

📌 第 5 步容易被忽视。为什么用这个精确匹配?因为纯 cgroup v2 环境下 /sys/fs/cgroup/ 本身仍然是一个有效的挂载点,很多正常进程都会访问它——如果只用 grep '/sys/fs/cgroup/' 去查,会输出一大堆无关的 v2 路径访问记录。真正需要警惕的是硬编码了 v1 特有子路径的服务,比如旧版 Zabbix Agent 读取 /sys/fs/cgroup/memory/、Datadog Agent v6 读取 /sys/fs/cgroup/cpu/。这些路径在纯 v2 环境下已经不存在,服务要么启动失败,要么静默丢数据。

⚠️ containerd 和 kubelet 的 cgroup driver 不一致时,节点会不稳定甚至 NotReady,Pod 无法启动。务必两边都是 systemd。

三、文件系统与 I/O:overlayfs 的隐藏代价

现象

节点 iowait 突然飙升到 30%+,应用侧只是做了个普通的文件写入,但节点 load average 冲到了 CPU 核心数的 3 倍,iostat -x 显示 I/O 队列深度(aqu-sz)超过 8,大量进程处于 D 状态。这里要理解一个关键点——load average 统计的不只是 CPU 繁忙程度,还包含 D 状态(不可中断睡眠)的进程。进程在等待磁盘 I/O 时进入 D 状态,即使 CPU 空闲,load 也会被推高。

原因

容器里的文件写入触发 overlayfs 的 copy-up 操作——先把文件从 lower layer 完整复制到 upper layer,再执行写入。大量小文件写入时,这个复制过程会产生远超预期写量的元数据操作。配合 XFS 的日志提交线程或 ext4 的 jbd2 线程阻塞,整个文件系统写路径都会卡住。

解决方案

overlayfs 支持 ext4 和 XFS 两种底层文件系统。XFS 在 overlayfs 场景下更常见,主要原因是 XFS 在格式化时即可确保 ftype 支持,而 ext4 的 d_type 支持取决于格式化参数——如果格式化时没有启用 dir_index 和 filetype,overlayfs 可能无法正常工作。选择 XFS 更多是为了避免 ftype 配置失误,而不是因为它本身有显著的性能优势。

格式化 XFS 时必须加 ftype=1:

mkfs.xfs -f -m reflink=1,ftype=1 /dev/nvme0n1

mount 参数也值得调:

# /etc/fstab
/dev/nvme0n1  /var/lib/containerd  xfs  defaults,noatime,nodiratime,allocsize=4m  0 0

allocsize=4m 是 XFS 在 overlayfs 场景下的常见调优值,能减少小文件写入的元数据开销。具体值可以根据工作负载调整,4MB 是一个稳妥的起点。如果你用的是 NVMe SSD,把 I/O 调度器设为 none。

四、网络栈:conntrack 打满是最常见的“网络不通”

现象

Service 偶发不可用,dmesg 里刷屏 nf_conntrack: table full, dropping packet,新连接直接被丢弃。

原因

conntrack 表大小默认由内核决定(通常几万条),但 K8s 的 Service 转发依赖 conntrack 记录每条连接。节点上 Pod 数量多、短连接多的时候,conntrack 表很快被打满。

解决方案

优先通过 kube-proxy 配置,而不是直接改 sysctl:

conntrack:
  maxPerCore: 32768
  min: 131072
  tcpEstablishedTimeout: 24h0m0s
  tcpCloseWaitTimeout: 1h0m0s

⚠️ 关键注意:从 Kubernetes 1.29 起,kube-proxy 启动时的行为变更了——它允许设置低于节点 sysctl 的参数值。比如你在 sysctl 中把 nf_conntrack_max 设为 1000000,但 kube-proxy 计算的值为 131072,那么最终生效的会是 kube-proxy 计算出的 131072。这意味着如果你之前通过 sysctl 调大过 nf_conntrack_max,升级节点后一定要检查这个值是否被 kube-proxy 刷新回了较小值。

验证最终生效值:

cat /proc/sys/net/netfilter/nf_conntrack_max
conntrack -C

ARP 缓存也别忘了:

net.ipv4.neigh.default.gc_thresh1 = 1024
net.ipv4.neigh.default.gc_thresh2 = 4096
net.ipv4.neigh.default.gc_thresh3 = 8192

五、CPU 与内存高级调优:延迟敏感型负载的必修课

CPU Manager 的 static 策略通过 cgroup v2 的 cpuset 控制器来强制独占 CPU,所以在启用绑核之前,确保节点已经运行在 cgroup v2 上。cgroup v1 下 CPU Manager 的绑核功能是正常工作的,但 cpuset 控制器的子树委派机制存在安全隐患,cgroup v2 的 nsdelegate 机制解决了这个问题。

CPU Manager:从共享池到独占核心

Kubernetes 从 1.26 起 CPU Manager 的 static 策略正式 GA。对于延迟敏感的服务,把 CPU 绑核能显著降低延迟抖动。

kubelet 配置:

cpuManagerPolicy: static
kubeReserved:
  cpu: "500m"
  memory: "512Mi"
systemReserved:
  cpu: "500m"
  memory: "512Mi"

📌 kubeReserved 和 systemReserved 的值取决于节点规格和组件数量。对于 8C16G 的节点,500m/512Mi 是一个保守的起点。如果你的集群部署了 Calico、Prometheus node-exporter、Fluent Bit 等多个 DaemonSet,建议适当提高,比如 8C16G 节点预留 1 核 CPU 和 1Gi 内存。

Red Hat 官方文档提供了更精确的自动计算公式:1 GiB 内存以下的节点预留 255 MiB;超过 1 GiB 的节点,前 4 GiB 预留 25%,接下来 4 GiB 预留 20%,再接下来 8 GiB 预留 10%,剩余部分预留 6%。对于 16 GiB 节点,按这个公式计算约预留 2.55 GiB。你可以通过 kubectl describe node 观察实际用量后调整,也可以启用 autoSizingReserved: true 让 kubelet 自动计算。完整公式和配置说明见 Red Hat 官方文档:Allocating resources for nodes。

⚠️ 开启 static 策略后,只有 Guaranteed QoS 且 requests 为整数的 Pod 才能获得独占 CPU。Guaranteed QoS 要求 requests 和 limits 相等且均为整数:

resources:
  requests:
    cpu: 2        # 必须是整数
    memory: 4Gi
  limits:
    cpu: 2        # 必须与 requests 相等
    memory: 4Gi

写成 cpu: "1.5" 不会绑核,只改 requests 不改 limits 也不会绑核。

NUMA 对齐:CPU 和内存必须在同一个 NUMA 节点

绑核只解决了一半问题。如果 CPU 在 NUMA 0 但内存分配在 NUMA 1,跨节点访存延迟会增加 30%~50%。需要配合 Topology Manager:

topologyManagerPolicy: single-numa-node

single-numa-node 策略最严格也最有效——它要求 CPU、内存、设备全部对齐到同一个 NUMA 节点。如果无法对齐,Pod 会被拒绝调度。代价是调度可能失败,所以只建议在专用节点池上启用。

透明大页(THP):内存密集型负载的双刃剑

生产环境推荐配置:

echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
echo defer+madvise > /sys/kernel/mm/transparent_hugepage/defrag

madvise 模式只对显式调用 madvise(MADV_HUGEPAGE) 的区域启用 THP,避免全局 THP 带来的延迟尖刺。defer+madvise 让内核在后台做碎片整理,而不是阻塞式同步回收。

对于数据库类负载,直接用显式大页更可靠:

resources:
  limits:
    hugepages-2Mi: 2Gi
  requests:
    hugepages-2Mi: 2Gi

大页的 requests 和 limits 必须相等,不支持超卖。

⚠️ 彩蛋:cgroup v2 下两个极少人知道的软限制

cgroup v2 的内存控制器提供了两个软限制接口——memory.high 和 memory.low/memory.min。Kubernetes 的 Memory QoS 特性就是基于它们实现的,但在 v1.36 里发生了重要变化。

memory.high:给容器一个“减速带”

当容器的内存用量超过 memory.high 阈值时,内核会温和地回收内存——限制分配速率,给应用一个缓冲期来释放内存,而不是像 memory.max 那样直接触发 OOM Kill。启用 Memory QoS 特性门控后,kubelet 会根据 memoryThrottlingFactor(默认 0.9)自动设置 memory.high。一个 limits 为 1Gi 的容器,memory.high 会被设为 900Mi 左右。

v1.36 的新变化:memoryReservationPolicy

在 v1.36 之前,启用 Memory QoS 特性门控会立即为每个有内存 request 的容器设置 memory.min。memory.min 是硬预留,内核在任何情况下都不会回收。假设一个 8 GiB 的节点上 Burstable Pod 的 requests 总量达到 7 GiB,这 7 GiB 会被锁定为 memory.min,留给内核和系统进程的余量极少,反而增加了 OOM 风险。

v1.36 把节流和预留分开了:

  • 特性门控:只开启 memory.high 节流
  • memoryReservationPolicy(独立的 kubelet 配置字段):
    • None(默认):不写入 memory.min 或 memory.low,节流仍然有效
    • TieredReservation:分层保护——Guaranteed Pod 通过 memory.min 获得硬保护,Burstable Pod 通过 memory.low 获得软保护

启用方式:

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
featureGates:
  MemoryQoS: true
memoryThrottlingFactor: 0.9
memoryReservationPolicy: TieredReservation  # v1.36+,可选

我有个日志处理服务,内存用量有周期性峰值。之前设 limits: 1Gi,峰值一到直接被 OOMKill。开启 Memory QoS 后,内核在 900Mi 时开始温和回收,服务有足够的时间窗口来释放缓存,OOMKill 次数从每天 3~4 次降到了零。

⚠️ 版本要求:Memory QoS 特性要求 Kubernetes 1.22+ 和内核 5.8+;memory.high 节流在内核 5.9 之前存在活锁问题,该缺陷已在内核 5.9 中修复。截至 v1.36,该特性仍为 Alpha。

快速验证:

uname -r                                      # 检查内核版本
cat /sys/fs/cgroup/memory.high                # 检查 memory.high 是否可写

📋 参数速查汇总

以下参数按文档章节顺序排列,一 = 内核基础参数,四 = 网络栈,五 = CPU 与内存高级调优,彩蛋 = Memory QoS。

参数/配置

推荐值

适用场景

所在章节

vm.swappiness

10

全局内存策略

一

vm.force_cgroup_v2_swappiness

1(按需)

RHEL 8 cgroup v2 swap 异常

一

vm.panic_on_oom

0

全局内存策略

一

vm.min_free_kbytes

64GB→1GB / 128GB→2GB / 256GB→3GB

全局内存策略

一

fs.inotify.max_user_instances

8192(>50 Pod 时 16384+)

监控 Agent 密集场景

一

net.ipv4.tcp_tw_reuse

1

高并发短连接

一

net.ipv4.tcp_fin_timeout

30

高并发短连接

一

kube-proxy conntrack.maxPerCore

32768

高并发 Service

四

kube-proxy conntrack.min

131072

高并发 Service

四

cpuManagerPolicy

static

延迟敏感负载

五

topologyManagerPolicy

single-numa-node

NUMA 对齐

五

THP enabled

madvise

内存密集型负载

五

THP defrag

defer+madvise

内存密集型负载

五

Memory QoS memoryReservationPolicy

TieredReservation

内存敏感负载

彩蛋

📝 总结:OS 层调优的 5 条铁律

  1. Swap 关掉,swappiness 沿用社区常用的 10——在 swap 关闭的节点上这个参数几乎不影响行为;如果遇到 RHEL 8 上 cgroup v2 的 swap 异常,先确认内核版本(8.7 需 kernel-4.18.0-425.3.1.el8+,8.6.z 需 kernel-4.18.0-372.36.1.el8_6+,8.4.z 需 kernel-4.18.0-305.76.1.el8_4+),再启用 vm.force_cgroup_v2_swappiness
  2. cgroup v2 迁移前先检查 Java/Go 运行时版本,Java 8u372/11.0.16 以下必炸;containerd 和 kubelet 的 cgroup driver 必须一致;别忘了确认没有依赖 cgroup v1 特有子路径的服务
  3. overlayfs 底层推荐 XFS,核心原因是 ftype=1 在格式化时即可确保;mount 加 allocsize=4m
  4. conntrack 用 kube-proxy 的 conntrack-min 调,K8s 1.29+ 它会覆盖你的 sysctl 设置
  5. CPU 绑核必须同时满足 Guaranteed QoS 和整数 requests/limits,配合 NUMA 对齐才有效

你的节点上踩过哪些 OS 层的坑?有没有遇到过“K8s 调了半天最后发现是内核参数”的案例?评论区聊聊,说不定你踩的坑别人刚好也在踩。觉得有用的话,转给团队里管节点的那个人吧。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐