上一篇 下一篇 分享链接 返回 返回顶部

香港服务器运行Ubuntu 20.04时,如何调优cgroups与systemd以提升容器化环境的资源隔离效率?

发布人:Minchunlin 发布时间:2025-08-16 09:38 阅读量:670


我第一次在香港机房上部署容器化业务时,遇到的场景和大多数人差不多:一台裸机服务器,Ubuntu 20.04 系统,KVM 虚拟化之上跑 Docker 与 Kubernetes 节点,硬件配置是 双路 Intel Xeon Silver 4310(2.1GHz,12C/24T)、256GB DDR4 ECC 内存、4块 1.92TB NVMe SSD(组RAID10)、双万兆网卡。

刚上线时,业务跑得飞快,客户反馈延迟降低了不少。但随着容器数量增多,问题逐渐显现:某些批处理任务容器会瞬间吃掉 CPU 与 IO 带宽,其他 Web 服务容器则直接“卡死”。简单的 docker run --cpus 和 --memory 限制,效果并不理想,systemd 默认的资源调度策略也让部分容器抢占了系统进程的资源,最终导致宿主机本身都出现过一次 ssh 连接不上、现场只能强制重启的惨痛事故。

于是,我开始在 Ubuntu 20.04 上深入调优 cgroups v2 与 systemd,目标就是让容器化环境真正实现“资源隔离”,而不是“资源互殴”。

Step 1:确认 cgroups 版本与 systemd 配置

Ubuntu 20.04 默认启用的是 cgroups v1,而 Docker 与 K8s 在新版本内核(>=5.4)下已逐步支持 cgroups v2,它对 CPU/IO 内存隔离更细粒度。
我首先检查系统状态:

grep cgroup /proc/filesystems

发现还是挂载了 cgroup v1,于是我修改 grub:

vim /etc/default/grub
# 增加如下参数
GRUB_CMDLINE_LINUX="systemd.unified_cgroup_hierarchy=1 systemd.legacy_systemd_cgroup_controller=0"

然后执行:

update-grub && reboot

重启后验证:

mount | grep cgroup2

输出 /sys/fs/cgroup 挂载为 cgroup2,说明 v2 已生效。

Step 2:systemd slice 的资源隔离

在生产中,光靠 Docker 自己的限制不够。因为宿主机上的 systemd 服务(比如 sshd、systemd-journald)和容器属于同一层级,容易出现竞争。

我做的第一步是为容器创建独立的 systemd slice,例如:

systemctl edit docker.service

增加:

[Service]
Slice=container.slice

然后在 /etc/systemd/system/container.slice 定义 slice 的配额:

[Slice]
CPUQuota=80%
MemoryMax=200G
IOReadBandwidthMax=/dev/nvme0n1 500M
IOWriteBandwidthMax=/dev/nvme0n1 300M

这样,所有 Docker 容器运行在 container.slice 下,保证宿主机留有 20% CPU 和 56GB 内存做“紧急生存空间”。

Step 3:容器内更细粒度的 cgroup 控制

Docker 本身支持 --cpus、--memory 参数,但我发现生产中仍然不够精准。比如 CPU 抢占问题,可以通过 CPUWeight 来解决:

systemctl set-property docker.service CPUWeight=500


这里的范围是 1~10000,值越大优先级越高。我在实际调优时做过实验:

容器类型 CPUWeight MemoryHigh IOThrottle
Web API服务容器 2000 4G 读 200M/s
数据分析容器 800 32G 读 100M/s
日志收集容器 500 8G 读 50M/s

结果很明显:Web 容器在高峰期依旧能快速响应,而分析型容器被“温柔”地限制住,不会再拖垮全局。

Step 4:现场遇到的坑与解决过程

内存突刺问题

一开始我用 MemoryMax 硬限制,结果某个 Java 服务容器直接 OOM,业务中断。后来改用 MemoryHigh 设置软限制,配合 oomd 做优雅杀进程,才解决了“暴力 OOM”的问题。

示例:

systemctl set-property docker.service MemoryHigh=180G MemoryMax=200G

IO 限流失效

我曾在容器中跑 fio 测试,发现 IO 限制没生效。最后查到是因为 cgroups v2 下需要明确设备映射,必须用 IOReadBandwidthMax=/dev/nvme0n1,而不能写 /。这个坑在生产环境里排查了两小时。

K8s 与 systemd 的调度冲突
在 kubelet 部署的节点上,K8s 的 --cgroup-driver=systemd 必须和宿主保持一致,否则 Pod 配额设置不起作用。这个地方我在凌晨调试过,差点被同事喊出来背锅。

Step 5:效果验证

调优后,我跑了一次压力测试:

Web 容器并发 10k 请求/秒

分析容器同时执行 200GB parquet 文件的 Spark job

日志容器每秒写入 20k log events

监控数据如下:

指标 调优前 调优后
Web容器响应P99 350ms 90ms
系统load平均值 42.5 23.1
磁盘延迟 (ms) 18~25 5~8
SSH可用性 偶发丢失连接 稳定可登陆

那一刻,我在机房里看着 Grafana 曲线稳定下来,心里才真正松了一口气。

总结:运维的“温柔刀”

这次在香港机房的 Ubuntu 20.04 调优经历,让我体会到 cgroups v2 与 systemd 的真正威力。过去我们总说容器是“轻量级虚拟化”,但如果底层资源控制没到位,它就是“轻量级灾难”。

通过合理划分 slice、利用 CPUWeight、MemoryHigh、IO 限制等机制,我把容器环境从一盘散沙变成了一个有序的“资源调度系统”。这并不是一劳永逸的配置,而是一个不断试错、实时监控、灵活调整的过程。

每次走进机房,听着风扇轰鸣、盯着亮闪闪的服务器指示灯,我都在提醒自己:运维从来不是冷冰冰的参数配置,而是一场和硬件、系统、业务的博弈。真正的高手,不是写下一堆最佳实践,而是能在凌晨三点、所有人都指望着你的时候,让系统安然无恙地挺过去。

目录结构
全文