如何辨識使用 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 調整,要麼管理諸如
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 程序在它作為服務啟動時註銷其明顯的設置。我對這些值應該設置什麼感到滿意,有太多層可能會強製或覆蓋這些值。我想了解我需要觸摸哪些配置位置。
我的看法是,我可能會遇到像 SystemD
LimitFOO
設置這樣的情況,它的sysctl -w kernel.shmfoo
值不同於/etc/someconfig/serviceuser/limit.foo
. 我需要確定實際使用或應用了哪些限制,以便我可以正確更改和設置這些限制以調整我正在執行的服務。
正如您在問題中指出的那樣,有幾個限制:
- System V IPC 的,例如
shmall
,shmmax
等。- RLIMIT(通常由 shell 中的命令設置和檢查,
ulimit
因此您可能通過該名稱知道它們。)- cgroup 限制(特別是記憶體 cgroup,在您的情況下),這是一種將限制應用於現代核心中的程序組的新方法。
systemd 管理後兩者,特別是使用 cgroups 作為限制和記帳的主要機制。它確實對 System V IPC 有一些小的有限支持,但並不是真正的限制。
讓我們分解這三個獨立的概念,並研究如何檢查和調整每個與 systemd 相關的限制。
系統 V IPC
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
並用
sysctl -w
.systemd 只參與設置這些限制,因為它包括systemd-sysctl.service,它負責設置來自
/etc/sysctl.conf
和/etc/sysctl.d/*.conf
. 但除此之外,它還sysctl
直接為您提供有關這些限制的核心資訊。RLIMITs (ulimit)
這些限制是按程序設置的,並由子程序繼承(因此通常它們在程序樹中是相同的,但不一定。)
systemd 允許為每個服務設置這些限制,以便在服務啟動時將限制設置為配置。
這些是由您在問題中已經提到的指令配置的,例如
LimitSTACK=
,等。您可以在 systemd 的手冊頁中查看LimitAS=
RLIMIT 的完整列表,其中還將這些列表與熟悉的命令相關聯。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)
最後,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
您會看到啟用了記憶體記帳 (
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),並且它報告目前正在使用 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
(數字 9223372036854771712 是
2^63 - 4096
,在這種情況下表示infinity
在 64 位計數器內。)您可以查看記憶體 cgroup 的核心文件以獲取有關這些限制和計數器的更多詳細資訊。核心中有兩個版本的 cgroup(cgroup-v1 和 cgroup-v2),因此如果使用 cgroup-v2 代替,您可能會發現系統中存在一些顯著差異。systemd 支持兩者(以及同時使用兩者的混合模型),因此
systemctl
無論核心上啟用了哪個版本的 cgroup,使用查詢限制和計數器都應該為您提供一致的視圖。