告别虚高的 Load Average:在传统虚拟机集群中玩转 PSI 压力预警与轻量级调度
7
0
0
0
在云原生时代,大家都在谈论 Kubernetes 的资源隔离和自动扩缩容,但实际上,仍有大量公司的业务跑在传统的虚拟机(VM)或物理机集群上。
在这种环境下,很多运维同学会遇到一个经典痛点:Load Average 飘高,但系统响应似乎还行;或者 Load Average 不高,但某个关键 I/O 操作已经卡死了。 这是因为传统的负载均值混淆了 CPU、I/O 甚至不可中断进程。
其实,只要你的内核版本在 4.20+,完全可以抛开 K8s,利用 Linux 内核自带的 PSI(Pressure Stall Information) 实现极其精准的资源预警和动态调度。
1. 为什么在虚拟机里也要用 PSI?
PSI 将压力分为两种状态:
- some:表示部分任务因为该资源(如内存)被阻塞,但 CPU 还能干点别的。
- full:表示所有非空闲任务都因为该资源被卡住了,这是真正的系统瓶颈。
在虚拟机环境下,PSI 的价值在于:
- 精准区分瓶颈:一眼看出是磁盘 I/O 慢了,还是内存页回收太频繁。
- 提前介入自愈:在 OOM 发生前,根据
memory full指标主动杀掉低优先级进程或清理缓存。 - 轻量级调度策略:如果本机压力过大,脚本自动修改 Nginx 权重或停止 Cron Job。
2. 核心原理:解析 /proc/pressure/
在 Linux 中,PSI 信息直接通过文件暴露:
/proc/pressure/cpu/proc/pressure/io/proc/pressure/memory
文件内容示例:
some avg10=0.10 avg60=0.05 avg300=0.01 total=123456
full avg10=0.02 avg60=0.01 avg300=0.00 total=12345
我们关注的是 avg10(过去 10 秒的停滞百分比)。如果 memory full avg10 超过了 10,说明系统已经有 10% 的时间完全卡在内存申请上了。
3. 轻量级调度与预警脚本示例
下面是一个基于 Python 的轻量级守护脚本示例。它的逻辑非常简单:监控 PSI 指标,一旦超过阈值,执行自定义的“降级”操作(如停止本地日志压缩任务或发送告警)。
import time
import os
# 配置阈值:10秒内的平均停滞百分比
THRESHOLD = {
'cpu_some': 50.0, # CPU 压力超过 50%
'mem_full': 5.0, # 内存完全阻塞超过 5% (高危)
'io_full': 20.0 # I/O 完全阻塞超过 20%
}
def get_psi_metric(resource, type='some'):
"""解析 /proc/pressure 文件"""
path = f"/proc/pressure/{resource}"
if not os.path.exists(path):
return 0.0
with open(path, 'r') as f:
for line in f:
if line.startswith(type):
# 提取 avg10 的值
parts = line.split()
avg10 = parts[1].split('=')[1]
return float(avg10)
return 0.0
def handle_overload(reason):
"""
触发过载处理逻辑
这里可以根据实际需求改为:
1. 修改 Nginx upstream 状态
2. 杀掉吃内存的临时进程
3. 发送钉钉/企业微信告警
"""
print(f"[ALERT] 系统资源过载: {reason} | 执行降级策略...")
# 示例:如果是生产环境,可以执行 os.system("systemctl stop some-low-priority-job")
def main():
print("PSI Monitor Started. Target Kernel: 4.20+")
while True:
# 检测内存压力
mem_stall = get_psi_metric('memory', 'full')
if mem_stall > THRESHOLD['mem_full']:
handle_overload(f"Memory Stall Full ({mem_stall}%)")
# 检测 CPU 压力
cpu_stall = get_psi_metric('cpu', 'some')
if cpu_stall > THRESHOLD['cpu_some']:
handle_overload(f"CPU Stall Some ({cpu_stall}%)")
# 每 5 秒轮询一次
time.sleep(5)
if __name__ == "__main__":
main()
4. 进阶玩法:结合 Systemd 资源控制
如果你想在虚拟机上实现类似 K8s 的效果,可以将上述脚本与 cgroup v2 结合。
- 设置 cgroup 监控:在
/sys/fs/cgroup/下,每个子系统也有自己的cpu.pressure。你可以监控特定业务进程所属的 cgroup。 - 配合触发器(Inotify):Linux PSI 实际上支持
poll()系统调用。你可以通过监听文件描述符,在压力超过阈值的毫秒级内获得内核通知,而不是像上面脚本那样循环轮询。这正是 K8soom-kill逻辑的底层实现原理。
5. 总结
即便没有 K8s,PSI 也是现代 Linux 运维的“神兵利器”。
- 如果你有 Prometheus:可以直接使用
node_exporter,它默认已经采集了 PSI 指标,你只需要在 Grafana 上配置node_pressure_cpu_waiting_percent等告警规则即可。 - 如果你需要本地自愈:参考上面的 Python 逻辑,写一个 100 行以内的守护进程,就能比传统的
top/uptime监控快得多、准得多。
避坑指南:如果你的 /proc/pressure 目录下是空的,请检查内核版本,并确保启动参数中没有禁用 PSI(部分发行版需要检查 CONFIG_PSI=y)。