我正在追寻一个错误,试图将新的调整应用于 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 调整或管理设置,例如shmall
和ulimit
。
在本例中,我们正在调整一个由其他人创建的 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
现在回到我的实际问题:显然有几层内核设置、每用户限制和服务配置,每个层都可以对shmmax
、shmall
、ulimit
和相关设置施加限制。如何从配置或运行时确定 SystemD 服务启动时实际应用的限制?
如果我可以确定运行时的限制是什么,我就可以开始grep
配置文件和脚本来查找这些限制的设置位置。一旦找到这些值,我就可以根据需要设置值。我希望有一个标志可以让 SystemD 或我的 postgres 进程在它作为服务启动时注销其明显的设置。
我对这些值应该设置的内容感到满意,有太多层可能会强制或覆盖这些值。我想了解我需要触摸哪些配置位置。
我的看法是,我可能会遇到像 SystemDLimitFOO
设置这样的情况,它的值与 不同,sysctl -w kernel.shmfoo
并且与 不同/etc/someconfig/serviceuser/limit.foo
。我需要确定实际使用或应用的限制,以便我可以正确更改和设置这些限制来调整我正在运行的服务。
答案1
正如您在问题中指出的那样,游戏存在一些限制:
- System V IPC,如
shmall
、shmmax
等。 - 这RLIMIT 的(通常由
ulimit
shell 中的命令设置和检查,因此您可能通过该名称知道它们。) - 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 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
您将看到内存核算已启用 ( 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
,在本例中代表infinity
64 位计数器。)
你可以看看内存 cgroup 的内核文档有关这些限制和计数器的更多详细信息。内核中有两个版本的 cgroup(cgroup-v1 和 cgroup-v2),因此如果系统使用 cgroup-v2,您可能会发现系统中存在一些显着差异。 systemd 支持两者(以及使用两者的混合模型),因此systemctl
无论内核上启用了哪个版本的 cgroups,查询限制和计数器都应该为您提供一致的视图。