在CentOS服务器上运行多个服务时,某个进程因代码缺陷或流量突增而耗尽CPU或内存,会拖垮整台机器。通过系统自带的限制机制,可以把每个服务的资源占用框定在安全范围内,避免单点故障扩散。

一、CentOS资源限制的核心机制
CentOS主要依赖两种底层能力来约束进程资源。其一是cgroup(控制组),它由内核提供,将进程分组后对相关组的CPU、内存、块设备IO等设上限;其二是systemd,作为初始化系统,它把cgroup封装成易用的单元配置项,让我们不必直接操作虚拟文件系统。
在CentOS 7中,默认启用cgroup v1,资源控制接口挂在/sys/fs/cgroup下,按子系统分目录。CentOS 8及后续版本逐渐切换到cgroup v2,统一以/sys/fs/cgroup/unified呈现,并且systemd对其支持更完整。理解版本差异,是写对配置的前提,否则可能遇到配置不生效却无报错的情况。
1.1 cgroup与systemd的关系
systemd在启动服务时,会为该服务建立一个专属的cgroup路径,例如/system.slice/nginx.service。我们写在unit文件里的资源指令,实际被翻译成对该cgroup节点的文件写入。也就是说,systemd是cgroup的用户态管理器,避开了人工echo值进文件的繁琐与风险。
这种架构好处是策略随服务生命周期走:服务停止,限制自动解除;服务重启,限制重新加载。比起用ulimit在shell里临时设,或者写定时脚本监控杀进程,要稳健得多。不过要注意,ulimit限制的是用户会话级资源,如打开文件数,而cgroup限制的是群体级的物理资源,两者互补而非替代。
二、用systemd配置服务资源限制
最直观的做法是在/usr/lib/systemd/system/下的服务单元文件中,添加[Service]段的资源控制指令。下面以限制Nginx为例,展示常用参数。
[Unit] Description=The nginx HTTP and reverse proxy server After=network.target [Service] Type=forking PIDFile=/run/nginx.pid ExecStart=/usr/sbin/nginx ExecReload=/bin/kill -s HUP $MAINPID ExecStop=/bin/kill -s QUIT $MAINPID # 以下为资源限制策略 MemoryMax=512M CPUQuota=50% TasksMax=256 IOWeight=100 [Install] WantedBy=multi-user.target
上面配置中,MemoryMax限定Nginx及其子进程最多用五百一十二兆内存,超出会被系统根据策略终止或回收;CPUQuota设成百分之五十,代表最多占满半个核心的计算力;TasksMax限制线程与进程总数,防fork炸弹;IOWeight调磁盘IO优先级。改完文件需执行systemctl daemon-reload才能生效。
如果服务已经在跑,不想重写文件,也可以用命令动态改限。例如systemctl set-property nginx.service MemoryMax=400M,这条指令会把限制写入/run/systemd/system/下的运行时配置,立即约束住当前进程树。重启后若未加--runtime参数,该属性还会持久化,方便紧急止血。
2.1 Java进程的特殊处理
Java应用常因堆外内存和线程数失控吃光资源。除了在systemd里设MemoryMax,还应配合JVM参数,如-Xmx设堆上限,避免JVM自己申请的比cgroup允许的多。否则cgroup触发OOM Killer时,可能连日志都没留下进程就没了。
[Service] ExecStart=/usr/bin/java -Xmx1g -XX:+UseG1GC -jar /opt/app.jar MemoryMax=1400M CPUQuota=80%
这里MemoryMax比堆大四百兆,留给元空间、线程栈等堆外部分。若只设-Xmx而忘设MemoryMax,容器或系统层面依旧可能超用。用systemctl show app.service可核对实际生效值,确认LimitMEM与cgroup当前上限一致。
三、直接操作cgroup的底层方式
当systemd指令不够细,或需批量管理非systemd拉起的进程时,可直接写cgroup文件系统。以下是在cgroup v1环境手动建组限制内存的示例。
# 创建名为limit_app的内存控制组 mkdir /sys/fs/cgroup/memory/limit_app # 设最大内存为300MB echo 314572800 > /sys/fs/cgroup/memory/limit_app/memory.limit_in_bytes # 将当前shell及后续子进程加入该组 echo $$ > /sys/fs/cgroup/memory/limit_app/tasks上述命令把当前进程塞进limit_app组,它及子孙都不能超三百兆。这种方式灵活,但机器重启就丢,需靠脚本或systemd unit的ExecStartPre来重建。生产环境推荐优先用systemd,手动cgroup适合调试与临时压测。
3.1 常见误区与排查
有人以为在/etc/security/limits.conf里写了nofile就能限内存,这是概念混淆。limits.conf走PAM,只影响会话级软硬上限;真正卡死物理资源的是cgroup。排查时可读/proc/进程号/cgroup看进程归属,再用systemctl status服务名观察资源图,避免调错层面。
另一个坑是CentOS 8的cgroup v2里,内存限制文件名变成memory.max而非memory.limit_in_bytes,直接用老路径会报无文件。迁移系统时要注意这套命名变化,或统一用systemd接口屏蔽底层差异。
四、策略落地建议
实际运维中,应先梳理每台机上的服务清单与资源画像,再按重要性分级设限。前端代理类给够CPU少给内存,计算类反之。所有限制写进systemd unit受版本控制,禁止纯手工echo,保证环境间一致。
定期用systemd-cgtop观察各组占用,发现某组长期触顶,说明配额不合理或程序有泄漏。结合日志与OOM记录,迭代阈值。这样CentOS的资源安全策略就不是摆设,而是真实拦住故障的护栏。