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

Fre*_*eit 7 memory configuration 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.
Run Code Online (Sandbox Code Playgroud)

我熟悉这个错误。调整 postgres 的各种实例是与我一起工作的工程师的每月任务。解决方案是撤回我们的 postgres 调优或管理像shmall和 之类的设置ulimit

在这种情况下,我们正在调整由其他人创建的 postgres 安装,并且有一些来自几年运行时和升级的残留物。此安装从 CentOS 5 安装开始,现在安装在 CentOS 7 上。 CentOS 5 上的旧 SysV 安装应用了几个内存限制控制,包括:

  • /etc/sysconfig/postgresql.d/ulimit.sh
  • /etc/sysconfig/postgresql.d/memory-cap
  • 非常保守的设置shmmaxshmall
  • 来自其他供应商或系统管理员的脚本,它们通过更改配置文件故意强制某些值
  • /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
Run Code Online (Sandbox Code Playgroud)

现在在我身边我实际的问题:有明确的内核设置几层,每用户的限制,以及服务配置可以各强加限制shmmaxshmallulimit,和相关的设置。如何从配置或运行时确定 SystemD 服务在启动时实际应用的限制?

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

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

我的看法是,我可以遇到类似 SystemDLimitFOO设置的情况,该设置sysctl -w kernel.shmfoo/etc/someconfig/serviceuser/limit.foo. 我需要确定实际使用或应用的限制,以便我可以正确更改和设置这些限制以调整我正在运行的服务。

fil*_*den 7

正如您在问题中指出的那样,有几个限制:

  1. 系统V IPC的,如shmallshmmax
  2. RLIMIT的人(这常常被设置和检查通过ulimit在shell命令,所以你可能会用这个名字认识他们。)
  3. cgroup 限制(特别是内存 cgroup,在您的情况下),这是一种对现代内核中的进程组应用限制的新方法。

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

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

系统 V 工控机

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

因此 System V IPC 的限制由 专门管理sysctl,因此您可以使用以下内容检查这些限制:

$ sysctl kernel.shmmax kernel.shmall kernel.shmmni
kernel.shmmax = 18446744073692774399
kernel.shmall = 18446744073692774399
kernel.shmmni = 4096
Run Code Online (Sandbox Code Playgroud)

并用sysctl -w.

systemd 只参与设置这些限制,因为它包含systemd-sysctl.service负责设置这些限制来自/etc/sysctl.conf/etc/sysctl.d/*.conf。但除此之外,这就是全部sysctl,它还直接为您提供内核关于这些限制的信息。

RLIMIT (ulimit)

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

systemd 允许为每个服务设置这些限制,以便在服务启动时按照配置设置限制。

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

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

例如:

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

您还可以通过查看来检查内核认为的限制是什么/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) ...
Run Code Online (Sandbox Code Playgroud)

cgroups(内存 cgroup)

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

systemd 有很多可用和支持的 cgroup(如 CPU、内存、IO、任务等),但在本次讨论中,让我们关注内存 cgroup(因为这些是您的问题所涉及的限制,我们查看了SysV IPC 和 RLIMIT 的相应内存限制也是如此。)

与 RLIMIT 相同,您还可以systemctl show使用 cgroups 查看 systemd 提供的内存记帐:

$ systemctl show postgresql.service | grep ^Memory
MemoryCurrent=631328768
MemoryAccounting=yes
MemoryLow=0
MemoryHigh=infinity
MemoryMax=infinity
MemorySwapMax=infinity
MemoryLimit=infinity
MemoryDenyWriteExecute=yes
Run Code Online (Sandbox Code Playgroud)

您会看到内存记帐已启用 ( 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
Run Code Online (Sandbox Code Playgroud)

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

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

例如:

$ cd /sys/fs/cgroup/memory/system.slice/postgresql.service/
$ cat memory.limit_in_bytes 
9223372036854771712
$ cat memory.usage_in_bytes 
631328768
Run Code Online (Sandbox Code Playgroud)

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

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