Docker中のウェブサーバプロセス数を制御する
ウェブサービス目的のDockerのコンテナでは、当然のことながら内部でウェブサーバを起動している。Apacheやnginxである。しかし、特にApacheの場合には、ほうっておくと起動プロセス数が多すぎ、メモリを圧迫し、Apacheではなく、他プログラムが落ちてしまうことがある。
実際に、Apacheのプロセス数が多すぎて、Javaのサーバプログラムが落ちてしまう事例に見舞われた。
その時の対処方法が、Apacheのメモリ消費を抑える(prefork)にあるが、この記事は古い。Apache2.4も含む説明については、Apache2.4 preforkパラメータの設定方法を参照のこと。
状況の確認
これはホスト側のメモリ状況を見てみればわかる。起動直後と、その後のメモリ消費状況をみてみると、明らかに大きく異なっており、何者かがメモリを食い尽くしているのがわかる。
特にapacheのプロセスを見てみると、こんな具合だ。もちろんホスト側を調べている。
# ps ax | grep apache
43958 ? Ss 0:04 apache2 -DFOREGROUND
44051 ? S 1:17 apache2 -DFOREGROUND
44052 ? S 1:18 apache2 -DFOREGROUND
44053 ? S 1:13 apache2 -DFOREGROUND
44054 ? S 1:12 apache2 -DFOREGROUND
44055 ? S 1:15 apache2 -DFOREGROUND
44951 ? S 1:15 apache2 -DFOREGROUND
44952 ? S 1:19 apache2 -DFOREGROUND
44981 ? S 1:14 apache2 -DFOREGROUND
44982 ? S 1:15 apache2 -DFOREGROUND
44984 ? S 1:18 apache2 -DFOREGROUND
51945 ? S 0:04 apache2 -DFOREGROUND
52001 ? S 0:20 apache2 -DFOREGROUND
52002 ? S 0:20 apache2 -DFOREGROUND
52003 ? S 0:20 apache2 -DFOREGROUND
52004 ? S 0:21 apache2 -DFOREGROUND
52005 ? S 0:21 apache2 -DFOREGROUND
52811 ? S 0:21 apache2 -DFOREGROUND
702819 pts/0 S+ 0:00 grep --color=auto apache
どうもDockerの環境において、pstreeを使っても、どのコンテナがこれらのプロセスを生み出しているのかわからない。何かしらそういう方法があるのかも、現在のところわからない。
コンテナ内のapacheプロセスを調べる
現在のところ、一つ一つコンテナを探っていくしか無いと判断した。
例えば、mailuシステムだが、これだけのコンテナが動作している。
# docker ps | grep mailu
4c7e64a48d01 mailu/roundcube:1.8 "docker-php-entrypoi…" 37 hours ago Up 37 hours (healthy) 80/tcp mailu_webmail_1
4c926a10619c mailu/postfix:1.8 "/bin/sh -c /start.py" 37 hours ago Up 37 hours (healthy) 25/tcp, 10025/tcp mailu_smtp_1
3d543fc97e02 mailu/dovecot:1.8 "/bin/sh -c /start.py" 37 hours ago Up 37 hours (healthy) 110/tcp, 143/tcp, 993/tcp, 2525/tcp, 4190/tcp mailu_imap_1
2255453af60a mailu/rspamd:1.8 "/bin/sh -c /start.py" 37 hours ago Up 37 hours (healthy) 11332/tcp, 11334-11335/tcp mailu_antispam_1
11cd1edf975c mailu/admin:1.8 "/bin/sh -c /start.py" 37 hours ago Up 37 hours (healthy) 80/tcp mailu_admin_1
10afa7ffe8c3 mailu/fetchmail:1.8 "/fetchmail.py" 37 hours ago Up 37 hours mailu_fetchmail_1
bbc84e8aafe7 mailu/unbound:1.8 "/bin/sh -c /start.py" 37 hours ago Up 37 hours (healthy) 53/tcp, 53/udp mailu_resolver_1
7c7761781dbb mailu/nginx:1.8 "/bin/sh -c /start.py" 37 hours ago Up 37 hours (healthy) 0.0.0.0:25->25/tcp, :::25->25/tcp, 0.0.0.0:110->110/tcp, :::110->110/tcp, 0.0.0.0:143->143/tcp, :::143->143/tcp, 0.0.0.0:465->465/tcp, :::465->465/tcp, 0.0.0.0:993->993/tcp, :::993->993/tcp, 80/tcp, 10025/tcp, 0.0.0.0:995->995/tcp, :::995->995/tcp, 10143/tcp, 0.0.0.0:8443->443/tcp, :::8443->443/tcp, 0.0.0.0:49156->587/tcp, :::49156->587/tcp mailu_front_1
d38f37e58bd0 redis:alpine "docker-entrypoint.s…" 37 hours ago Up 37 hours 6379/tcp mailu_redis_1
おそらくは、mailu_webmail_1というコンテナだろう。これを調べてみる。
# docker exec -it mailu_webmail_1 bash
root@4c7e64a48d01:/var/www/html# ps ax | grep apache
7 ? S 0:04 apache2 -DFOREGROUND
29 ? S 0:21 apache2 -DFOREGROUND
30 ? S 0:21 apache2 -DFOREGROUND
31 ? S 0:21 apache2 -DFOREGROUND
32 ? S 0:21 apache2 -DFOREGROUND
33 ? S 0:21 apache2 -DFOREGROUND
60 ? S 0:21 apache2 -DFOREGROUND
35767 pts/1 S+ 0:00 grep apache
r# cd /etc/apache2
# ls
apache2.conf conf-enabled magic mods-enabled sites-available
conf-available envvars mods-available ports.conf sites-enabled
#
案の定、Apacheプロセスが不必要に起動している。私の環境では、使っても数人、それも同時に使うことはほとんど無いだろう。そこで、Apacheをチューニングしてプロセス数を減らすことにする。
現状のMPMを確認する
docker exec -it mailu_webmail_1 bash
として
apachectl -V | grep MPM
とすると、
Server MPM: prefork
とpreforkになっていることがわかる。
Apacheの設定ファイルを外部に出す
まずは、現状ではコンテナ内にあるデフォルトのApache設定ファイルを外に出してやる。コンテナ内のままでも設定変更はできるが、再インストールの際には失われてしまうため、外に出した方が良い。
docker cp mailu_webmail_1:/etc/apache2 ./apache2
とし、docker-compose.ymlを書き換える。
# Webmail
webmail:
image: ${DOCKER_ORG:-mailu}/${DOCKER_PREFIX:-}roundcube:${MAILU_VERSION:-1.8}
restart: always
env_file: mailu.env
volumes:
- "./webmail:/data"
- "./apache2:/etc/apache2" # これを追加
depends_on:
- imap
Apache2の設定変更
問題としては、apacheのMPMがpreforkになっており、それがデフォルトで必要以上のプロセスを発生していることである。これについては、ApacheのMPMはpreform,worker,eventのどれを選ぶべきか?を参照のこと。
この設定を変更してみる。Apache2のバージョンや、構成によって異なるのかもれないが、設定ファイルは以下の構成になっている。
# ls -lR
.:
total 76
drwxr-xr-x. 8 root root 4096 Oct 12 2021 apache2
-rw-r--r--. 1 root root 7244 Oct 15 2021 apache2.conf
drwxr-xr-x. 2 root root 4096 Oct 15 2021 conf-available
drwxr-xr-x. 2 root root 4096 Oct 15 2021 conf-enabled
-rw-r--r--. 1 root root 1924 Oct 12 2021 envvars
-rw-r--r--. 1 root root 31063 Aug 8 2020 magic
drwxr-xr-x. 2 root root 8192 Oct 14 2021 mods-available
drwxr-xr-x. 2 root root 4096 Oct 14 2021 mods-enabled
-rw-r--r--. 1 root root 320 Aug 8 2020 ports.conf
drwxr-xr-x. 2 root root 54 Oct 12 2021 sites-available
drwxr-xr-x. 2 root root 30 Oct 12 2021 sites-enabled
バージョンやディストリビューションによって、様々な設定方法があるようだが、この環境では以下と同じ設定ファイル構成のようだ。
つまり、mods-available内に、使われているいないに関わらずあらゆる設定があり、そのうちの使うものがmods-enabledにリンクされているというだけだ。
mods-enabled内でls- lすると以下になる。
lrwxrwxrwx. 1 root root 36 Oct 12 2021 access_compat.load -> ../mods-available/access_compat.load
lrwxrwxrwx. 1 root root 28 Oct 12 2021 alias.conf -> ../mods-available/alias.conf
lrwxrwxrwx. 1 root root 28 Oct 12 2021 alias.load -> ../mods-available/alias.load
lrwxrwxrwx. 1 root root 33 Oct 12 2021 auth_basic.load -> ../mods-available/auth_basic.load
lrwxrwxrwx. 1 root root 33 Oct 12 2021 authn_core.load -> ../mods-available/authn_core.load
lrwxrwxrwx. 1 root root 33 Oct 12 2021 authn_file.load -> ../mods-available/authn_file.load
lrwxrwxrwx. 1 root root 33 Oct 12 2021 authz_core.load -> ../mods-available/authz_core.load
lrwxrwxrwx. 1 root root 33 Oct 12 2021 authz_host.load -> ../mods-available/authz_host.load
lrwxrwxrwx. 1 root root 33 Oct 12 2021 authz_user.load -> ../mods-available/authz_user.load
lrwxrwxrwx. 1 root root 32 Oct 12 2021 autoindex.conf -> ../mods-available/autoindex.conf
lrwxrwxrwx. 1 root root 32 Oct 12 2021 autoindex.load -> ../mods-available/autoindex.load
lrwxrwxrwx. 1 root root 30 Oct 12 2021 deflate.conf -> ../mods-available/deflate.conf
lrwxrwxrwx. 1 root root 30 Oct 12 2021 deflate.load -> ../mods-available/deflate.load
lrwxrwxrwx. 1 root root 26 Oct 12 2021 dir.conf -> ../mods-available/dir.conf
lrwxrwxrwx. 1 root root 26 Oct 12 2021 dir.load -> ../mods-available/dir.load
lrwxrwxrwx. 1 root root 26 Oct 12 2021 env.load -> ../mods-available/env.load
lrwxrwxrwx. 1 root root 29 Oct 12 2021 filter.load -> ../mods-available/filter.load
lrwxrwxrwx. 1 root root 27 Oct 12 2021 mime.conf -> ../mods-available/mime.conf
lrwxrwxrwx. 1 root root 27 Oct 12 2021 mime.load -> ../mods-available/mime.load
lrwxrwxrwx. 1 root root 34 Oct 12 2021 mpm_prefork.conf -> ../mods-available/mpm_prefork.conf
lrwxrwxrwx. 1 root root 34 Oct 12 2021 mpm_prefork.load -> ../mods-available/mpm_prefork.load
lrwxrwxrwx. 1 root root 34 Oct 12 2021 negotiation.conf -> ../mods-available/negotiation.conf
lrwxrwxrwx. 1 root root 34 Oct 12 2021 negotiation.load -> ../mods-available/negotiation.load
lrwxrwxrwx. 1 root root 27 Oct 14 2021 php7.load -> ../mods-available/php7.load
lrwxrwxrwx. 1 root root 33 Oct 12 2021 reqtimeout.conf -> ../mods-available/reqtimeout.conf
lrwxrwxrwx. 1 root root 33 Oct 12 2021 reqtimeout.load -> ../mods-available/reqtimeout.load
lrwxrwxrwx. 1 root root 31 Oct 12 2021 setenvif.conf -> ../mods-available/setenvif.conf
lrwxrwxrwx. 1 root root 31 Oct 12 2021 setenvif.load -> ../mods-available/setenvif.load
lrwxrwxrwx. 1 root root 29 Oct 12 2021 status.conf -> ../mods-available/status.conf
lrwxrwxrwx. 1 root root 29 Oct 12 2021 status.load -> ../mods-available/status.load
これをまずは変更する。
rm -f mpm_prefork.*
ln -s ../mods-available/mpm_event.load .
ln -s ../mods_available/mpm_event.conf ,
これでmailuを再起動してみる。すると、エラーが発生する。
webmail_1 | [Sat Oct 29 12:28:10.916685 2022] [php7:crit] [pid 7:tid 140663369444672] Apache is running a threaded MPM, but your PHP Module is not compiled to be threadsafe. You need to recompile PHP.
webmail_1 | AH00013: Pre-configuration failed
mailu_webmail_1 exited with code 1
smtp_1 | Oct 29 12:28:11 mail
PHPがスレッドセーフでないと言う。面倒なので元に戻す。ともあれ、外部に出したApache2の設定が反映されていることは確認できた。
rm -f mpm_event.*
ln -s ../mods-available/mpm_prefork.load .
ln -s ../mods_available/mpm_preform.conf ,
preforkのまま設定を変更する
eventにするにはPHPまで何らかの変更をしなければならないらしい。preforkのまま、メモリ使用量を減らすことにする。
mods-available/mpm_prefork.confを見ると以下の設定になっている。
<IfModule mpm_prefork_module>
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxRequestWorkers 150
MaxConnectionsPerChild 0
</IfModule>
チューニングの仕方はApacheのメモリ消費を抑える(prefork)に書いたが、しかしこの頃とはチューニングパラメータが異なるようだ。以前の設定をそのまま移行することができない。迷惑な話だ。
今回はメールサーバのユーザインターフェースということもあり、最小限で十分だ。以下の設定をする。
<IfModule mpm_prefork_module>
StartServers 1
MinSpareServers 1
MaxSpareServers 1
MaxRequestWorkers 1
MaxConnectionsPerChild 0
</IfModule>
これで問題ないようだ。
# docker exec -it mailu_webmail_1 bash
# ps ax | grep apache
とすると、プロセスは二つしか起動していない。おそらく、複数の人間が使ったとしてもこのままだろう。
Nextcloudの設定
NextCloudも同様にApache2を使用していた。しかし、Nextcloudの場合には、上の設定だと全くページが表示されず、ログを見ると警告がでている。以下の値を変更した。
MaxRequestWorkers 50