Ubuntu

Ubuntu22.04 ApacheでHTTP/2

2015年5月にはHTTP/2が始まっており、2022年6月からはHTTP/3が始まっているというのに、ホームラボはHTTP/1.1のままだった。
まずは、HTTP/2でサービス提供できるようにしよう。



広告


いくつかのサイトで、設定からパフォーマンスチューニングまで教えてくれている。
Hacker’s High / php-fpm の設定を理解してサイトのパフォーマンスを向上させる
Qiita / Ubuntu20.04 と Apache2 で HTTP/2 化

ただ、読んでもよくわからない。
諸兄が教えてくれるこれらを理解するために、背景となる知識が必要だ。

設定の概要

ホームラボにはいくつかサーバーがあるが、PHPが入っている・入っていないの違いでちょっと動きが違ったので、その観点で書いてみる。
※Rubyの場合にはまた違った設定が必要かもしれないが、必要になったときに頑張る方向。

やろうとすることを整理してみると、

  • ApacheがHTTP/2を取り扱えるようにする。(mod_http2.so)
  • Apacheのマルチプロセッシングモジュール(MPM)を、マルチプロセス・マルチスレッドのものに切り替える。(mod_mpm_event.so)
  • ApacheのMPMが作業を渡せるようにする。(mod_proxy_fcgi.so, mod_proxy.so)
  • ApacheのMPMに具体的な作業の渡し方を知らせる。(php8.1-fpm.conf)
  • Apacheから作業を受け取って処理するリスナーを設置する。(PHP 8.1 FastCGI Process Manager)
  • チューニング。ここでいうチューニングは最低限のもので、大量アクセスに対応するチューニングを指してはいない。

ということで、考えていたよりも多かった。

まず、HTTP/2はHTTP/1.1と違って、リクエストを並行して扱うことができるプロトコルになった。
mdn web docs / HTTP の進化 HTTP/2 – 高パフォーマンスなプロトコル 参照

一方、ApacheはリクエストをMPMで受け付けて処理する。
MPMはOSに最適なものが提供されており、UNIXではprefork、worker、eventの3つがある。このうちのどれか1つをロードする必要がある。

MPMは、ネットワークポートのバインド、リクエストの受け付け、子プロセスの割り当てを行う主役と言っても良い機能で、このような特徴がある。

スレッドをサポートスレッドセーフな
ポーリングをサポート
デフォルトのMPMポイント
NONOpreforkスレッド化されていないサーバーを実装している。スレッド化を避ける必要がある場合に適する。
MaxRequestWorkersを予想される同時リクエストより大きく、すべてのプロセスがメモリを確保できるように小さく設定することが重要。
YESNOworkerマルチプロセス・マルチスレッドサーバーを実装している。
ThreadsPerChildで子プロセスが展開するスレッドの数を制御し、MaxRequestWorkersで起動可能なスレッドの最大総数を制御することが重要。
YESYESeventworkerベースの実装で、パフォーマンスが良い。
イマドキのOSの多くは、スレッドとスレッドセーフなポーリングをサポートするので、これがデフォルトになる。
リスナースレッドに作業を渡すことで、より多くのリクエストを同時に処理できるようにしている。

並行したリクエストはスレッドで処理するようなので、HTTP/2が並列で多くのリクエストを送信するプロトコルであることから、preforkは向いていないので(できるけれども厳しい制限)、eventを設定するのがベスト

そして、MPMにeventを使用するならば、作業を渡すリスナーを設置する必要がある。

以上のように大掛かりな仕掛けになってしまうが、並列で要求を処理できるとページの読み込みスピードは上がるので、設定する価値はあるかもしれない。

なお、ブラウザはHTTPSでないとHTTP/2の通信をしない。
今回の記事ではSSLの設定をサボっているので、curlコマンドを使ってHTTP/2の通信ができるようになるところまで確認しているが、サービスを公開するならSSLの設定が必要。

PHPを使用していない環境

ほぼまっさらな環境で試したが、この場合はMPMがデフォルトでeventになっており、HTTP/2を有効にすれば良かった。

まず、Apacheをインストール。

$ sudo apt -y install apache2
$ apache2 -v
Server version: Apache/2.4.52 (Ubuntu)
Server built:   2023-03-01T22:43:55

http2モジュールを有効化する。

$ sudo a2enmod http2
$ sudo systemctl restart apache2

ヘッダーを確認してみる。

$ curl --http2 --head http://localhost
HTTP/1.1 101 Switching Protocols
Upgrade: h2c
Connection: Upgrade

HTTP/2 200 
last-modified: Sat, 27 May 2023 05:34:11 GMT
etag: W/"29af-5fca6340f6d01"
accept-ranges: bytes
content-length: 10671
vary: Accept-Encoding
content-type: text/html
date: Sun, 00 Jan 1900 00:00:00 GMT
server: Apache/2.4.52 (Ubuntu)

HTTP/2で通信ができるようになっている。

PHPを使用している環境

状態の確認

テスト環境として、後述の手順でWordPressをインストールした。
この場合は、MPMがpreforkになっている。

$ apachectl -M
 ...
 mpm_prefork_module (shared)
 ...

レスポンスを確認すると、HTTP/1.1での通信になっている。
誰かに教えてもらった手順で普通にWordPressをインストールすると、大体こんな状態になっているのではなかろうか。

$ curl --http2 --head http://localhost
HTTP/1.1 200 OK
Date: Sat, 27 May 2023 09:35:13 GMT
Server: Apache/2.4.52 (Ubuntu)
Link: <http://script.hogeserver.hogeddns.jp/wp-json/>; rel="https://api.w.org/"
Content-Type: text/html; charset=UTF-8

HTTP/2を有効にする

作業を受け取るリスナーを設置する。

$ sudo apt install php-fpm

php-fpm(実態はphp8.1-fpm)はサービスで、設定ファイルが/etc/php/8.1/fpm/php-fpm.confに見える。
また、pool wwwというのが2つあること、ドメインソケットが開いていることがわかる。

$ systemctl status php8.1-fpm.service
● php8.1-fpm.service - The PHP 8.1 FastCGI Process Manager
     Loaded: loaded (/lib/systemd/system/php8.1-fpm.service; enabled; vendor preset: enabled)
     Active: active (running) since Sat 2023-05-27 09:50:20 UTC; 14h ago
       Docs: man:php-fpm8.1(8)
    Process: 97480 ExecStartPost=/usr/lib/php/php-fpm-socket-helper install /run/php/php-fpm.sock /etc/php/8.1/fpm/pool.d/www.conf 81 (code=exited, status=0/SUCCESS)
   Main PID: 97477 (php-fpm8.1)
     Status: "Processes active: 0, idle: 2, Requests: 60, slow: 0, Traffic: 0req/sec"
      Tasks: 3 (limit: 4530)
     Memory: 52.2M
        CPU: 3.493s
     CGroup: /system.slice/php8.1-fpm.service
             ├─97477 "php-fpm: master process (/etc/php/8.1/fpm/php-fpm.conf)" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
             ├─97478 "php-fpm: pool www" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
             └─97479 "php-fpm: pool www" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" ""
...

$ sudo ss -axp
...
Netid State  Recv-Q Send-Q            Local Address:Port   Peer Address:Port   Process                                                                                                                                                                                                            
u_str LISTEN 0      511    /run/php/php8.1-fpm.sock 194938            * 0      users:(("php-fpm8.1",pid=97479,fd=9),("php-fpm8.1",pid=97478,fd=9),("php-fpm8.1",pid=97477,fd=7))              
u_str ESTAB  0      0                             * 194937            * 194936 users:(("php-fpm8.1",pid=97477,fd=6))                                                                          
u_str ESTAB  0      0                             * 194936            * 194937 users:(("php-fpm8.1",pid=97477,fd=5))                                                           
...

※ssコマンドでは、何故かcgroupを使用したフィルターが使用できなかったので、適当に表示を詰めて表示している。

続いて、いま有効になっているpreforkを無効にする。
php8.1がmpm_preforkに依存しているので、この順番で実行しないと無効にできない。

$ sudo a2dismod php8.1
$ sudo a2dismod mpm_prefork

eventとhttp2、proxy_fcgiを有効にする。
また、php8.1-fpmという設定を有効にして、php-fpmに作業を渡す方法を具体的に伝える。

$ sudo a2enmod mpm_event http2 proxy_fcgi
$ sudo a2enconf php8.1-fpm
$ sudo systemctl restart apache2

レスポンスを確認すると、HTTP/2になっていた。

$ curl --http2 --head http://localhost
HTTP/1.1 101 Switching Protocols
Upgrade: h2c
Connection: Upgrade

HTTP/2 200 
link: <http://script.hogeserver.hogeddns.jp/wp-json/>; rel="https://api.w.org/"
content-type: text/html; charset=UTF-8
date: Sun, 00 Jan 1900 00:00:00 GMT
server: Apache/2.4.52 (Ubuntu)

以降、チューニングをしていく。

php.ini

php.iniは3つあった。

$ find /etc/php/8.1/ -name 'php.ini'
/etc/php/8.1/fpm/php.ini
/etc/php/8.1/cli/php.ini
/etc/php/8.1/apache2/php.ini

コマンドラインから実行したときと、apacheから実行したときで、使うphp.iniが違うようだ。
stack overflow / 2 php.ini files

今回、apacheで直接作業をするのではなく、php-fpmに作業を渡すことにしたのだから、使われるのは/etc/php/8.1/fpm/php.iniということになる。

具体的には、apache2/php.iniとfpm/php.iniの差分を調べて、fpm/php.iniに変更点を反映させた。

変更した結果を反映させるため、必要かどうかはっきりとはわからないが、サービスを再起動しておいた。

$ sudo systemctl restart php8.1-fpm

mpm_event.conf

ApacheのMPM eventについて、/etc/apache2/mods-available/mpm_event.confで設定されている。
Apache / Apache MPM Common Directives

この他にもeventで使える設定項目はあるが、ファイルにある項目だけを整理した。

設定項目mpm_event.confの説明設定値公式サイトの説明で見つけたポイント
StartServers起動するサーバープロセスの初期数2プロセスは負荷に応じて動的に制御されるため、通常このパラメーターを調整する理由はない。
MinSpareThreadsスペアを維持するワーカースレッドの最小数25リクエスト急増への対応に利用可能なアイドルスレッドの最小値。サーバーに十分なアイドルスレッドがなければ、この数になるまで子プロセスが作られる。
MaxSpareThreadsスペアを維持するワーカースレッドの最大数75サーバーのアイドルスレットが多ければ、この数より少なくなるまでプロセスが強制終了される。
ThreadLimit<説明なし>64ThreadsPerChildの設定値の最大値。ThreadsPerChildの値を遥かに大きくすると、余分な未使用共有メモリが割り当てられるので注意が必要。
ThreadsPerChild各サーバープロセスにおけるワーカースレッドの数を一定にする25子プロセスが起動時に作成するスレッドの数。
MaxRequestWorkersワーカースレッドの最大数150同時に処理されるリクエストの最大数。これを超えた接続は、ListenBacklogに基づく数までキューに入れられ、別のリクエストが終了するとサービスされる。
MaxConnectionsPerChildサーバープロセスが処理するリクエストの最大数0個々のプロセスが処理するコネクション数の上限。処理が終われば子プロセスは終了する。0が設定されているときには、プロセスは終了しない。

自分のサイトがどの程度アクセスされているのか、1度のアクセスでどの程度の並列のリクエストがあるのか、という観点で最適値を考えることになるのだろう。

当サイトの場合、同時アクセスは殆どないに等しいので、ワーカースレッドが150もできれば十分ということで、このままで進めることにした。
仮にこれを超えた場合でも、ListenBacklog(デフォルト511)まではキューに入って順番に処理されるので、まず問題はないだろう。

php-fpm.conf

PHP FastCGI Process Managerのグローバル設定は、/etc/php/8.1/fpm/php-fpm.confにある。
ファイルの中では、設定項目についてコメントで詳しく説明がなされている

コメントをDeepL先生に翻訳してもらった後に、公式に日本語ページがあることに気が付いたりする不思議…
PHPマニュアル / インストールと設定 / FastCGI Process Manager (FPM)

当サイトに必要な変更はないと思われた。

www.conf

PHP FastCGI Process Managerのプール設定は、/etc/php/8.1/fpm/pool.d/ディレクトリにwww.confが1つだけ設置されていた。
設定項目はたくさんあるのだけれど、今の段階で重要なのはプロセスの管理かと思ったので、それだけを抜粋した。

インストール直後は、プロセスマネージャーがdynamicに設定されており、かつ、pm.max_childrenに5が設定されている。
このpm.max_childrenの値は小さすぎて、当サイトでも足らないくらいなので、引き上げておく必要があると思われる。

実際、数値を引き上げておかないと、並列のリクエストが5つになった時点で、ログに警告が出力されたりする。
とはいえ、単純に100とか200とかにすると、プロセスが増えちゃって大変なんじゃないかと思う。

/var/log/php7.4-fpm.log

WARNING: [pool www] server reached pm.max_children setting (5), consider raising it

さて、今回、プロセスマネージャーをstaticに変更し、あわせて、pm.max_childrenはコア数と同じ2にしみた。
[再掲]Hacker’s High / php-fpm の設定を理解してサイトのパフォーマンスを向上させる

これによって並行で取り扱えるリクエストは2つになるが、プロセスを起こすオーバーヘットがないので、CPUをよりリクエストへの対応に働かせることができる。
Apacheの設定では最大150スレッドを起こすことになっているが、Apacheからの要求があふれてもlisten.backlog(デフォルト511)の分だけキューに入り、順番に処理される理屈なので問題はないだろう。

2023/06/02 追記
pmをstatic、pm.max_childrenを2にして運用をしていたら、時々ApacheもPHP-FPMもログを出さずに停止(?)していることがあった。
停止というか、アクセスするとずーっと待たされて何も表示されない、PHP-FPMを再起動すると処理が再開する、という症状だった。
Z-Pushを運用している環境で、そこからアクセスすると一発でこの状態に陥る。

想像だけれども、
 PHPのプログラムから自分のサイトにWebアクセスする
 ApacheがPHPの処理を要求する
 PHP-FPMは処理中のために要求がキューに入る
となって、最初のPHPのプログラムはWebアクセスの応答がないので、いつまでも待っているのではないか。

PHPが自分のサイトにWebアクセスするような流れになっている場合には、それを考慮したプロセス数を設定するのだろう。
同時アクセスが多ければ多いほど、デッドロックみたいになってしまう可能性が高まるだろうから、どこかで隙間ができる程度にプロセス数を設定しておかなければ。
まっすぐ走るだけのプログラムなら、こんなことは考えなくても良いのだろうから、pm.max_childrenは2が一番効率的なんだろうけれども…仕方がない。

なお、pm.max_requestsは未設定(=0)だったものを500に設定した(500がコメントで用意されていたのでそのまま利用)。
これにより、プロセスは500回処理をすると再起動するので、アプリで何らかのメモリリークが発生していても、そのタイミングで開放されるようになる。

設定項目は後にまとめているが、この変更に関係しそうな部分だけを抜粋しておく。

設定項目説明デフォルト値設定値
listen.backloglisten(2)のバックログを設定します。511
(FreeBSDとOpenBSDは-1)
なし
pmプロセスマネージャが子プロセスの数をどのように制御するかを選択します。
使用可能な値:
static – 固定された子プロセス数(pm.max_children)
dynamic – 子プロセスの数は、pm.max_children、pm.start_servers、pm.min_spare_servers、pm.max_spare_serversに基づき動的に設定されます。このプロセス管理では、常に最低1つの子プロセスが存在することになります。
ondemand – 起動時に子プロセスは作成されません。新しいリクエストが接続されると、子プロセスはフォークされます。pm.max_children、pm.process_idle_timeoutが使用されます。
備考:この値は必須です。
なし(設定必須)dynamic
pm.max_childrenpm が ‘static’ に設定されているときに作成される子プロセスの数、 pm が ‘dynamic’ あるいは ‘ondemand’ に設定されているときの子プロセスの最大数を指定します。 この値は、同時に処理されるリクエストの数の制限を設定します。mpm_prefork を使用した ApacheMaxClients ディレクティブと同等です。 オリジナルの PHP CGI の PHP_FCGI_CHILDREN 環境変数に相当します。以下のデフォルトは、あまりリソースのないサーバーを想定しています。あなたのニーズに合うように pm.* を調整することを忘れないでください。
備考:pmが「static」、「dynamic」、「ondemand」に設定されている場合に使用します。
備考:この値は必須です。
なし(設定必須)5
pm.start_servers起動時に生成される子プロセスの数。(min_spare_servers + max_spare_servers) / 22
pm.min_spare_serversアイドル状態のサーバープロセス数の最小値の希望値。
備考:pmが’dynamic’に設定されているときのみ使用されます。
備考:pmが’dynamic’に設定されている場合は必須です。
なし1
pm.max_spare_serversアイドル状態のサーバープロセス数の最大値の希望値。
備考:pmが’dynamic’に設定されているときのみ使用されます。
備考:pmが’dynamic’に設定されている場合は必須です。
なし3
pm.process_idle_timeoutアイドル状態のプロセスが強制終了するまでの秒数を指定しま す。
注:pmが’ondemand’に設定されているときのみ使用されます。
10s10s
pm.max_requests各子プロセスが再起動するまでに実行すべきリクエストの数です。
これは、サードパーティライブラリのメモリリークを回避するために有用です。無限にリクエストを処理したい場合は ‘0’ を指定します。PHP_FCGI_MAX_REQUESTS と同じ意味です。
0500

変更した結果を反映させるため、サービスを再起動する。

$ sudo systemctl restart php8.1-fpm

以上の設定で、だいたい問題なく動くようになったと思われる。

Ubuntu20.04でPHPを使用している環境

Ubuntu 20.04の場合も、22.04とやることは同じだった。

PHP FastCGI Process Managerをインストール。

$ sudo apt install php-fpm

php-fpmのphp.iniを設定する。
今までにファイルのアップロードサイズなどの変更を加えており、同じ動作をさせるため。
具体的には、/etc/php/7.4/apache2/php.iniと/etc/php/7.4/fpm/php.iniの差分を比較し、変更点をfpm/php.iniに反映させる。

続いて、php-fpmのpool wwwを設定する。
具体的には、プロセスマネージャーをstaticにして、pm.max_childrenをコア数に、pm.max_requestsを500に設定。

/etc/php/7.4/fpm/pool.d/www.conf

pm = static
pm.max_children = 2
pm.max_requests = 500

php-fpmの設定変更を反映する。

$ sudo systemctl restart php7.4-fpm.service

続いて、Apacheを止めて、MPMをpreforkからeventに変更する。
また、関連するモジュールや設定を有効化する。

$ sudo systemctl stop apache2

$ sudo a2dismod php7.4
$ sudo a2dismod mpm_prefork

$ sudo a2enmod mpm_event http2 proxy_fcgi
$ sudo a2enconf php7.4-fpm

$ sudo systemctl start apache2

これで、HTTP/2に切り替えることができた。

最初に設定を変更したときに、設定漏れでPHPのソースが表示されてしまった。

これは、モジュールproxy_fcgiを有効化しておらず、MPM eventが作業をリスナーに渡す事自体をしていなかったために発生していた。
もし、設定php7.4-fpmを有効にし忘れると、proxy_fcgiが作業をリスナーに渡す方法がわからないので、同様にPHPのソースが表示される。

仕組みから考えれば当然ではあるが、セットで一式ちゃんと設定することが大切。

その他

PHP-FPMとは何か

Ubuntuのマニュアルを一生懸命読んで、各設定ファイルのコメントを翻訳した後で、こういうページを見つけるわけで…
PHP / FastCGI Process Manager(FPM)

Ubuntuのマニュアルがこちらで、概要にこのようなことが書かれている。
ubuntu manuals / php-fpm – PHP FastCGI Process Manager

  • デーモンとしてバックグラウンドで動作し、CGIリクエストをリスニングする。
  • 出力は/var/log/php-fpm.logに記録される。
  • ほとんどのオプションを/etc/php-fpm.confで設定する。
  • デフォルトではlocalhost:9000/httpをリスニングして応答する。
  • webserverから、”.php”ファイルへの要求をすべてポート9000に転送することを期待する。

リスナーとして動作する、というのはこういうことなのかと理解。
実際にインストールすると、要求の転送はドメインソケットを通じて行われるように初期設定されている。

ポートでの転送と考えると、DockerHubにあるNextCloudのFPMは、恐らくそのタイプ。
docker-compose.ymlにNginxを加えてフロントに立たせ、NextCloudのコンテナはアプリケーションの動作だけを実行するようだった。
途中までしか試していないから断言はできないが、NextCloudコンテナのプロセス1がphp-fmpだったので、恐らくは。

Ubuntuのパッケージはこちら。
ubuntu packages / php8.1-fpm

インストール後に/lib/systemd/system/php8.1-fpm.serviceを見てみると、/etc/php/8.1/fpm/php-fpm.confを設定ファイルとして読み込むようになっていた。

php-fpm.conf

php-fpmの設定ファイルにあるコメントを、ほぼDeepL先生に翻訳してもらってみた。

最初のメッセージがこれ。

この設定ファイル内の相対パスは、すべて PHP のインストール先である /usr からの相対パスとなります。このプレフィックスは、コマンドラインから ‘-p’ 引数を使用することで動的に変更することができます。

セクションはglobalのみ。

設定項目説明デフォルト値設定値
pidpidファイル
備考:デフォルトプレフィックスは/var。
注意事項:この値を変更した場合は、systemd service PIDFile= の設定をこの値に合わせて変更する必要があります。
なし/run/php/php8.1-fpm.pid
error_log“syslog”に設定すると、ログはローカルファイルに書き込まれるのではなく、syslogdに送信されます。
デフォルトプレフィックスは/var。
log/php-fpm.log/var/log/php8.1-fpm.log
syslog.facilitysyslog_facility は、メッセージをロギングするプログラムの種類を指定するために使用します。これにより、syslogd は異なる施設からのメッセージが異なるように処理されることを指定することができます。
可能な値については syslog(3) を参照してください (ex daemon equiv LOG_DAEMON)
daemonなし
syslog.identsyslog_ident は、すべてのメッセージの前に付加されます。同じサーバーで複数の FPM インスタンスが動作している場合は、 一般的なニーズに合うようにデフォルト値を変更することができます。php-fpmなし
log_levelログレベル
alert, error, warning, notice, debug
noticeなし
log_limit1行(ログエントリ)の文字数にログ制限をかける。制限を超えた場合は、複数行に折り返されます。この制限は、メッセージのプレフィックスとサフィックスが存在する場合、それを含むすべてのログ文字に適用されます。ただし、改行文字はファイルディスクリプタにログを記録するときのみ存在するため、この制限には含まれません。つまり、syslogにログを記録する場合、改行文字は存在しないことになります。1024なし
log_bufferingログバッファリングは、ログラインがバッファリングされ、1回の書き込み操作で書き込まれるかどうかを指定します。値がfalseの場合、データはファイルディスクリプタに直接書き込まれます。このオプションは実験的なもので、重いロギングシナリオの場合、ロギングのパフォーマンスとメモリ使用量を改善できる可能性があります。このオプションは、常にバッファリングされる必要があるため、syslogにロギングする場合は無視されます。yesなし
emergency_restart_thresholdemergency_restart_interval で設定した時間内に、この数の子プロセスが SIGSEGV または SIGBUS で終了した場合、FPM は再起動します。値 ‘0’ は ‘Off’ を意味します。0なし
emergency_restart_intervalemergency_restart_interval が、graceful restart の開始時期を決定するために使用する時間間隔。これは、アクセラレータの共有メモリの偶発的な破損を回避するために有用です。
使用可能な単位: 秒(s), 分(m), 時(h), 日(d)
デフォルトの単位は秒。
0なし
process_control_timeout子プロセスがマスターからのシグナルに対する反応を待つための制限時間。
使用可能な単位: 秒(s), 分(m), 時(h), 日(d)
デフォルトの単位は秒。
0なし
process.maxFPM がフォークするプロセスの最大数を指定します。これは、多くのプールで動的 PM を使用する際に、グローバルなプロセス数を制御するために設計されています。
注意深く使用してください。
値が 0 の場合は、制限なしを意味します。
0なし
process.priorityマスタープロセスに適用するnice(2)優先度を指定する(設定されている場合のみ)
値は-19(最高優先度)から20(最低優先度)まで変化させることができます。
– FPMマスタープロセスをrootで起動した場合のみ動作します。
– プールプロセスは、特に指定がない限り、マスタープロセスの優先順位を引き継ぎます
未設定なし
daemonizeFPM をバックグラウンドに送る。デバッグのために FPM をフォアグラウンドに保つには ‘no’ を設定します。yesなし
rlimit_filesマスタープロセスのオープンファイルディスクリプターrlimitを設定する。システムの設定値なし
rlimit_coreマスタープロセスの最大コアサイズrlimitを設定する。
使用可能な値: ‘unlimited’、または、0以上の整数
システムの設定値なし
events.mechanismFPMが使用するイベント機構を指定します。以下が使用可能です:
– select (any POSIX os)
– poll (any POSIX os)
– epoll (linux >= 2.5.44)
– kqueue (FreeBSD >= 4.1, OpenBSD >= 2.9, NetBSD >= 2.0)
– /dev/poll (Solaris >= 7)
– port (Solaris >= 10)
未設定(自動検出)なし
systemd_intervalFPMをsystemdと統合して構築する場合、systemdにヘルスレポートを通知する間隔を秒単位で指定します。0を指定すると無効になります。
使用可能な単位は、s(秒)、m(分)、h(時)。
デフォルトの単位は秒。
10なし
include子プロセスの複数のプールを、異なるリスニングポートや異なる管理オプションで起動することができます。プールの名前は、ログや統計情報で使用されます。FPM が扱えるプールの数には制限がありません。いずれにせよ、システムが教えてくれるでしょう 🙂

1つまたは複数のファイルをインクルードします。glob(3) が存在する場合、glob(3) パターンからファイルの束をインクルードするために使用されます。このディレクティブは、ファイル中のあらゆる場所で使用することができます。相対パスも使用可能です。これらのパスには、プレフィックスがつきます:
– グローバルプレフィックスが設定されている場合 (-p 引数) は、そのプレフィックスを使用します。
– それ以外の場合は、/usr
記載なし/etc/php/8.1/fpm/pool.d/*.conf

www.conf

php-fpm.confのincludeの指定によって、pool.d/www.confが読み込まれる。
この中で www というセクションが定義されていて、これがプールと呼ばれている。

こちらもほぼDeepL先生に翻訳してもらう。

インストール直後は、プロセスマネージャーがdynamicに設定されており、かつ、pm.max_childrenに5が設定されている。
これはだいぶ小さいようなので広げておいて上げるのが良さそうだ。

設定項目説明デフォルト値設定値
prefixPer pool prefix
以下のディレクティブにのみ適用されます:
– ‘access.log’
– ‘slowlog’
– ‘listen’ (unixsocket)
– ‘chroot’
– ‘chdir’
– ‘php_values’
– ‘php_admin_values’
設定されていない場合は、グローバルプレフィックス (あるいは /usr) が代わりに適用されます。
備考: このディレクティブは、グローバルプレフィックスからの相対指定も可能です。
なし(説明参照)なし
user
group
Unix user/group of processes
備考:userは必須です。グループが設定されていない場合、デフォルトのユーザーのグループが使用されます。
なしwww-data
listenFastCGI リクエストを受け付けるアドレスです。
有効な構文は次のとおりです:
‘ip.add.re.ss:port’ – TCSソケットで指定されたIPv4アドレスの指定されたポートを待ち受けます。
‘[ip:6:addr:ess]:port’ – TCPソケットで指定されたIPv6アドレスの指定されたポートを待ち受けます。
‘port’ – TCPソケットですべてのアドレス(IPv6およびIPv4-mapped)の指定されたポートの待ち受けます。
‘/path/to/unix/socket’ – unixソケットで待ち受けます。
備考:この値は必須です。
なし(設定必須)/run/php/php8.1-fpm.sock
listen.backloglisten(2)のバックログを設定します。511
(FreeBSDとOpenBSDは-1)
なし
listen.owner
listen.group
UNIX ソケットが使用されている場合は、そのパーミッションを設定します。Linuxでは、Webサーバーからの接続を許可するために、読み取り/書き込みのパーミッションを設定する必要があります。BSD由来のシステムでは、パーミッションに関係なく接続を許可するものが多いようです。オーナーとグループは、名前または数値のIDで指定することができます。なしwww-data
listen.mode0660なし
listen.acl_users
listen.acl_groups
POSIXアクセス制御リストがサポートされている場合、これらのオプションを使用して設定することができます。
設定すると、listen.ownerとlisten.groupは無視されます。
なしなし
listen.allowed_clients接続を許可する FastCGI クライアントのアドレス (IPv4/IPv6) のリストです。
オリジナルの PHP FCGI (5.2.2+) の FCGI_WEB_SERVER_ADDRS 環境変数に相当します。tcp リスニングソケットでのみ意味を持ちます。各アドレスはカンマで区切る必要があります。この値を空白にすると、どの IP アドレスからも接続を受け付けます。
anyなし
process.priorityプールプロセスに適用するnice(2)優先度を指定する(設定されている場合のみ)。
値は -19 (最高優先度) から 20 (低優先度) の間で変化します。
備考:
– FPMマスタープロセスがrootとして起動されている場合のみ動作します。
– プールプロセスは、特に指定がない限り、マスタープロセスの優先順位を引き継ぎます。
未設定なし
process.dumpableプロセスユーザーやグループがマスタープロセスユーザーと異なる場合でも、プロセスダンプ可能フラグを設定する(PR_SET_DUMPABLE prctl)。これにより、プールユーザーのプロセスコアダンプの作成とプロセスのptraceが可能になります。noなし
pmプロセスマネージャが子プロセスの数をどのように制御するかを選択します。
使用可能な値:
static – 固定された子プロセス数(pm.max_children)
dynamic – 子プロセスの数は、pm.max_children、pm.start_servers、pm.min_spare_servers、pm.max_spare_serversに基づき動的に設定されます。このプロセス管理では、常に最低1つの子プロセスが存在することになります。
ondemand – 起動時に子プロセスは作成されません。新しいリクエストが接続されると、子プロセスはフォークされます。pm.max_children、pm.process_idle_timeoutが使用されます。
備考:この値は必須です。
なし(設定必須)dynamic
pm.max_childrenpm が ‘static’ に設定されているときに作成される子プロセスの数、 pm が ‘dynamic’ あるいは ‘ondemand’ に設定されているときの子プロセスの最大数を指定します。 この値は、同時に処理されるリクエストの数の制限を設定します。mpm_prefork を使用した ApacheMaxClients ディレクティブと同等です。 オリジナルの PHP CGI の PHP_FCGI_CHILDREN 環境変数に相当します。以下のデフォルトは、あまりリソースのないサーバーを想定しています。あなたのニーズに合うように pm.* を調整することを忘れないでください。
備考:pmが「static」、「dynamic」、「ondemand」に設定されている場合に使用します。
備考:この値は必須です。
なし(設定必須)5
pm.start_servers起動時に生成される子プロセスの数。(min_spare_servers + max_spare_servers) / 22
pm.min_spare_serversアイドル状態のサーバープロセス数の最小値の希望値。
備考:pmが’dynamic’に設定されているときのみ使用されます。
備考:pmが’dynamic’に設定されている場合は必須です。
なし1
pm.max_spare_serversアイドル状態のサーバープロセス数の最大値の希望値。
備考:pmが’dynamic’に設定されているときのみ使用されます。
備考:pmが’dynamic’に設定されている場合は必須です。
なし3
pm.process_idle_timeoutアイドル状態のプロセスが強制終了するまでの秒数を指定しま す。
注:pmが’ondemand’に設定されているときのみ使用されます。
10s10s
pm.max_requests各子プロセスが再起動するまでに実行すべきリクエストの数です。
これは、サードパーティライブラリのメモリリークを回避するために有用です。無限にリクエストを処理したい場合は ‘0’ を指定します。PHP_FCGI_MAX_REQUESTS と同じ意味です。
0500
pm.status_pathFPM のステータスページを表示するための URI。この値が設定されていない場合、どのURIもステータスページとして認識されません。このページには、以下の情報が表示されます:
pool – プールの名前
process manager – 静的、動的、またはオンデマンド
start time – FPM が起動した日時
start since – FPM が起動してから何秒経ったか
accepted conn – プールが受け付けたリクエストの数
listen queue – 接続待ちのキューにあるリクエストの数 (listen(2) の backlog を参照)
max listen queue – FPM が起動して以来、保留中のコネクションのキューにあるリクエストの最大数
listen queue len – 接続待ちのソケットキューの大きさ
idle processes – アイドル状態のプロセス数
active processes – アクティブなプロセスの数
total processes – idle + active processesの数
max active processes – FPMが起動してからの最大アクティブプロセス数
max children reached – pm がさらに子プロセスを開始しようとしたときに、プロセスの上限に達した回数 (pm ‘dynamic’ と ‘ondemand’ のみで動作する)
値はリアルタイムで更新されます。
出力例:
<ここから先も長いので省略、必要時には原本のファイルを参照>
未設定なし
pm.status_listenFastCGI ステータス要求を受け付けるアドレスです。これは、独立して要求を処理できる新しい不可視プールを作成します。これは、メインプールが長く実行されているリクエストでビジー状態になっている場合に便利です。
有効な構文は次のとおりです:
‘ip.add.re.ss:port’ – TCSソケットで指定されたIPv4アドレスの指定されたポートを待ち受けます。
‘[ip:6:addr:ess]:port’ – TCPソケットで指定されたIPv6アドレスの指定されたポートを待ち受けます。
‘port’ – TCPソケットですべてのアドレス(IPv6およびIPv4-mapped)の指定されたポートの待ち受けます。
デフォルト値:listenオプションの値
listen optionの値なし
ping.pathFPM の監視ページを呼び出すための ping URI。この値が設定されていない場合は、どの URI も ping ページとして認識されません。これは、FPM が生きていて応答しているかどうかを外部から調べるために使うことができます。
– FPM の可用性のグラフを作成する (rrd など)
– 応答がないサーバーをグループから外す(負荷分散)
– 運用チームへのアラートを発動する(24時間365日)
備考:値は、先頭のスラッシュ(/)で始まる必要があります。値は何でもかまいませんが、拡張子.phpを使用すると、実際のPHPファイルと衝突する可能性があるので、あまりお勧めできません。
未設定なし
ping.responseこのディレクティブは、pingリクエストのレスポンスをカスタマイズするために使用することができます。レスポンスは、200レスポンスコードでtext/plainとしてフォーマットされます。pongなし
access.logアクセスログファイル未設定なし
access.formatアクセスログフォーマット
有効な構文は次のとおりです:
<ここから先は長いので省略、必要時には原本のファイルを参照>
“%R – %u %t \”%m %r\” %s”なし
slowlogslow requests ログファイル(訳注:実行に時間が掛かったリクエストを記録するファイル)
備考:request_slowlog_timeoutが設定されているときは必須
未設定なし
request_slowlog_timeout1つのリクエストを処理する際のタイムアウトで、その後 PHP のバックトレースが ‘slowlog’ ファイルにダンプされます。0s’ の値は ‘off’ を意味します。
使用可能な単位: 秒(s), 分(m), 時(h), 日(d)
0なし
request_slowlog_trace_depthslow log スタックトレースの階層20なし
request_terminate_timeout一つのリクエストに対応するためのタイムアウトで、その後ワーカープロセスは強制終了されます。このオプションは、iniオプションの ‘max_execution_time’ が何らかの理由でスクリプトの実行を停止しない場合に使用する必要があります。値 ‘0’ は ‘off’ を意味します。
使用可能な単位: 秒(s), 分(m), 時(h), 日(d)
00
request_terminate_timeout_track_finishedアプリケーションが ‘fastcgi_finish_request’ を呼び出した後や、アプリケーションが終了してシャットダウン機能を呼び出している場合(register_shutdown_function で登録)、ini オプション ‘request_terminate_timeout’ で設定したタイムアウトは作動しません。このような場合でも、無条件にタイムアウト制限を適用することができます。noなし
rlimit_filesファイルディスクリプタ rlimit の設定システムの設定値なし
rlimit_core最大コアサイズ rlimit の設定
使用可能な値:’unlimited’、または、0以上の整数
システムの設定値なし
chroot開始時にこのディレクトリにChrootします。この値は、絶対パスで定義する必要があります。この値が設定されていない場合、chroot は使用されません。
備考: ‘$prefix’ でプレフィックスを指定すると、プールプレフィックスまたはそのサブディレクトリのいずれかに chroot することができます。
備考: chroot は素晴らしいセキュリティ機能であり、可能な限り使用すべきです。
未設定なし
chdir開始時にchdirするディレクトリ
備考:相対パスを使用することも可能です。
カレントディレクトリ
chrootの時は /
なし
catch_workers_outputWorker の stdout と stderr をメインエラーログにリダイレクトします。設定されていない場合、FastCGI仕様にしたがって、stdoutとstderrは/dev/nullにリダイレクトされます。
備考: 高負荷の環境では、ページ処理時間の遅延(数ミリ秒)を引き起こす可能性があります。
noなし
decorate_workers_outputワーカーの出力を、ログに書き込む子プロセスに関する情報を含むプレフィックスとサフィックスで装飾します。yesなし
clear_envFPM ワーカーの環境をクリアする
このプール設定で指定した環境変数が追加される前に、ワーカーの環境をクリアすることで、任意の環境変数が FPM ワーカー処理に到達するのを防止します。”no” に設定すると、すべての環境変数が PHP コードから getenv(), $_ENV, $_SERVER を通して利用できるようになります。
yesなし
security.limit_extensionsFPM が解析するメインスクリプトの拡張子を制限します。これにより、Web サーバー側での設定ミスを防ぐことができます。悪意のあるユーザーが他の拡張子を使用して php コードを実行するのを防ぐために、 FPM の拡張子を .php にのみ制限する必要があります。
備考: すべての拡張子を許可するには、空の値を設定します。
.phpなし
envLD_LIBRARY_PATHのような環境変数を渡します。すべての$VARIABLEは、現在の環境から取得されます。
env[HOSTNAME] = $HOSTNAME
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp
クリーンな環境なし
php_value/php_flag
php_admin_value/php_admin_flag
このワーカーのプールに特化した追加のphp.iniの定義です。これらの設定は、以前にphp.iniで定義された値を上書きします。ディレクティブは、PHP SAPI と同じです:
php_value/php_flag – PHP の ‘ini_set’ から上書き可能な標準的な ini 定義を設定することができます。
php_admin_value/php_admin_flag – これらのディレクティブは、PHP の ‘ini_set’ コールによって上書きされることはありません。
php_*flag の場合、有効な値は on, off, 1, 0, true, false, yes または no です。

‘extension’ を定義すると、extension_dir から対応する共有拡張機能を読み込みます。disable_functions’ または ‘disable_classes’ を定義すると、以前に定義した php.ini の値は上書きされず、代わりに新しい値が追加されます。

備考:パスINIオプションは相対指定が可能で、プレフィックス(pool、global、/usr)で展開されます。

php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com
php_flag[display_errors] = off
php_admin_value[error_log] = /var/log/fpm-php.www.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 32M
php.iniの値、および、と起動時に -d 引数で指定する値なし

WordPressのセットアップ

PHPがちゃんと動いている環境をテストのために作成。

WordPressをセットアップする。ほとんどTutorialsの手順でやっているので、手順の説明は省略。
ubuntu tutorials / Install and configure WordPress

$ sudo apt -y install apache2 ghostscript libapache2-mod-php mariadb-server php php-bcmath php-curl php-imagick php-intl php-json php-mbstring php-mysql php-xml php-zip
$ sudo mkdir /srv/www
$ sudo chown www-data: /srv/www/
$ curl https://wordpress.org/latest.tar.gz | sudo -u www-data tar zx -C /srv/www

$ sudo tee /etc/apache2/sites-available/wordpress.conf <<EOF > /dev/null
<VirtualHost *:80>
    DocumentRoot /srv/www/wordpress
    <Directory /srv/www/wordpress>
        Options FollowSymLinks
        AllowOverride Limit Options FileInfo
        DirectoryIndex index.php
        Require all granted
    </Directory>
    <Directory /srv/www/wordpress/wp-content>
        Options FollowSymLinks
        Require all granted
    </Directory>
</VirtualHost>
EOF
$ sudo a2ensite wordpress
$ sudo a2dissite 000-default
$ sudo a2enmod rewrite
$ sudo systemctl restart apache2

$ sudo mysql
MariaDB [(none)]> create database wordpress;
MariaDB [(none)]> create user wordpress@localhost identified by 'password';
MariaDB [(none)]> grant select,insert,update,delete,create,drop,alter on wordpress.* to wordpress@localhost;
MariaDB [(none)]> flush privileges;
MariaDB [(none)]> quit

$ sudo -u www-data cp /srv/www/wordpress/wp-config-sample.php /srv/www/wordpress/wp-config.php
$ sudo -u www-data sed -i 's/database_name_here/wordpress/' /srv/www/wordpress/wp-config.php
$ sudo -u www-data sed -i 's/username_here/wordpress/' /srv/www/wordpress/wp-config.php
$ sudo -u www-data sed -i 's/password_here/password/' /srv/www/wordpress/wp-config.php
$ sudo sed -i "/put your unique phrase here/d" /srv/www/wordpress/wp-config.php

WordPressにアクセスし(http://<あなたのサイト>)、最終設定をすれば完了。

状態を確認、だいたいホームラボの環境と似たようなのができた。

$ php --version
PHP 8.1.2-1ubuntu2.11 (cli) (built: Feb 22 2023 22:56:18) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.2, Copyright (c) Zend Technologies
    with Zend OPcache v8.1.2-1ubuntu2.11, Copyright (c), by Zend Technologies
$ apache2 -v
Server version: Apache/2.4.52 (Ubuntu)
Server built:   2023-03-01T22:43:55

さいごに

やりたいことがあって調べる → 現状に問題ありと気付く → 脇道に逸れて分かるまで調べる

ということで、やりたいことから少し外れたところで時間を掛けた。
弱点だなとも思っていたので、丁度いいといえば丁度いいのだが、なかなかスタートラインに立てない。

まぁでも、焦っても仕方ないか、足元から固めていこう。いつかきっと役に立つ。

広告
ろっひー

コメントはこちらから お気軽にどうぞ ~ 投稿に関するご意見・感想・他