
凌晨 2 点,香港将军澳机房的 Prometheus 告警把我从酒店床上惊醒,一台承载 60 多台 KVM 虚拟机的宿主机突然出现内存利用率异常飙升,几乎触顶。更糟糕的是,几台高并发 API 节点的 CPU 就绪时间也在指数级上升,业务延迟被用户截图发到群里。那一刻我才意识到:单纯在服务器上“开虚拟机”远不等于真正把硬件吃干榨尽。
这篇文章记录了我在 A5 数据香港双路 Xeon 6338N + 512 GB DDR4 + NVMe RAID 10 的宿主机上,通过内存优化与超线程(HT)精细化配置,将整体计算资源利用率从 68 % 提升到 91 % 的全过程。希望你能站在我的踩坑与验证结果之上,少走弯路。
1. 环境与基线测量
| 组件 | 规格 |
|---|---|
| 机型 | 2 × Intel Xeon Gold 6338N (40C80T) |
| 内存 | 512 GB DDR4-3200,4 × 8-channel NUMA 节点 |
| 存储 | 4 × 3.84 TB NVMe U.2 → RAID 10 (mdadm) |
| 网络 | 2 × 25 Gbps Intel E810 – LACP |
| Hypervisor | AlmaLinux 9.4 + kernel 5.14 + qemu-8.2 + libvirt-10 |
| 虚拟机 | 60 台(Web API、Redis、MySQL、副本节点等) |
初始 benchmark(sysbench + stress-ng)结果:
- 宿主机整体 CPU 利用率上限 ≈ 68 %(HT 开启)。
- 平均可用内存碎片率 26 %,导致频繁 page-fault & NUMA 跨节点访问。
- 个别高负载 VM 出现 CPU steal time > 12 %。
2. 内存优化:让每个字节都产生价值
2.1 NUMA 感知与亲和性
查硬件拓扑
lscpu --extended=cpu,node,socket,core | column -t | head
numastat -m
KVM 侧启用 numa=’on’,为大型 VM 声明 <numatune mode=’strict’ nodeset=’0-1’/>。
对数据库类 VM 做 HugePages + 跨 NUMA 引导,防止跨节点跳跃带来 ~12 ns 额外延迟。
2.2 HugePages / THP 双管齐下
静态 1 GiB HugePages:适用于 In-Memory DB、Java 堆。
/etc/default/grub 追加:
default_hugepagesz=1G hugepagesz=1G hugepages=64
动态 THP 仅对突发批处理 VM 开启:
echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
两种方案并存可兼顾吞吐与碎片率。
2.3 KSM + 内存重叠去重
开启 KSM 并把扫描速率调到 500 pages/s,仅对同构 Web front VM 应用:
echo 1 > /sys/kernel/mm/ksm/run
echo 500 > /sys/kernel/mm/ksm/pages_to_scan
echo 50 > /sys/kernel/mm/ksm/sleep_millisecs
KSM 与 HugePages 互斥,对数据库/缓存 VM 设置 <memory mode=’locked’/> 避免被 KSM 扫描。
2.4 Balloon & Overcommit 策略
内核参数:
vm.overcommit_memory = 1
vm.overcommit_ratio = 140
- 结合 virtio-balloon,允许宿主机在低峰期回收 ~20 % 内存,用于 CI/CD 临时 VM。
- qemu-guest-agent + libvirt auto-balloon:高峰前 5 分钟预热脚本取消 balloon,避免抖动。
2.5 Swap / zswap / zram
关闭传统机械盘 Swap,改用 zram-swap 8 GB / node:
systemctl enable --now zram-generator
启用 zswap(压缩 LRU)并指定 lz4:
echo lz4 > /sys/module/zswap/parameters/compressor
echo 1 > /sys/module/zswap/parameters/enabled
结果:在压力测试中吞吐下降 < 3 %,而内存溢出告警消失。
2.6 监控指标与自动告警
Node Exporter 自定义字段:ksm_pages_sharing、vm_memory_balloon。
Grafana 告警:当 node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes < 0.10 并且 ksm_merge_across_nodes == 0 时推送钉钉。
3. 超线程策略:刀口向内的 CPU 调度学
3.1 BIOS 层面的 HT 开启/关闭准则
高并发短任务(Nginx、Envoy):HT 收益大,保留。
高 IPC 负载(数据库、AI 推理):禁用 HT,可减少竞态与 cache 污染。
grubby --update-kernel=ALL --args="noht"
或在 BIOS 设置 Hyper-Threading = Disable。
3.2 vCPU 拓扑透传与 sibling 隔离
在 libvirt XML 中:
<cpu mode='host-passthrough'>
<topology sockets='2' dies='1' cores='20' threads='2'/>
<feature policy='require' name='hypervisor'/>
</cpu>
<vcpu placement='static' cpuset='0-15,40-55'/>
视业务需求决定 threads=’1’(禁用 HT)或 threads=’2’。
使用 hw_scheduler.allow_smt 控制虚机是否可见 sibling。
3.3 vCPU Pinning 与 cgroup 调度隔离
virsh vcpupin db01 0 0
virsh vcpupin db01 1 40
systemd-run --unit db01-isolate --property="AllowedCPUs=0,40" --scope
Noisy Neighbor 现象可通过 isolcpus= + rcu_nocbs= 移除内核 RCU 干扰。
对 GPU 直通 VM,用 taskset 锁定在同一物理核心的 sibling,保证 GPU-CPU 低延迟通信。
3.4 Tuned & Scheduler 微调
tuned-adm profile throughput-performance
echo 1 > /sys/devices/system/cpu/smt/active # 动态开关 SMT
echo 50 > /proc/sys/kernel/sched_wakeup_granularity_ns
- C-states 限制到 C1 防抖动;
- deadline I/O 调度器绑定数据库盘。
4. 实战步骤:在 KVM / QEMU + libvirt 中的落地
批量生成 HugePages
for node in /sys/devices/system/node/node*/hugepages/hugepages-1048576kB/nr_hugepages; \
do echo 16 > $node; done
创建带 NUMA 拓扑的 VM
virt-install \
--name web01 --memory 4096,maxmemory=8192,hugepages=1G \
--vcpus 2,maxvcpus=4,placement=static,cpuset=2,42 \
--numa nodeset=0 \
--cpu host-passthrough
动态调整 balloon
virsh qemu-monitor-command web01 '{"execute":"balloon","arguments":{"value":2097152}}'
Run workload & compare
stress-ng --cpu 4 --vm 2 --vm-bytes 80% -t 300
5. 性能验证与故障排查
| 指标 | 调优前 | 调优后 |
|---|---|---|
| 宿主机平均 CPU 利用率 | 68 % | 91 % |
| VM 平均 steal time | 12.3 % | 3.1 % |
| 内存碎片率 | 26 % | 5 % |
| P95 API 延迟 | 42 ms | 24 ms |
| 母机 OOM 事件 | 每周 1 次 | 0 |
若仍出现瓶颈,可按顺序检查:
- timestamp=… page allocation failure → HugePages 不足。
- sched: DL bw % → 调度器超售,重新 pin 或降级 HT。
- ksmd: MEMDUP → KSM 速率过高。
- cat /proc/sched_debug | grep rq → Run-queue 拥堵,考虑 migrate task。
6. 经验教训与后续迭代
- 在 BIOS 手动禁用 HT 比简单 noht 内核参数更干净,可额外降低 0.5 µs 的上下文切换延迟。
- HugePages 与 KSM 不要混用;前者迁移成本高但 TLB 命中率好,后者适合无状态微服务。
- Balloon 不是万金油;过度回收会让 Guest 页缓存失效,致 IO 抖动。
- 监控先行:没有 node_schedstat、node_cpu_seconds_total 数据的告警,是在帮自己挖坑。
- 下一步计划在 Kernel 6.9 + EPP Balance_Performance 上尝试 Core Scheduling,把同级 vCPU 隔离到同一 SMT 域内,提高 L1D 命中率。
从凌晨的紧急告警,到彻底抠出每一份内存与每一根逻辑线程的价值,我在香港这台 512 GB 大内存宿主机上学到的最大教训是:虚拟化的资源瓶颈往往是“看不见摸得着”的。只有让 NUMA、HugePages、Balloon、HT 这些底层细节与业务模型互相配合,才能真正把 SLA 写进硬件基因里。如果你也在香港或其他节点运营高密度虚拟化集群,希望这份笔记能让你的报警短信更少、睡眠更长。祝一路高帧率!











