内存分配错误无法创建容器异常排查¶
最近 使用Helm 3在Kubernetes集群部署Prometheus和Grafana 完成生产环境的监控部署,在一次 更新Kubernetes集群的Prometheus配置 意外发现需要更新的pod没有正常替换( 原本是准备 kube-prometheus-stack 持久化卷 )。检查pods状态:
# kubectl get pods -o wide | grep prometheus | grep -v exporter
alertmanager-kube-prometheus-stack-1681-alertmanager-0 2/2 Running 1 7d3h 10.233.76.51 i-2zeav45krsh6sr8t9r4q <none> <none>
kube-prometheus-stack-1681-admission-create-z7p94 0/1 ContainerCreating 0 14h <none> i-2zeav45krsh6sr8t9r4q <none> <none>
kube-prometheus-stack-1681-operator-749d66d8d4-xjnrn 1/1 Running 0 11d 10.233.76.31 i-2zeav45krsh6sr8t9r4q <none> <none>
kube-prometheus-stack-1681388621-grafana-7df74fffd4-86v54 3/3 Running 0 11d 10.233.76.35 i-2zeav45krsh6sr8t9r4q <none> <none>
kube-prometheus-stack-1681388621-kube-state-metrics-598b99r4zfw 0/1 ContainerCreating 0 4d13h <none> i-2zeav45krsh6sr8t9r4q <none> <none>
kube-prometheus-stack-1681388621-kube-state-metrics-67cb67w6g76 1/1 Running 0 5d19h 10.233.76.60 i-2zeav45krsh6sr8t9r4q <none> <none>
prometheus-kube-prometheus-stack-1681-prometheus-0 0/2 Init:RunContainerError 0 11d 10.233.76.33 i-2zeav45krsh6sr8t9r4q <none> <none>
检查容器创建失败原因:
# kubectl describe pods kube-prometheus-stack-1681-admission-create-z7p94
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedCreatePodContainer 2m7s (x4132 over 14h) kubelet unable to ensure pod container exists: failed to create container for [kubepods besteffort pod3c701e3a-1cc8-4807-a96d-425fa37d979a] : mkdir /sys/fs/cgroup/memory/kubepods/besteffort/pod3c701e3a-1cc8-4807-a96d-425fa37d979a: cannot allocate memory
# kubectl describe pods kube-prometheus-stack-1681388621-kube-state-metrics-598b99r4zfw
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedCreatePodContainer 2m6s (x30278 over 4d13h) kubelet unable to ensure pod container exists: failed to create container for [kubepods besteffort podeb6abc31-be1d-46a3-b93e-4230893ac649] : mkdir /sys/fs/cgroup/memory/kubepods/besteffort/podeb6abc31-be1d-46a3-b93e-4230893ac649: cannot allocate memory
可以看到两个替换pods的容器创建失败都是由于内存分配失败导致 cannot allocate memory
由于正在走 kube-prometheus-stack 持久化卷 ,一旦切换就会出现 Grafana通用可视分析平台 数据丢失,需要从备份中恢复,所以断开时间较长。非常巧,这个卡住并不影响 Prometheus配置(文件) 更新,只是容器不能重建导致状态一直显示不正常,虽然一切功能运行正常。
模仿 kubernetes
的 events
中内容手工创建cgroup目录确实报错:
# mkdir /sys/fs/cgroup/memory/kubepods/besteffort/podeb6abc31-be1d-46a3-b93e-4230893ac649
mkdir: cannot create directory ‘/sys/fs/cgroup/memory/kubepods/besteffort/podeb6abc31-be1d-46a3-b93e-4230893ac649’: Cannot allocate memory
但是非常奇怪的是,通过 top
观察可以看到系统内存充足:
Tasks: 365 total, 1 running, 364 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.9 us, 0.2 sy, 0.0 ni, 98.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 25982569+total, 19016694+free, 10491360 used, 59167380 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 24839529+avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
21993 admin 20 0 42.7g 4.9g 1.4g S 23.6 2.0 4510:43 prometheus
...
检查 slab
# ls /sys/kernel/slab | wc -l
182210
检查 slabtop
:
slabtop -s -c
解决方法¶
Kubernetes Cannot Allocate Memory | Solutions Revealed. 提供了相关信息和解决方案:
上述报错是因为
cgroup memory allocation
无法分配内核
3.10.0-1062.4.1
或更新版本解决了slab
泄漏问题,这个slab leak
会导致kmem control group
奔溃(不过,也有讨论提到高于3.10.0-1160.36.2.el7.x86 64
还是存在问题)
我的服务器内核版本确实是 3.10.0-1160.83.1.el7.x86_64
,看起来确实存在这个 slab leak
问题
解决方法有两个:
方法一¶
配置内核参数
cgroup.memory=nokmem
(修订/etc/default/grub
的GRUB_CMDLINE_LINUX
行配置)修改grub并重启:
grub2-mkconfig -o /boot/grub2/grub.cfg reboot
方法二¶
升级内核
我检查了我的服务器,当前操作系统版本:
# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)
但是如果不做大版本升级,则内核版本升级也只有 3.10.0-1160.88.1.el7
: 已检查 mirrors.163.com ,CentOS 7.9.2009 已经是最高的7系列版本,从仓库能够找到的最新内核版本也只有 3.10.0-1160.88.1.el7
,这已经是 2023年3月8日的更新版本
Red Hat官方解决方案¶
根据Red Hat官方知识库 The kmem enabled memory cgroup cannot be fully released unless all associated kmem_cache are "destroyed" 提供的解决方案:
如果你的工作负载环境经常遇到这个问题,则建议升级到 RHEL 8.1 版本,内核补丁已经修复了这个问题
对于RHEL7系统(RHEL 7.7及更高版本),需要在内核命令行参数添加
cgroup.memory=nokmem
来禁止内核内存记账功能( disable the kernel memory accounting )目前RedHat内部私有bug修复补丁正在调研 BZ#1925619 (需要注意RHEL7已经进入Maintenance phase II, RedHat不保证这个bug一定修复)
备注
根因¶
在分层记账(hierarchical accounting)的上下文(context)中,根据 mm/memcontrol.c
中的逻辑,只有在所有未决的内核内存记账(关联的slab对象)被删除以后,内存控制的cgroup才会被正确删除。即一旦页面被释放,并且相应的 kmem_cache
(slab) 被销毁。此时,分配给每个内存控制的 cgroup ( memcg-ID
)的唯一标识符在最后一次引用计数下从 "私有IDR" (private IDR)中删除。所以,确实有可能在正常运行时消耗掉整个IDR。此时遇到 mkdir
返回 -ENOMEM
(或无法分配内存),即使同一层次结构中的
cgroup
数量表明依然有可用空间(以创建额外的内存控制cgroup)。
这个问题的已知/适当补丁解决方案是将 kmem
记账移动/重新设置为父内存cgroup(RHEL 8.1补丁);但是RHEL7缺乏先决的几个补丁来修改内存记账基础架构,以便能够重新设置内存记账,所以这个补丁不太可能移植到RHEL 7。
我的修复¶
我参考Red Hat官方方法: 首先升级CentOS 7.9操作系统(但是由于生产限制不能升级到8.1版本),然后修改内核参数添加 cgroup.memory=nokmem
关闭内存记账功能,并重启服务器