k8s node cgroup 泄露如何优化?
在 Kubernetes 中,cgroup 泄露会导致节点资源耗尽、容器调度失败甚至整个节点崩溃。以下是针对 cgroup 泄露的优化策略和解决方案:
一、快速诊断 cgroup 泄露
常规方法
1. 检查 cgroup 挂载点
# 查看 cgroup v1 挂载点
mount | grep cgroup
# cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)# 查看 cgroup v2 挂载点
mount | grep cgroup2
# cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate)
2. 检查 orphan cgroup 目录
# 查找无对应 PID 的 cgroup 目录(v1 示例)
find /sys/fs/cgroup/cpu/kubepods/burstable -type d -empty# 查找无对应 PID 的 cgroup 目录(v2 示例)
find /sys/fs/cgroup/kubepods.slice/kubepods-burstable.slice -type d -empty
3. 使用工具监控 cgroup 资源
# 安装 cgroup-tools
apt install cgroup-tools # Ubuntu/Debian
yum install libcgroup-tools # CentOS/RHEL# 查看 cgroup 资源使用情况
cgroup-tree -a
高级技术手段
除了上述方法,还有哪些方式可以诊断cgroup泄露?
除了常规方法外,诊断 cgroup 泄露还可以通过以下 高级技术手段 和 工具链组合 实现:
内核层面诊断
1)使用 cgroup_events
监控(需内核支持)
# 挂载 debugfs(如未挂载)
mount -t debugfs none /sys/kernel/debug# 启用 cgroup 事件监控
echo 1 > /sys/kernel/debug/cgroup/events_enabled# 查看 cgroup 创建/销毁事件
cat /sys/kernel/debug/cgroup/events
2)内核参数调试
# 启用 cgroup v2 详细日志
echo "cgroup_no_v1=all" >> /etc/default/grub
grub-mkconfig -o /boot/grub/grub.cfg# 重启后查看内核日志
dmesg | grep -i cgroup
BPF 动态追踪
1)使用 bpftrace
追踪 cgroup 操作
# 安装 bpftrace
apt install bpftrace # Ubuntu/Debian
yum install bpftrace # CentOS/RHEL# 追踪 cgroup 创建事件
bpftrace -e '
kprobe:cgroup_attach_task {printf("New cgroup task: pid=%d, cgroup=%s\n", pid, str(args->cgrp->kn->name));
}'# 追踪 cgroup 内存分配
bpftrace -e '
kprobe:mem_cgroup_try_charge {printf("Memory allocation: pid=%d, bytes=%d\n", pid, args->bytes);
}'
2)使用 BCC 工具集
# 示例:追踪 orphan cgroup 目录
from bcc import BPFbpf_text = """
#include <linux/cgroup.h>// 追踪 cgroup 释放
int trace_cgroup_release(struct ptrace_regs *ctx) {struct cgroup *cgrp = (struct cgroup *)PT_REGS_PARM1(ctx);bpf_trace_printk("Cgroup released: %s\\n", cgrp->kn->name);return 0;
}
"""b = BPF(text=bpf_text)
b.attach_kprobe(event="cgroup_release", fn_name="trace_cgroup_release")
print("Tracing cgroup releases... Ctrl+C to exit.")
b.trace_print()
容器运行时诊断
1)containerd 调试命令
# 查看所有容器状态(包括已退出但未清理的)
ctr -n k8s.io containers ls# 检查 orphan 容器沙箱
ctr -n k8s.io sandboxes ls# 强制清理所有已退出容器
ctr -n k8s.io containers rm $(ctr -n k8s.io containers ls -q)
2)Docker 调试命令
# 查看所有容器(包括已退出的)
docker ps -a# 检查 orphan 容器
docker ps -a | grep "Exited"# 清理所有已退出容器
docker container prune
系统资源分析
1)使用 systemd-cgls
查看 cgroup 树
# 查看完整 cgroup 层级
systemd-cgls# 查看特定 cgroup 下的进程
systemd-cgls /kubepods.slice/kubepods-burstable.slice
2)使用 lsof
检查文件描述符
# 查找指向 cgroup 文件的打开描述符
lsof | grep cgroup# 示例输出:
# kubelet 12345 root 255r FIFO 0,10 0t0 123456 /sys/fs/cgroup/memory/kubepods/burstable/pod1234-5678-9abc-def0/memory.usage_in_bytes
性能分析工具
1)使用 perf
分析 cgroup 相关系统调用
# 记录 cgroup 相关系统调用
perf record -a -g -e 'cgroup*' sleep 30# 生成调用图
perf report --stdio
2)使用 strace
追踪特定进程
# 追踪 kubelet 的 cgroup 操作
strace -p $(pgrep kubelet) -e 'cgroup_*' -f -o /tmp/kubelet_cgroup.log
自定义监控脚本
1)监控 cgroup 目录数量增长
#!/bin/bash# 监控 cgroup 目录数量变化
BASE_DIR="/sys/fs/cgroup"
INTERVAL=60while true; doCOUNT=$(find $BASE_DIR -type d | wc -l)echo "$(date): $COUNT cgroup directories"sleep $INTERVAL
done
2)检测 orphan cgroup 目录
#!/usr/bin/env python3
import os
import subprocessdef find_orphan_cgroups(base_dir):orphan_cgroups = []for root, dirs, files in os.walk(base_dir):for d in dirs:cgroup_path = os.path.join(root, d)procs_file = os.path.join(cgroup_path, "cgroup.procs")if os.path.exists(procs_file):with open(procs_file, "r") as f:if not f.read().strip():orphan_cgroups.append(cgroup_path)return orphan_cgroups# 示例:检查 kubepods 下的 orphan cgroups
orphans = find_orphan_cgroups("/sys/fs/cgroup/kubepods")
print(f"Found {len(orphans)} orphan cgroups:")
for cg in orphans:print(cg)
容器编排层面诊断
1)使用 kubectl describe node
查看节点状态
kubectl describe node <node-name> | grep -i cgroup
2)检查 kubelet 日志
journalctl -u kubelet | grep -i cgroup
3)分析事件和条件
kubectl get nodes <node-name> -o yaml | grep -A 20 conditions
内存泄露检测工具
1)使用 pmap
分析进程内存
# 查看 kubelet 内存映射
pmap -x $(pgrep kubelet) | tail -n 10# 检查是否有大量 cgroup 相关内存占用
2)使用 valgrind
检测内存泄露(谨慎用于生产)
# 示例:检测容器运行时内存泄露
valgrind --leak-check=full --show-leak-kinds=all /usr/bin/containerd
云厂商特定工具
1)AWS EKS
# 使用 CloudWatch 日志监控 cgroup 指标
aws logs filter-log-events --log-group-name /aws/eks/<cluster-name>/cluster --filter-pattern "cgroup"
2)GKE
# 在 Stackdriver 中查询 cgroup 相关指标
gcloud logging read 'resource.type="k8s_node" AND textPayload:"cgroup"'
硬件层面排查
1)检查内存健康状态
# 运行内存检测工具
memtest86+
2)检查磁盘 I/O 性能
# 监控磁盘 I/O
iostat -x 1
通过上述方法,可以从多个维度诊断 cgroup 泄露问题,定位根本原因并采取针对性措施。
二、优化策略
1. 升级 Kubernetes 版本
- 升级到 1.24+ 版本,修复了多个 cgroup 资源管理问题。
- 启用 cgroup v2(推荐):
# 修改 kubelet 配置 cat > /etc/default/kubelet <<EOF KUBELET_EXTRA_ARGS="--cgroup-driver=systemd --cgroup-root=/sys/fs/cgroup" EOF# 重启 kubelet systemctl restart kubelet
2. 优化容器运行时配置
容器运行时:
- 使用 containerd 1.6+ 或 cri-o 1.24+,它们对 cgroup 资源回收更健壮。
- 配置
SystemdCgroup
为true
(containerd):# /etc/containerd/config.toml [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc][plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]SystemdCgroup = true
3. 调整内核参数
# 启用 cgroup v2
echo "systemd.unified_cgroup_hierarchy=1" >> /etc/default/grub
grub-mkconfig -o /boot/grub/grub.cfg# 优化内存回收
echo "vm.overcommit_memory=1" >> /etc/sysctl.conf
echo "vm.panic_on_oom=0" >> /etc/sysctl.conf
echo "vm.oom_kill_allocating_task=1" >> /etc/sysctl.conf
sysctl -p
4. 配置资源限制
确保所有 Pod 都设置了 requests
和 limits
:
apiVersion: v1
kind: Pod
metadata:name: my-pod
spec:containers:- name: my-containerimage: nginxresources:requests:cpu: "100m"memory: "128Mi"limits:cpu: "200m"memory: "256Mi"
5. 清理 orphan cgroup 目录
# 安全清理 orphan cgroup 目录(v1 示例)
for dir in $(find /sys/fs/cgroup/cpu/kubepods -type d -empty); doecho "Cleaning $dir"echo 1 > "$dir/notify_on_release"
done# 对于 cgroup v2,需确保没有进程使用该 cgroup 后删除
三、监控与告警
1. Prometheus + Grafana 监控
关键指标:
container_memory_usage_bytes
container_cpu_usage_seconds_total
kube_pod_container_resource_limits
2. 告警规则
# prometheus.rules
groups:
- name: cgroup.rulesrules:- alert: CgroupMemoryLeakexpr: increase(container_memory_usage_bytes{container!="POD", container!=""}[1h]) > 1e+9labels:severity: warningannotations:summary: "Cgroup memory leak detected in {{ $labels.namespace }}/{{ $labels.pod }}"
四、长期解决方案
启用节点自动修复:
- 使用 NodeProblemDetector 检测 cgroup 泄露并自动重启节点。
- 配置 Cluster Autoscaler 自动替换不健康节点。
容器生命周期管理:
- 确保容器优雅退出(处理 SIGTERM 信号)。
- 使用 preStop hook 清理资源:
lifecycle:preStop:exec:command: ["/bin/sh", "-c", "cleanup.sh"]
定期审计 cgroup 状态:
# 脚本示例:检查 cgroup 目录数量是否异常增长
COUNT=$(find /sys/fs/cgroup/kubepods -type d | wc -l)
if [ $COUNT -gt 10000 ]; thenecho "Warning: Too many cgroup directories ($COUNT)"# 触发告警或自动修复
fi
五、验证优化效果
# 监控节点 cgroup 目录数量
watch -n 60 'find /sys/fs/cgroup/kubepods -type d | wc -l'# 检查容器内存使用率
kubectl top pods --containers
通过以上措施,可显著降低 cgroup 泄露的风险,提升 Kubernetes 集群的稳定性和可靠性。