本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
在 AL2023 中使用 systemd 限制进程资源使用量
在 Amazon Linux 2023(AL2023)上,我们推荐使用 systemd 来控制进程或进程组可以使用的资源。使用 systemd 是一个强大且易于使用的替代方案,可以替代手动操作 cgroups 或使用诸如 cpulimit 之类的工具,后者先前仅在第三方 EPEL 存储库中可用于 Amazon Linux。
有关全面信息,请参阅上游 systemd 关于 systemd.resource-controlsystemd.resource-control 的 man 手册页。
以下示例将使用 stress-ng CPU 压力测试(来自 stress-ng 程序包)来模拟 CPU 密集型应用程序,并使用 memcached 来模拟内存密集型应用程序。
以下示例涵盖对一次性命令施加 CPU 限制和对服务施加内存限制。systemd 提供的大多数资源约束可以在任何 systemd 运行进程的地方使用,并且可以同时使用多个约束。为了说明目的,以下示例仅限于单一约束。
使用 systemd-run 对运行一次性命令进行资源控制
虽然通常与系统服务关联,但非 root 用户也可使用 systemd 来运行服务、调度定时器或运行一次性进程。在以下示例中,我们将使用 stress-ng 作为示例应用程序。在第一个示例中,我们将使用 systemd-run 在 ec2-user 默认账户中运行它;在第二个示例中,我们将对其 CPU 使用量施加限制。
例 在命令行使用 systemd-run 运行进程,不限制资源使用量
-
确保已安装
stress-ng程序包,因为我们将以其为例。[ec2-user ~]$sudo dnf install -ystress-ng -
使用
systemd-run执行一个 10 秒的 CPU 压力测试,不限制其可使用的 CPU 量。[ec2-user ~]$systemd-run --user --tty --wait --property=CPUAccounting=1stress-ng --cpu 1 --timeout 10Running as unit: run-u6.service Press ^] three times within 1s to disconnect TTY. stress-ng: info: [339368] setting to a 10 second run per stressor stress-ng: info: [339368] dispatching hogs: 1 cpu stress-ng: info: [339368] successful run completed in 10.00s Finished with result: success Main processes terminated with: code=exited/status=0 Service runtime: 10.068s CPU time consumed: 9.060s--user选项指示systemd-run以当前登录用户身份执行命令,--tty选项表示附加一个 TTY,--wait表示等待服务完成,--property=CPUAccounting=1选项指示systemd-run记录运行该进程所使用的 CPU 时间。--property命令行选项可用于传递可在systemd.unit配置文件中配置的systemd-run设置。
当被指示对 CPU 施加负载时,stress-ng 程序将在您要求的运行时间内使用所有可用的 CPU 时间执行其测试。对于实际应用,可能需要对进程的总运行时间设置限制。在以下示例中,我们将要求 stress-ng 运行的时间长于我们使用 systemd-run 为其设置的最长持续时间限制。
例 在命令行使用 systemd-run 运行进程,将 CPU 使用量限制为 1 秒
-
确保已安装
stress-ng以运行此示例。 -
LimitCPU属性等效于ulimit -t,它将限制该进程允许使用的 CPU 最长时间。在这种情况下,由于我们要求进行 10 秒的压力运行,但将 CPU 使用量限制为 1 秒,该命令将收到SIGXCPU信号并失败。[ec2-user ~]$systemd-run --user --tty --wait --property=CPUAccounting=1 --property=LimitCPU=1stress-ng --cpu 1 --timeout 10Running as unit: run-u12.service Press ^] three times within 1s to disconnect TTY. stress-ng: info: [340349] setting to a 10 second run per stressor stress-ng: info: [340349] dispatching hogs: 1 cpu stress-ng: fail: [340349] cpu instance 0 corrupted bogo-ops counter, 1370 vs 0 stress-ng: fail: [340349] cpu instance 0 hash error in bogo-ops counter and run flag, 3250129726 vs 0 stress-ng: fail: [340349] metrics-check: stressor metrics corrupted, data is compromised stress-ng: info: [340349] unsuccessful run completed in 1.14s Finished with result: exit-code Main processes terminated with: code=exited/status=2 Service runtime: 1.201s CPU time consumed: 1.008s
更常见的情况是,您可能希望限制特定进程可消耗的 CPU 时间百分比。在以下示例中,我们将限制 stress-ng 可消耗的 CPU 时间百分比。对于实际的服务,可能需要限制后台进程可消耗的 CPU 时间最大百分比,以便为处理用户请求的进程空出资源。
例 使用 systemd-run 将进程限制在单颗 CPU 10% 的 CPU 时间
-
确保已安装
stress-ng以运行此示例。 -
我们将使用
CPUQuota属性来告知systemd-run约束即将运行的命令的 CPU 使用量。我们不限制进程的运行时间,只限制其可以使用的 CPU 量。[ec2-user ~]$systemd-run --user --tty --wait --property=CPUAccounting=1 --property=CPUQuota=10%stress-ng --cpu 1 --timeout 10Running as unit: run-u13.service Press ^] three times within 1s to disconnect TTY. stress-ng: info: [340664] setting to a 10 second run per stressor stress-ng: info: [340664] dispatching hogs: 1 cpu stress-ng: info: [340664] successful run completed in 10.08s Finished with result: success Main processes terminated with: code=exited/status=0 Service runtime: 10.140s CPU time consumed: 1.014s请注意 CPU 统计信息显示,虽然服务运行了 10 秒,但它仅消耗了 1 秒的实际 CPU 时间。
有多种方法可以配置 systemd 来限制 CPU、内存、网络和 IO 的资源使用量。有关完整文档,请参阅上游 systemd 关于 systemd.resource-controlsystemd.resource-control 的 man 手册页。
在底层,systemd 利用 Linux 内核的特性(如 cgroups)来实现这些限制,同时避免了您手动配置的需要。Linux 内核关于 cgroup-v2cgroups 工作的详尽细节。
在 systemd 服务中进行资源控制
有多个参数可以添加到 systemd 服务的 [Service] 部分,以控制系统资源使用量。这些参数包括硬限制和软限制。关于每个选项的确切行为,请参考上游 systemd 关于 systemd.resource-controlsystemd.resource-control 的 man 手册页。
常用的限制包括:使用 MemoryHigh 指定内存使用的节流限制,使用 MemoryMax 设置硬性上限(一旦达到,将调用 OOM Killer),以及使用 CPUQuota(如前一节所示)。也可以配置权重和优先级,而不是固定的数值。
例 使用 systemd 为服务设置内存使用限制
在本示例中,我们将为 memcached(一个简单的键值缓存)设置内存使用硬限制,并展示 OOM Killer 是如何针对该服务而非整个系统被调用的。
-
首先,我们需要安装此示例所需的程序包。
[ec2-user ~]$sudo dnf install -ymemcached libmemcached-awesome-tools -
启用
memcached.service然后启动服务,使memcached运行。[ec2-user ~]$sudo systemctl enablememcached.serviceCreated symlink /etc/systemd/system/multi-user.target.wants/memcached.service → /usr/lib/systemd/system/memcached.service.[ec2-user ~]$sudo systemctl startmemcached.service -
检查
memcached.service是否正在运行。[ec2-user ~]$sudo systemctl statusmemcached.service● memcached.service - memcached daemon Loaded: loaded (/usr/lib/systemd/system/memcached.service; enabled; preset: disabled) Active: active (running) since Fri 2025-01-31 22:36:42 UTC; 1s ago Main PID: 356294 (memcached) Tasks: 10 (limit: 18907) Memory: 1.8M CPU: 20ms CGroup: /system.slice/memcached.service └─356294 /usr/bin/memcached -p 11211 -u memcached -m 64 -c 1024 -l 127.0.0.1,::1 Jan 31 22:35:36 ip-1-2-3-4.us-west-2.compute.internal systemd[1]: Started memcached.service - memcached daemon. -
既然
memcached已安装并运行,我们可以通过向缓存中插入一些随机数据来观察其功能在
/etc/sysconfig/memcached中,CACHESIZE变量默认设置为 64,即 64 兆字节。通过向缓存中插入超过最大缓存大小的数据,我们可以看到缓存被填满,并且使用memcached-tool驱逐了一些项目,同时memcached.service使用了大约 64MB 的内存。[ec2-user ~]$for i in $(seq 1 150); do dd if=/dev/random of=$i bs=512k count=1; memcp -s localhost $i; done[ec2-user ~]$memcached-tool localhost display# Item_Size Max_age Pages Count Full? Evicted Evict_Time OOM 2 120B 0s 1 0 no 0 0 0 39 512.0K 4s 63 126 yes 24 2 0[ec2-user ~]$sudo systemctl statusmemcached.service● memcached.service - memcached daemon Loaded: loaded (/usr/lib/systemd/system/memcached.service; enabled; preset: disabled) Active: active (running) since Fri 2025-01-31 22:36:42 UTC; 7min ago Main PID: 356294 (memcached) Tasks: 10 (limit: 18907) Memory: 66.7M CPU: 203ms CGroup: /system.slice/memcached.service └─356294 /usr/bin/memcached -p 11211 -u memcached -m 64 -c 1024 -l 127.0.0.1,::1 Jan 31 22:36:42 ip-1-2-3-4.us-west-2.compute.internal systemd[1]: Started memcached.service - memcached daemon. -
使用
MemoryMax属性为memcached.service设置一个硬限制,如果达到此限制,将调用 OOM Killer。可以通过将其他选项添加到覆盖文件来为服务设置它们。这可以通过直接编辑/etc/systemd/system/memcached.service.d/override.conf文件或使用systemctl的edit命令交互式完成。[ec2-user ~]$sudo systemctl editmemcached.service将以下内容添加到覆盖文件中,为服务设置 32MB 内存的硬限制。
[Service] MemoryMax=32M -
告诉
systemd重新加载其配置[ec2-user ~]$sudo systemctl daemon-reload -
观察
memcached.service现在正以 32MB 的内存限制运行。[ec2-user ~]$sudo systemctl statusmemcached.service● memcached.service - memcached daemon Loaded: loaded (/usr/lib/systemd/system/memcached.service; enabled; preset: disabled) Drop-In: /etc/systemd/system/memcached.service.d └─override.conf Active: active (running) since Fri 2025-01-31 23:09:13 UTC; 49s ago Main PID: 358423 (memcached) Tasks: 10 (limit: 18907) Memory: 1.8M (max: 32.0M available: 30.1M) CPU: 25ms CGroup: /system.slice/memcached.service └─358423 /usr/bin/memcached -p 11211 -u memcached -m 64 -c 1024 -l 127.0.0.1,::1 Jan 31 23:09:13 ip-1-2-3-4.us-west-2.compute.internal systemd[1]: Started memcached.service - memcached daemon. -
在使用少于 32MB 内存时,服务将正常运行,我们可以通过向缓存加载少于 32MB 的随机数据,然后检查服务状态来验证。
[ec2-user ~]$for i in $(seq 1 30); do dd if=/dev/random of=$i bs=512k count=1; memcp -s localhost $i; done[ec2-user ~]$sudo systemctl statusmemcached.service● memcached.service - memcached daemon Loaded: loaded (/usr/lib/systemd/system/memcached.service; enabled; preset: disabled) Drop-In: /etc/systemd/system/memcached.service.d └─override.conf Active: active (running) since Fri 2025-01-31 23:14:48 UTC; 3s ago Main PID: 359492 (memcached) Tasks: 10 (limit: 18907) Memory: 18.2M (max: 32.0M available: 13.7M) CPU: 42ms CGroup: /system.slice/memcached.service └─359492 /usr/bin/memcached -p 11211 -u memcached -m 64 -c 1024 -l 127.0.0.1,::1 Jan 31 23:14:48 ip-1-2-3-4.us-west-2.compute.internal systemd[1]: Started memcached.service - memcached daemon. -
我们现在可以通过尝试使用默认
memcached配置所允许的完整 64MB 缓存,使memcached使用超过 32MB 的内存。[ec2-user ~]$for i in $(seq 1 150); do dd if=/dev/random of=$i bs=512k count=1; memcp -s localhost $i; done您将观察到在上述命令的某个时刻,会出现连接到
memcached服务器的错误。这是因为 OOM Killer 由于我们施加的限制而终止了该进程。系统的其余部分将正常运行,OOM Killer 不会考虑其他进程,因为我们只限制了memcached.service。[ec2-user ~]$sudo systemctl statusmemcached.service● memcached.service - memcached daemon Loaded: loaded (/usr/lib/systemd/system/memcached.service; enabled; preset: disabled) Drop-In: /etc/systemd/system/memcached.service.d └─override.conf Active: failed (Result: oom-kill) since Fri 2025-01-31 23:20:28 UTC; 2s ago Duration: 2.901s Process: 360130 ExecStart=/usr/bin/memcached -p ${PORT} -u ${USER} -m ${CACHESIZE} -c ${MAXCONN} $OPTIONS (code=killed, signal=KILL) Main PID: 360130 (code=killed, signal=KILL) CPU: 94ms Jan 31 23:20:25 ip-1-2-3-4.us-west-2.compute.internal systemd[1]: Started memcached.service - memcached daemon. Jan 31 23:20:28 ip-1-2-3-4.us-west-2.compute.internal systemd[1]: memcached.service: A process of this unit has been killed by the OOM killer. Jan 31 23:20:28 ip-1-2-3-4.us-west-2.compute.internal systemd[1]: memcached.service: Main process exited, code=killed, status=9/KILL Jan 31 23:20:28 ip-1-2-3-4.us-west-2.compute.internal systemd[1]: memcached.service: Failed with result 'oom-kill'.