CLOVER🍀

That was when it all began.

systemd制御下で動作するプロセスの、リソース制御を行う

これは、なにをしたくて書いたもの?

Linuxにおけるリソース制御といえば、ulimitです。

limits.conf(5) - Linux manual page

/etc/security/limits.confファイルや/etc/security/limits.dディレクトリ内に[任意の名前].confファイルを作成して
nofileなどの値を設定すればOKです。

ですが、systemd制御下で動作するプロセスはこの事情が違います。前々からなんとなく知ってはいましたが、
自分で設定したことがなかったので試しておくことにしました。

環境

今回の環境は、こちら。Ubuntu Linux 20.04 LTSです。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.3 LTS
Release:        20.04
Codename:       focal


$ uname -srvmpio
Linux 5.4.0-91-generic #102-Ubuntu SMP Fri Nov 5 16:31:28 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

また、リソース制御を行う対象のサービスとして、nginxを使うことにします。

$ sudo apt install nginx

バージョン。

$ nginx -v
nginx version: nginx/1.18.0 (Ubuntu)

インストールした時点で、nginxはrootユーザーで起動しています。

$ ps -ef | grep nginx | grep -v grep
root        3976       1  0 14:32 ?        00:00:00 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
www-data    3977    3976  0 14:32 ?        00:00:00 nginx: worker process
www-data    3978    3976  0 14:32 ?        00:00:00 nginx: worker process

この時の/proc/[PID]/limitsを見ると、Max open filesのsoft limitが1024になっているので、こちらを変更してみましょう。

$ cat /proc/$(cat /run/nginx.pid)/limits
Limit                     Soft Limit           Hard Limit           Units     
Max cpu time              unlimited            unlimited            seconds   
Max file size             unlimited            unlimited            bytes     
Max data size             unlimited            unlimited            bytes     
Max stack size            8388608              unlimited            bytes     
Max core file size        0                    unlimited            bytes     
Max resident set          unlimited            unlimited            bytes     
Max processes             7596                 7596                 processes 
Max open files            1024                 524288               files     
Max locked memory         65536                65536                bytes     
Max address space         unlimited            unlimited            bytes     
Max file locks            unlimited            unlimited            locks     
Max pending signals       7596                 7596                 signals   
Max msgqueue size         819200               819200               bytes     
Max nice priority         0                    0                    
Max realtime priority     0                    0                    
Max realtime timeout      unlimited            unlimited            us

/etc/security/limits〜が効かないことを確認する

まずは/etc/security/limits〜が効かないことを確認してみます。

rootユーザーにスイッチして、現在の値を確認。

$ sudo su -

# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 7596
max locked memory       (kbytes, -l) 65536
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 7596
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

ファイルの書き方は、/etc/security/limits.confファイルに書かれているので

/etc/security/limits.conf

# /etc/security/limits.conf
#
#Each line describes a limit for a user in the form:
#
#<domain>        <type>  <item>  <value>
#
#Where:
#<domain> can be:
#        - a user name
#        - a group name, with @group syntax
#        - the wildcard *, for default entry
#        - the wildcard %, can be also used with %group syntax,
#                 for maxlogin limit
#        - NOTE: group and wildcard limits are not applied to root.
#          To apply a limit to the root user, <domain> must be
#          the literal username root.
#
#<type> can have the two values:
#        - "soft" for enforcing the soft limits
#        - "hard" for enforcing hard limits
#
#<item> can be one of the following:
#        - core - limits the core file size (KB)
#        - data - max data size (KB)
#        - fsize - maximum filesize (KB)
#        - memlock - max locked-in-memory address space (KB)
#        - nofile - max number of open file descriptors
#        - rss - max resident set size (KB)
#        - stack - max stack size (KB)
#        - cpu - max CPU time (MIN)
#        - nproc - max number of processes
#        - as - address space limit (KB)
#        - maxlogins - max number of logins for this user
#        - maxsyslogins - max number of logins on the system
#        - priority - the priority to run user process with
#        - locks - max number of file locks the user can hold
#        - sigpending - max number of pending signals
#        - msgqueue - max memory used by POSIX message queues (bytes)
#        - nice - max nice priority allowed to raise to values: [-20, 19]
#        - rtprio - max realtime priority
#        - chroot - change root to directory (Debian-specific)
#
#<domain>      <type>  <item>         <value>
#

#*               soft    core            0
#root            hard    core            100000
#*               hard    rss             10000
#@student        hard    nproc           20
#@faculty        soft    nproc           20
#@faculty        hard    nproc           50
#ftp             hard    nproc           0
#ftp             -       chroot          /ftp
#@student        -       maxlogins       4

# End of file

こんなファイルを作成。

/etc/security/limits.d/root.limits.conf

root            -    nofile            10240

1度シェルを抜けて、再度rootにスイッチして確認。

$ sudo su -


# ulimit -n
10240


# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 7596
max locked memory       (kbytes, -l) 65536
max memory size         (kbytes, -m) unlimited
open files                      (-n) 10240
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 7596
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

open filesの値が増えていることが確認できます。

このあたりは、PAMの範囲です。

$ grep -r limit /etc/pam.d
/etc/pam.d/runuser:session              required        pam_limits.so
/etc/pam.d/systemd-user:session  required pam_limits.so
/etc/pam.d/sshd:# access limits that are hard to express in sshd_config.
/etc/pam.d/sshd:# Set up user limits from /etc/security/limits.conf.
/etc/pam.d/sshd:session    required     pam_limits.so
/etc/pam.d/atd:session    required   pam_limits.so
/etc/pam.d/login:# set access limits.
/etc/pam.d/login:# Sets up user limits according to /etc/security/limits.conf
/etc/pam.d/login:# (Replaces the use of /etc/limits in old login)
/etc/pam.d/login:session    required   pam_limits.so
/etc/pam.d/su:# Sets up user limits according to /etc/security/limits.conf
/etc/pam.d/su:# (Replaces the use of /etc/limits in old login)
/etc/pam.d/su:session    required   pam_limits.so
/etc/pam.d/cron:# Sets up user limits, please define limits for cron tasks
/etc/pam.d/cron:# through /etc/security/limits.conf
/etc/pam.d/cron:session    required   pam_limits.so

なのですが、nginxを再起動しても

$ sudo systemctl restart nginx

こちらのMax open filesの値は変わりません。

$ cat /proc/$(cat /run/nginx.pid)/limits
Limit                     Soft Limit           Hard Limit           Units     
Max cpu time              unlimited            unlimited            seconds   
Max file size             unlimited            unlimited            bytes     
Max data size             unlimited            unlimited            bytes     
Max stack size            8388608              unlimited            bytes     
Max core file size        0                    unlimited            bytes     
Max resident set          unlimited            unlimited            bytes     
Max processes             7596                 7596                 processes 
Max open files            1024                 524288               files     
Max locked memory         65536                65536                bytes     
Max address space         unlimited            unlimited            bytes     
Max file locks            unlimited            unlimited            locks     
Max pending signals       7596                 7596                 signals   
Max msgqueue size         819200               819200               bytes     
Max nice priority         0                    0                    
Max realtime priority     0                    0                    
Max realtime timeout      unlimited            unlimited            us

こちらは事情が異なります、と。

今回は、これを変更できるようにすることが目的です。

systemdのドキュメントを読む

systemdのユニットのリソース制御は、ユニット定義ファイルで行います。

こちらを見ると、プロセスのリソース制御について書かれています。

PROCESS PROPERTIES

   LimitCPU=, LimitFSIZE=, LimitDATA=, LimitSTACK=, LimitCORE=,
   LimitRSS=, LimitNOFILE=, LimitAS=, LimitNPROC=, LimitMEMLOCK=,
   LimitLOCKS=, LimitSIGPENDING=, LimitMSGQUEUE=, LimitNICE=,
   LimitRTPRIO=, LimitRTTIME=

[https://man7.org/linux/man-pages/man5/systemd.exec.5.html:title]

各項目が、ulimitのどの項目に該当するかも書かれていますね。

Table 1. Resource limit directives, their equivalent ulimit shell commands and the unit used

LimitNOFILE ulimit -n

プロセスのリソース制御も含め、こういったカテゴリのものが設定できるようなので、ドキュメントには
目を通しておくとよいでしょう。

  • PATHS
  • USER/GROUP IDENTITY
  • CAPABILITIES
  • SECURITY
  • MANDATORY ACCESS CONTROL
  • PROCESS PROPERTIES
  • SCHEDULING
  • SANDBOXING
  • …

今回の対象のnginxのサービスユニット定義ファイルはこちらなので

/etc/systemd/system/multi-user.target.wants/nginx.service

# Stop dance for nginx
# =======================
#
# ExecStop sends SIGSTOP (graceful stop) to the nginx process.
# If, after 5s (--retry QUIT/5) nginx is still running, systemd takes control
# and sends SIGTERM (fast shutdown) to the main process.
# After another 5s (TimeoutStopSec=5), and if nginx is alive, systemd sends
# SIGKILL to all the remaining processes in the process group (KillMode=mixed).
#
# nginx signals reference doc:
# http://nginx.org/en/docs/control.html
#
[Unit]
Description=A high performance web server and a reverse proxy server
Documentation=man:nginx(8)
After=network.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'
ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'
ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid
TimeoutStopSec=5
KillMode=mixed

[Install]
WantedBy=multi-user.target

ServiceにLimitNOFILEを設定。

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'
ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'
ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid
TimeoutStopSec=5
KillMode=mixed
LimitNOFILE=10240

設定をリロードして、nginxを再起動。

$ sudo systemctl daemon-reload
$ sudo systemctl restart nginx

変更が反映されましたね。

$ cat /proc/$(cat /run/nginx.pid)/limits
Limit                     Soft Limit           Hard Limit           Units     
Max cpu time              unlimited            unlimited            seconds   
Max file size             unlimited            unlimited            bytes     
Max data size             unlimited            unlimited            bytes     
Max stack size            8388608              unlimited            bytes     
Max core file size        0                    unlimited            bytes     
Max resident set          unlimited            unlimited            bytes     
Max processes             7596                 7596                 processes 
Max open files            10240                10240                files     
Max locked memory         65536                65536                bytes     
Max address space         unlimited            unlimited            bytes     
Max file locks            unlimited            unlimited            locks     
Max pending signals       7596                 7596                 signals   
Max msgqueue size         819200               819200               bytes     
Max nice priority         0                    0                    
Max realtime priority     0                    0                    
Max realtime timeout      unlimited            unlimited            us

hard limitも一緒の値になってしまいましたが。

これが気持ち悪い場合は、以下のようにするとsoft limitとhard limitをそれぞれ指定できます。

LimitNOFILE=10240:524288

再読み込みして、再起動後して確認。

$ cat /proc/$(cat /run/nginx.pid)/limits
Limit                     Soft Limit           Hard Limit           Units     
Max cpu time              unlimited            unlimited            seconds   
Max file size             unlimited            unlimited            bytes     
Max data size             unlimited            unlimited            bytes     
Max stack size            8388608              unlimited            bytes     
Max core file size        0                    unlimited            bytes     
Max resident set          unlimited            unlimited            bytes     
Max processes             7596                 7596                 processes 
Max open files            10240                524288               files     
Max locked memory         65536                65536                bytes     
Max address space         unlimited            unlimited            bytes     
Max file locks            unlimited            unlimited            locks     
Max pending signals       7596                 7596                 signals   
Max msgqueue size         819200               819200               bytes     
Max nice priority         0                    0                    
Max realtime priority     0                    0                    
Max realtime timeout      unlimited            unlimited            us

soft limitとhard limitをそれぞれ指定できました。

これは、ドキュメントにそのように書いてあります。

Resource limits may be specified in two formats: either as single value to set a specific soft and hard limit to the same value, or as colon-separated pair soft:hard to set both limits individually (e.g. "LimitAS=4G:16G").

ここで、1度ユニット定義ファイルを元に戻します。

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t -q -g 'daemon on; master_process on;'
ExecStart=/usr/sbin/nginx -g 'daemon on; master_process on;'
ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload
ExecStop=-/sbin/start-stop-daemon --quiet --stop --retry QUIT/5 --pidfile /run/nginx.pid
TimeoutStopSec=5
KillMode=mixed

このように、パッケージでインストールされたユニット定義ファイルを触るのは嫌だと思うかもしれません。

そこで、「drop-in」という仕組みを使います。以下のように/etc/systemd/system/[サービス名].dというディレクトリを
作成します。

$ sudo mkdir /etc/systemd/system/nginx.service.d

この中に置いたファイルは、「drop-in」という仕組みでユニット構成ファイルをオーバーライドできるようです。
※なのですが、drop-inについて調べるとだいたい先ほどmkdirで作成したディレクトリの情報が現れるのですが、これは
 どこの情報なんでしょうね?

In addition to the "main" configuration file, drop-in configuration snippets are read from /usr/lib/systemd/.conf.d/, /usr/local/lib/systemd/.conf.d/, and /etc/systemd/.conf.d/. Those drop-ins have higher precedence and override the main configuration file. Files in the .conf.d/ configuration subdirectories are sorted by their filename in lexicographic order, regardless of in which of the subdirectories they reside. When multiple files specify the same option, for options which accept just a single value, the entry in the file sorted last takes precedence, and for options which accept a list of values, entries are collected as they occur in the sorted files.

When packages need to customize the configuration, they can install drop-ins under /usr/. Files in /etc/ are reserved for the local administrator, who may use this logic to override the configuration files installed by vendor packages. Drop-ins have to be used to override package drop-ins, since the main configuration file has lower precedence. It is recommended to prefix all filenames in those subdirectories with a two-digit number and a dash, to simplify the ordering of the files.

systemd-system.conf(5) - Linux manual page

ファイル名には2桁の数字を付けることが推奨されているようなので

It is recommended to prefix all filenames in those subdirectories with a two-digit number and a dash, to simplify the ordering of the files.

こんな感じに作成して

/etc/systemd/system/nginx.service.d/00-limits.conf

[Service]
LimitNOFILE=10240:524288

再読み込みさせると

$ sudo systemctl daemon-reload
$ sudo systemctl restart nginx

反映されました。

$ cat /proc/$(cat /run/nginx.pid)/limits
Limit                     Soft Limit           Hard Limit           Units     
Max cpu time              unlimited            unlimited            seconds   
Max file size             unlimited            unlimited            bytes     
Max data size             unlimited            unlimited            bytes     
Max stack size            8388608              unlimited            bytes     
Max core file size        0                    unlimited            bytes     
Max resident set          unlimited            unlimited            bytes     
Max processes             7596                 7596                 processes 
Max open files            10240                524288               files     
Max locked memory         65536                65536                bytes     
Max address space         unlimited            unlimited            bytes     
Max file locks            unlimited            unlimited            locks     
Max pending signals       7596                 7596                 signals   
Max msgqueue size         819200               819200               bytes     
Max nice priority         0                    0                    
Max realtime priority     0                    0                    
Max realtime timeout      unlimited            unlimited            us

systemctl statusを見ると、「Drop-In」という表示が現れるようになっています。

$ sudo systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
    Drop-In: /etc/systemd/system/nginx.service.d
             └─00-limits.conf
     Active: active (running) since Thu 2021-12-02 15:32:01 JST; 42s ago
       Docs: man:nginx(8)
    Process: 6734 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
    Process: 6735 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
   Main PID: 6736 (nginx)
      Tasks: 3 (limit: 2278)
     Memory: 3.5M
     CGroup: /system.slice/nginx.service
             ├─6736 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
             ├─6737 nginx: worker process
             └─6738 nginx: worker process

12月 02 15:32:01 ubuntu2004.localdomain systemd[1]: Starting A high performance web server and a reverse proxy server...
12月 02 15:32:01 ubuntu2004.localdomain systemd[1]: Started A high performance web server and a reverse proxy server.

全体のデフォルト値を変更する

最後に、systemdで管理しているユニット全体のデフォルト値を変更してみましょう。あまり使う機会はない気はしますが。

先ほどのnginxのdrop-inのファイルはコメントアウトして

/etc/systemd/system/nginx.service.d/00-limits.conf

#[Service]
#LimitNOFILE=10240:524288

nginxにも反映しておきます(デフォルトに戻しておきます)。

$ cat /proc/$(cat /run/nginx.pid)/limits
Limit                     Soft Limit           Hard Limit           Units     
Max cpu time              unlimited            unlimited            seconds   
Max file size             unlimited            unlimited            bytes     
Max data size             unlimited            unlimited            bytes     
Max stack size            8388608              unlimited            bytes     
Max core file size        0                    unlimited            bytes     
Max resident set          unlimited            unlimited            bytes     
Max processes             7596                 7596                 processes 
Max open files            1024                 524288               files     
Max locked memory         65536                65536                bytes     
Max address space         unlimited            unlimited            bytes     
Max file locks            unlimited            unlimited            locks     
Max pending signals       7596                 7596                 signals   
Max msgqueue size         819200               819200               bytes     
Max nice priority         0                    0                    
Max realtime priority     0                    0                    
Max realtime timeout      unlimited            unlimited            us

これは、以下で指定します。

DefaultLimitCPU=, DefaultLimitFSIZE=, DefaultLimitDATA=, DefaultLimitSTACK=, DefaultLimitCORE=, DefaultLimitRSS=, DefaultLimitNOFILE=, DefaultLimitAS=, DefaultLimitNPROC=, DefaultLimitMEMLOCK=, DefaultLimitLOCKS=, DefaultLimitSIGPENDING=, DefaultLimitMSGQUEUE=, DefaultLimitNICE=, DefaultLimitRTPRIO=, DefaultLimitRTTIME=

ファイルの場所は、このあたりのようですね。

/etc/systemd/system.conf, /etc/systemd/system.conf.d/.conf, /run/systemd/system.conf.d/.conf, /usr/lib/systemd/system.conf.d/*.conf

~/.config/systemd/user.conf, /etc/systemd/user.conf, /etc/systemd/user.conf.d/.conf, /run/systemd/user.conf.d/.conf, /usr/lib/systemd/user.conf.d/*.conf

systemd-system.conf(5) - Linux manual page

実際、/etc/systemd/system.confファイルを見ると、コメントアウトされていますが設定項目があるのがわかります。

$ grep DefaultLimit /etc/systemd/system.conf

#DefaultLimitCPU=
#DefaultLimitFSIZE=
#DefaultLimitDATA=
#DefaultLimitSTACK=
#DefaultLimitCORE=
#DefaultLimitRSS=
#DefaultLimitNOFILE=1024:524288
#DefaultLimitAS=
#DefaultLimitNPROC=
#DefaultLimitMEMLOCK=
#DefaultLimitLOCKS=
#DefaultLimitSIGPENDING=
#DefaultLimitMSGQUEUE=
#DefaultLimitNICE=
#DefaultLimitRTPRIO=
#DefaultLimitRTTIME=

ちなみに、これはManagerという設定カテゴリになります。

[Manager]

これらの項目は、systemdで管理されているサービスすべてに影響するので、あまり使うことはないかなと思いますが。
試すだけ試しておきましょう。

ここで、以下のように設定して

[Manager]
DefaultLimitNOFILE=10240:524288

反映。

$ sudo systemctl daemon-reload
$ sudo systemctl restart nginx

確認。

$ cat /proc/$(cat /run/nginx.pid)/limits
Limit                     Soft Limit           Hard Limit           Units     
Max cpu time              unlimited            unlimited            seconds   
Max file size             unlimited            unlimited            bytes     
Max data size             unlimited            unlimited            bytes     
Max stack size            8388608              unlimited            bytes     
Max core file size        0                    unlimited            bytes     
Max resident set          unlimited            unlimited            bytes     
Max processes             7596                 7596                 processes 
Max open files            10240                524288               files     
Max locked memory         65536                65536                bytes     
Max address space         unlimited            unlimited            bytes     
Max file locks            unlimited            unlimited            locks     
Max pending signals       7596                 7596                 signals   
Max msgqueue size         819200               819200               bytes     
Max nice priority         0                    0                    
Max realtime priority     0                    0                    
Max realtime timeout      unlimited            unlimited            us

今回はnginxだけ再起動して確認してみましたが、デフォルト値設定後に起動したプロセスには影響するように
なります。

また、このファイルも別に切り出すことができます。

ルールがこうなので

/etc/systemd/system.conf, /etc/systemd/system.conf.d/.conf, /run/systemd/system.conf.d/.conf, /usr/lib/systemd/system.conf.d/*.conf

~/.config/systemd/user.conf, /etc/systemd/user.conf, /etc/systemd/user.conf.d/.conf, /run/systemd/user.conf.d/.conf, /usr/lib/systemd/user.conf.d/*.conf

ディレクトリを作成して

$ sudo mkdir /etc/systemd/system.conf.d

ファイルを作成。

/etc/systemd/system.conf.d/00-override.conf

[Manager]
DefaultLimitNOFILE=10240:524288

再読込して、反映。

$ sudo systemctl daemon-reload
$ sudo systemctl restart nginx

変更が確認できました。

$ cat /proc/$(cat /run/nginx.pid)/limits
Limit                     Soft Limit           Hard Limit           Units     
Max cpu time              unlimited            unlimited            seconds   
Max file size             unlimited            unlimited            bytes     
Max data size             unlimited            unlimited            bytes     
Max stack size            8388608              unlimited            bytes     
Max core file size        0                    unlimited            bytes     
Max resident set          unlimited            unlimited            bytes     
Max processes             7596                 7596                 processes 
Max open files            10240                524288               files     
Max locked memory         65536                65536                bytes     
Max address space         unlimited            unlimited            bytes     
Max file locks            unlimited            unlimited            locks     
Max pending signals       7596                 7596                 signals   
Max msgqueue size         819200               819200               bytes     
Max nice priority         0                    0                    
Max realtime priority     0                    0                    
Max realtime timeout      unlimited            unlimited            us

まとめ

systemd制御下で動作するプロセスの、リソース制御を行ってみました。

drop-inファイルの場所とドキュメントの書かれ方にちょっと癖がある気がしますが(Ubunut Linuxだからですかね…)、
ここだけちょっとハマりました。

だいたいやり方はわかったので良しとしましょう。