Unyablog.

のにれんのブログ

systemd-nspawn 235 のコンテナで mlock を行う

systemd-nspwan で Elasticsearch の運用をしている。親のホストは ArchLinux なので新しい systemd が入ってくる。

ある日、 pacman -Syu して systemd を 234 から 235 に上げたら Elasticsearch がエラーで落ちるようになった。

[2017-10-19T07:10:13,955][WARN ][o.e.b.JNANatives         ] Unable to lock JVM Memory: error=1, reason=Operation not permitted
[2017-10-19T07:10:13,957][WARN ][o.e.b.JNANatives         ] This can result in part of the JVM being swapped out.
...
ERROR: [1] bootstrap checks failed
[1]: memory locking requested for elasticsearch process but memory is not locked

ソースコードを読むと、mlockall できてなくて落ちていた。

調べた結果、systemd 235 では systemd-nspawn の SystemCallFilter がホワイトリストになり、 mlockall ができなくなった のが原因だった。

解決法

コンテナに CAP_IPC_LOCK を与えるか、@memlock グループを許可する。

例えば、 foo というコンテナであれば、 /etc/systemd/nspawn/foo.nspawn

[Exec]
SystemCallFilter=@memlock # システムコールのみを許可する場合
Capability=CAP_IPC_LOCK # CAP_IPC_LOCK ごと許可する場合

を追加すれば良い。CAP_IPC_LOCK を与えた場合、 @memlock は自動で許可される。

詳細

systemd には SystemCallFilter というのがあって、呼び出せる system call を制限できる。

https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=

systemd-nspawn では、デフォルトでこの制限がかかっており、もともとはブラックリストで制限されていた。

systemd 235 からこのリストのデフォルトがホワイトリスト形式になり、CAP_IPC_LOCK がない場合は @memlock が許可されないようになった。

    * systemd-nspawn gained support for a new --system-call-filter= command
      line option for adding and removing entries in the default system
      call filter it applies. Moreover systemd-nspawn has been changed to
      implement a system call whitelist instead of a blacklist.

https://github.com/systemd/systemd/blob/c1719d8bc924ed59448616bd748671c5c7a66d93/NEWS

当該PRは以下*1

https://github.com/systemd/systemd/pull/6818/files#diff-524fbe88b6a5bf2879b598646b22ed25R70

Elasticsearch では bootstrap.memory_lock: true とした場合に mlockall を内部で実行するのだが、これが権限不足で失敗していた。

これを解決するには、capability を足すか @memlock を直接許可すれば良い。sytemd.nspawn 内でどちらも設定できる(「解決法」を参照)。

CAP_IPC_LOCK を付加すると、自動的に @memlock が付加される*2

Note that the applied system call filter is also altered implicitly if additional capabilities are passed using the --capabilities=.

https://www.freedesktop.org/software/systemd/man/systemd-nspawn.html#--system-call-filter=

@memlock のみを許可した場合、コンテナに CAP_IPC_LOCK は与えられないので、 RLIMIT_MEMLOCK の範囲内で mlock できるようになる。

In Linux 2.6.8 and earlier, a process must be privileged (CAP_IPC_LOCK) in order to lock memory and the RLIMIT_MEMLOCK soft resource limit defines a limit on how much memory the process may lock.
Since Linux 2.6.9, no limits are placed on the amount of memory that a privileged process can lock and the RLIMIT_MEMLOCK soft resource limit instead defines a limit on how much memory an unprivileged process may lock.

https://linux.die.net/man/2/mlockall

*1:そもそも @memlock が作られたのはこの pr である

*2:capsh --print で持っている capability が見える