线上 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。
| 参数/配置 | 推荐值 | 适用场景 | 所在章节 |
|
| 10 | 全局内存策略 | 一 |
|
| 1(按需) | RHEL 8 cgroup v2 swap 异常 | 一 |
|
| 0 | 全局内存策略 | 一 |
|
| 64GB→1GB / 128GB→2GB / 256GB→3GB | 全局内存策略 | 一 |
|
| 8192(>50 Pod 时 16384+) | 监控 Agent 密集场景 | 一 |
|
| 1 | 高并发短连接 | 一 |
|
| 30 | 高并发短连接 | 一 |
| kube-proxy | 32768 | 高并发 Service | 四 |
| kube-proxy | 131072 | 高并发 Service | 四 |
|
| static | 延迟敏感负载 | 五 |
|
| single-numa-node | NUMA 对齐 | 五 |
| THP | madvise | 内存密集型负载 | 五 |
| THP | defer+madvise | 内存密集型负载 | 五 |
| Memory QoS | TieredReservation | 内存敏感负载 | 彩蛋 |
📝 总结:OS 层调优的 5 条铁律
- 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 - cgroup v2 迁移前先检查 Java/Go 运行时版本,Java 8u372/11.0.16 以下必炸;containerd 和 kubelet 的 cgroup driver 必须一致;别忘了确认没有依赖 cgroup v1 特有子路径的服务
- overlayfs 底层推荐 XFS,核心原因是
ftype=1在格式化时即可确保;mount 加allocsize=4m - conntrack 用 kube-proxy 的
conntrack-min调,K8s 1.29+ 它会覆盖你的 sysctl 设置 - CPU 绑核必须同时满足 Guaranteed QoS 和整数 requests/limits,配合 NUMA 对齐才有效
你的节点上踩过哪些 OS 层的坑?有没有遇到过“K8s 调了半天最后发现是内核参数”的案例?评论区聊聊,说不定你踩的坑别人刚好也在踩。觉得有用的话,转给团队里管节点的那个人吧。
本文系统梳理Kubernetes集群在操作系统层的关键调优点,涵盖内核参数配置(如vm.swappiness、vm.overcommit_memory)、cgroup v2迁移要点(内存统计、Java/Go兼容性、swap行为)、overlayfs文件系统I/O优化(XFS格式化与mount参数)、conntrack表调优(kube-proxy优先配置)、CPU/NUMA对齐及透明大页(THP)策略,并详解cgroup v2软限制memory.high与v1.36新增的memoryReservationPolicy机制,强调OS层是K8s稳定性的根基。

366

被折叠的 条评论
为什么被折叠?



