ulimit 入門 - プロセスのリソース上限を理解する

ulimit 入門 - プロセスのリソース上限を理解する

ulimit とは?

結論: ulimit は shell とそこから起動するプロセスが使えるリソース量(開けるファイル数・プロセス数・メモリ等)の上限を確認・変更するコマンド。

ulimit は bash 等の shell に組み込まれたコマンドで、カーネルの getrlimit(2) / setrlimit(2) を呼び出してリソース上限(resource limit)を操作する。上限は「1 プロセスあたり」に適用され、fork した子プロセスへ継承される。

代表的な用途は次の 3 つ。

  • 「Too many open files」のような上限到達エラーの原因切り分け
  • DB / Web サーバ等の同時接続数に見合う上限の引き上げ
  • 暴走スクリプトによる資源枯渇を防ぐ上限の引き下げ

前提(対象環境)

  • bash を前提(ulimit は shell ビルトイン。zsh 等でも利用可だがオプションが一部異なる)
  • 永続化の節は systemd ベースのディストリ(Ubuntu / RHEL 系)を想定

ソフトリミットとハードリミットの違いは?

結論: ソフトは実際に効く上限、ハードはソフトの天井。非特権ユーザーはソフトをハードまで上げられるが、ハードを引き上げるには root 権限が必要。

各リソースには 2 つの値がある。

種別 意味 非特権ユーザーの変更可否
ソフト(soft) 実際に強制される現在の上限 ハード以下なら自由に増減できる
ハード(hard) ソフトが超えられない天井 下げる方向のみ(上げは不可)

ハードを一度下げると同一プロセス内では戻せない(戻すには root 権限 = CAP_SYS_RESOURCE が必要)。ulimit ではフラグで対象を切り替える。

$ ulimit -Sn        # ソフトの open files 上限を表示
$ ulimit -Hn        # ハードの open files 上限を表示
  • -S:ソフトリミットを対象
  • -H:ハードリミットを対象
  • 省略時:表示はソフト設定は両方を同時に変更する点に注意

現在の上限を確認するには?

結論: ulimit -a で全リソースの現在のソフト上限を一覧表示できる。個別に見るときは -n(ファイル)等の単独フラグを使う。

$ ulimit -a
real-time non-blocking time  (microseconds, -R) unlimited
core file size              (blocks, -c) 0
data seg size               (kbytes, -d) unlimited
scheduling priority                 (-e) 0
file size                   (blocks, -f) unlimited
pending signals                     (-i) 15363
max locked memory           (kbytes, -l) 8192
max memory size             (kbytes, -m) unlimited
open files                          (-n) 1024
pipe size                (512 bytes, -p) 8
stack size                  (kbytes, -s) 8192
max user processes                  (-u) 15363
virtual memory              (kbytes, -v) unlimited

-a はソフト値を表示する。ハード値をまとめて見たいときは ulimit -aH を使う。

$ ulimit -aH        # 全リソースのハード上限

主なリソース種別の一覧

結論: 実務で触る頻度が高いのは -n(open files)・-u(プロセス数)・-c(core)・-s(stack)の 4 つ。

フラグ リソース 典型的な用途・症状
-n open files 「Too many open files」。FD 数の上限
-u max user processes 「fork: retry: Resource temporarily unavailable」
-c core file size クラッシュ時の core dump 出力可否(0 で無効)
-f file size 1 ファイルあたりの最大サイズ
-s stack size 深い再帰での segfault 調査
-v virtual memory プロセスの仮想メモリ総量の上限
-l max locked memory mlock できるメモリ量(DB 等で関係)

-u(max user processes)はそのユーザー全体のプロセス数に対する上限。一見「このシェルだけ」に見えて、同一 UID の全プロセスを合算してカウントする点に注意する。

上限を一時的に変更するには?

結論: ulimit -Sn 4096 のように実行すると、その shell と以降に起動する子プロセスにだけ反映される。shell を閉じると元に戻る。

# ソフトの open files をハードの範囲内で 4096 に引き上げ
$ ulimit -Sn 4096

# 確認
$ ulimit -Sn
4096

ハードを超える値を指定すると拒否される。

$ ulimit -Sn 999999
bash: ulimit: open files: cannot modify limit: Operation not permitted

この場合はハードの引き上げが必要で、非特権ユーザーでは不可。ハードごと上げるには root で次のように実行する(root は両方変更できる)。

# root のみ。ソフト/ハードを同時に 65536 へ
$ sudo bash -c 'ulimit -n 65536; exec your-server'

ulimit の効果はそれを実行した shell とその子孫にしか及ばない。既に動いているデーモンの上限は変わらない。稼働中プロセスへの適用は prlimit を使う。

上限を永続化するには?

結論: ログインセッションには /etc/security/limits.d/*.conf、systemd 管理のサービスには unit の LimitNOFILE= を使う。両者は適用経路が別物。

ログインシェル(pam_limits 経由)

/etc/security/limits.conf および /etc/security/limits.d/*.conf は、PAM の pam_limits モジュールがログイン時に適用する。SSH ログインや login 経由の shell が対象。

# /etc/security/limits.d/90-nofile.conf
*        soft    nofile    8192
*        hard    nofile    65536
deploy   soft    nproc     4096

書式は <domain> <type> <item> <value>domain はユーザー名・@グループ名・全体を表す *。設定後はいったんログアウトして再ログインしないと反映されない。

pam_limits は PAM セッションを経由しないプロセスには効かない。systemd が起動するデーモンには limits.conf は適用されないため、サービスの上限を上げたつもりが効かない事故が多い。

systemd サービス

systemd 管理のサービスは unit ファイルの [Service] セクションで指定する。

# /etc/systemd/system/myapp.service.d/override.conf
[Service]
LimitNOFILE=65536
LimitNPROC=4096
$ sudo systemctl daemon-reload
$ sudo systemctl restart myapp

全サービス共通のデフォルトを変えるなら /etc/systemd/system.confDefaultLimitNOFILE= を編集する。systemctl show myapp -p LimitNOFILE で実効値を確認できる。

実行中プロセスの上限を調べるには?

結論: /proc/<PID>/limits を読むか prlimit --pid <PID> を使う。prlimit は再起動なしに稼働中プロセスの上限を変更もできる。

$ cat /proc/1234/limits
Limit                     Soft Limit  Hard Limit  Units
Max open files            1024        524288      files
Max processes             15363       15363       processes
...

prlimit なら 1 行で確認・変更できる。

# 確認
$ prlimit --pid 1234 --nofile

# 稼働中プロセスのソフト/ハードを 8192 に変更(権限が必要な場合あり)
$ sudo prlimit --pid 1234 --nofile=8192:8192

# コマンドを上限付きで起動
$ prlimit --nofile=4096:4096 ./myserver

Too many open files の対処

結論: まず lsof で実際の FD 数を数え、上限と突き合わせる。アプリの FD リークか、上限不足かを切り分けてから対処する。

「Too many open files」は open files(-n)の上限到達を示す。手順は次の通り。

  1. 対象プロセスの PID を特定する
  2. 実効上限を確認する(cat /proc/<PID>/limits
  3. 実際に開いている FD 数を数える
# プロセスが開いている FD 数をカウント
$ lsof -p 1234 | wc -l
  1. FD 数が上限に張り付いていれば、原因を判断する。
  • FD リーク(開きっぱなしで閉じていない)→ アプリ側の修正が本筋。上限引き上げは延命策
  • 正当に多い(高同時接続)→ 永続化の手順で上限を引き上げる

まとめ

  • ulimit はプロセス単位のリソース上限を操作するコマンド。ソフト/ハードの二層構造を押さえる
  • 一時変更は ulimit -Sn N、確認は ulimit -a / /proc/<PID>/limits / prlimit
  • 永続化はログインなら limits.d/*.conf、systemd サービスなら unit の LimitNOFILE=経路の取り違えに注意
  • 「Too many open files」は上限引き上げの前に FD リークを疑う

次に読む