如何识别使用 systemd 启动的服务的所有配置内存限制?

如何识别使用 systemd 启动的服务的所有配置内存限制?

我正在追寻一个错误,试图将新的调整应用于 postgres。

确切的错误是:

2018-11-07 22:14:49 EST [7099]: [1-1] FATAL:  could not map anonymous shared memory: Cannot allocate memory
2018-11-07 22:14:49 EST [7099]: [2-1] HINT:  This error usually means that PostgreSQL's request for a shared memory segment exceeded available memory, swap space, or huge pages. To reduce the request size (currently 35301089280 bytes), reduce PostgreSQL's shared memory usage, perhaps by reducing shared_buffers or max_connections.

我对这个错误很熟悉。调整 postgres 的各种实例是我一起工作的工程师的每月任务。解决方案是撤回我们的 postgres 调整或管理设置,例如shmallulimit

在本例中,我们正在调整一个由其他人创建的 postgres 安装,并且由于几年的运行和升级而产生了一些缺陷。此安装从 CentOS 5 安装开始,现在在 CentOS 7 上安装。CentOS 5 上的旧 SysV 安装应用了多种内存限制控制,包括:

  • /etc/sysconfig/postgresql.d/ulimit.sh
  • /etc/sysconfig/postgresql.d/memory-cap
  • shmmax和 的设置极其保守shmall
  • 来自其他供应商或系统管理员的脚本,通过更改配置文件故意强制某些值
  • /etc/sysctl.conf

自从从 CentOS 5 升级到 CentOS 7 以来,现在似乎对内存限制有了额外的控制,这些控制在从 SysV 更改为 SystemD 时应用。

例如systemctl cat postgresql.service显示:

# /usr/lib/systemd/system/postgresql.service
[Unit]
Description=PostgreSQL database server
After=network.target

[Service]
Type=forking
User=postgres
Group=postgres
Environment=PGPORT=5432
Environment=PGDATA=/opt/pgsql/data
OOMScoreAdjust=-1000
LimitSTACK=16384
ExecStart=/opt/pgsql/bin/pg_ctl start -D ${PGDATA} -s -o "-p ${PGPORT}" -w -l ${PGDATA}/serverlog
ExecStop=/opt/pgsql/bin/pg_ctl stop -D ${PGDATA} -s -m fast
ExecReload=/opt/pgsql/bin/pg_ctl reload -D ${PGDATA} -s
TimeoutSec=300

[Install]
WantedBy=multi-user.target

# /etc/systemd/system/postgresql.service.d/memory-cap.conf
#
# THIS FILE IS AUTO-GENERATED by /opt/pgsql/bin/tune.sh
# DO NOT MODIFY, it will be overwritten on next postgres startup.
# If you need to make a change, then disable the tuner:
#
# ln -s /dev/null /etc/systemd/system/postgresql.service.d/tune.conf
#

[Service]
LimitAS=12884901888
# /etc/systemd/system/postgresql.service.d/tune.conf
# /usr/lib/systemd/system/postgresql.service.d/use-system-timezone.conf
# Disable automatically setting the timezone by masking this drop-in file:
# ln -s /dev/null /etc/systemd/system/postgresql.service.d/use-system-timezone.conf
# Then you need to:
# systemctl daemon-reload
[Service]
ExecStartPre=/opt/pgsql/bin/use-system-timezone.sh

现在回到我的实际问题:显然有几层内核设置、每用户限制和服务配置,每个层都可以对shmmaxshmallulimit和相关设置施加限制。如何从配置或运行时确定 SystemD 服务启动时实际应用的限制?

如果我可以确定运行时的限制是什么,我就可以开始grep配置文件和脚本来查找这些限制的设置位置。一旦找到这些值,我就可以根据需要设置值。我希望有一个标志可以让 SystemD 或我的 postgres 进程在它作为服务启动时注销其明显的设置。

我对这些值应该设置的内容感到满意,有太多层可能会强制或覆盖这些值。我想了解我需要触摸哪些配置位置。

我的看法是,我可能会遇到像 SystemDLimitFOO设置这样的情况,它的值与 不同,sysctl -w kernel.shmfoo并且与 不同/etc/someconfig/serviceuser/limit.foo。我需要确定实际使用或应用的限制,以便我可以正确更改和设置这些限制来调整我正在运行的服务。

答案1

正如您在问题中指出的那样,游戏存在一些限制:

  1. System V IPC,如shmallshmmax等。
  2. RLIMIT 的(通常由ulimitshell 中的命令设置和检查,因此您可能通过该名称知道它们。)
  3. cgroup 限制(特别是内存 cgroup,在您的情况下),这是对现代内核中的进程组应用限制的新方法。

systemd 管理后两者,特别是使用 cgroup 作为限制和计费的主要机制。它确实对 System V IPC 有一些小的有限支持,但并不是真正的限制。

让我们分解这三个独立的概念,并研究如何检查和调整每个概念与 systemd 相关的限制。

系统V工控机

systemd 对 System V IPC 有一些小的支持(例如,服务停止时清理 IPC,在自己的 IPC 命名空间中运行服务或者/tmp为单个服务安装私有 tmpfs(由 shm 支持)),但在大多数情况下,它不会进一步管理 System V IPC 限制,也不会对其进行任何核算。

因此 System V IPC 的限制完全由 管理sysctl,因此您可以使用以下命令检查这些限制:

$ sysctl kernel.shmmax kernel.shmall kernel.shmmni
kernel.shmmax = 18446744073692774399
kernel.shmall = 18446744073692774399
kernel.shmmni = 4096

并用 调整它们sysctl -w

systemd 仅参与设置这些限制,因为它包括systemd-sysctl.service它负责设置来自/etc/sysctl.conf和 的那些/etc/sysctl.d/*.conf。但除此之外,sysctl它还直接为您提供了有关这些限制的内核信息。

RLIMIT(ulimit)

这些限制是按进程设置的,并由子进程继承(因此通常它们在进程树中是相同的,但不一定。)

systemd 允许对每个服务进行设置,以便在服务启动时按照配置设置限制。

这些是由诸如 、 等指令配置的LimitSTACK=LimitAS=您已经在问题中提到过。您可以查看 RLIMIT 的完整列表在 systemd 的手册页中,它还将这些与熟悉的ulimit命令相关联。

您可以使用该systemctl show命令检查正在运行的单元的当前限制,该命令会从 systemd 转储单元的内部状态。

例如:

$ systemctl show postgresql.service | grep ^Limit
LimitSTACK=16384
LimitSTACKSoft=16384
LimitAS=12884901888
LimitASSoft=12884901888
... (other RLIMITs omitted for terseness) ...

您还可以通过查看来检查内核认为的限制是什么/proc/$pid/limits(请记住,这些限制是针对每个进程的,因此您需要查看各个 PID。)

例如:

$ cat /proc/12345/limits 
Limit                     Soft Limit           Hard Limit           Units     
Max stack size            16384                16384                bytes     
Max address space         12884901888          12884901888          bytes     
... (other RLIMITs omitted for terseness) ...

cgroups(内存cgroup)

最后,cgroup 是 systemd 管理服务、提供限制和记账的主要机制。

systemd 有许多可用和支持的 cgroup(如 CPU、内存、IO、任务等),但对于本次讨论,我们将重点关注内存 cgroup(因为这些是您的问题中涉及的限制,我们研究了SysV IPC 和 RLIMIT 也有相应的内存限制。)

与 RLIMIT 相同,您还可以使用systemctl showcgroups 查看 systemd 提供的内存统计:

$ systemctl show postgresql.service | grep ^Memory
MemoryCurrent=631328768
MemoryAccounting=yes
MemoryLow=0
MemoryHigh=infinity
MemoryMax=infinity
MemorySwapMax=infinity
MemoryLimit=infinity
MemoryDenyWriteExecute=yes

您将看到内存核算已启用 ( MemoryAccounting=yes),但未设置任何限制(全部设置为inifinity。)限制列表可能会有所不同,具体取决于您的 systemd 和内核版本,这是内核 4.20-rc0 上的 systemd 239,其中有“低”、“高”、“最大”、“限制”和专门用于交换的单独限制。

您可能会发现有趣的另一点是,您将能够通过该MemoryCurrent=值得知该服务正在使用多少内存。这是从内核 cgroup 信息中获取的,它是该服务的内存使用情况的最新测量值。

您还可以在使用systemctl status该服务时看到该信息:

$ systemctl status postgresql.service
● postgresql.service - PostgreSQL database server
   Loaded: loaded (/usr/lib/systemd/system/postgresql.service; enabled; vendor preset: disabled)
 Main PID: 12345 (postgresql)
    Tasks: 10 (limit: 4321)
   Memory: 602M
   CGroup: /system.slice/postgresql.service
           └─12345 /usr/lib/postgresql/postgresql

正如您所看到的,systemd 正在报告内存使用情况 ( Memory: 602M),它来自 cgroup 信息。您还可以看到任务记帐已启用(通过相应的 cgroup),并且它报告当前使用该服务的 4321 个最大任务限制中的 10 个任务。

状态输出还包括有关底层 cgroup 的信息,以服务命名(每个服务都在自己的 cgroup 中运行),然后您可以使用这些信息直接从内核检查 cgroup 限制和记帐信息。

例如:

$ cd /sys/fs/cgroup/memory/system.slice/postgresql.service/
$ cat memory.limit_in_bytes 
9223372036854771712
$ cat memory.usage_in_bytes 
631328768

(数字 9223372036854771712 是2^63 - 4096,在本例中代表infinity64 位计数器。)

你可以看看内存 cgroup 的内核文档有关这些限制和计数器的更多详细信息。内核中有两个版本的 cgroup(cgroup-v1 和 cgroup-v2),因此如果系统使用 cgroup-v2,您可能会发现系统中存在一些显着差异。 systemd 支持两者(以及使用两者的混合模型),因此systemctl无论内核上启用了哪个版本的 cgroups,查询限制和计数器都应该为您提供一致的视图。

相关内容