この記事は何か
- Nginx + PHP-FPM 構成にてPHPアプリケーションを構築する
- ALBでロードバランシングさせる
- 母体はECS Fargateで構築する
上記構成にてアプリケーションを構築する際、(個人的に)意外と見逃しがちだけど重要だと思う設定値についてまとめました。
あくまで個人メモのため、随時更新したり、記述が正確でない箇所があるかと思いますが、ご容赦いただけますと幸いです。
この記事で書いてないこと
必要な設定値は他にもたくさんありますが、全部書くと量が大変多くなってしまうため、(個人的に)意外と見逃しがちだけど重要だと思う設定値に絞って記載しています。
繰り返しになりますが、「何が重要か」については個人の意見・所感ですので、ご容赦いただけますと幸いです。
Nginx設定値
タイムアウトまわり
send_timeout
- Nginxが、クライアント(ブラウザ)にレスポンスを返すまでのタイムアウト時間
keepalive_timeout
- Keep-Alive接続がアイドル状態でどのくらいの時間待機するかを指定する
- ALBのアイドルタイムアウト時間以上の秒数を設定する必要がある。参考
fastcgi_read_timeout
- NginxがFastCGIサーバー(例えば、PHP-FPM)からのレスポンスを待機する最大時間
fastcgi_send_timeout
- NginxがFastCGIサーバーにリクエストボディを送信する際に待機する最大時間
fastcgi_connect_timeout
- NginxがFastCGIサーバーへの接続を確立する際に待機する最大時間
fastcgi_next_upstream
- NginxがFastCGIサーバーへのリクエストの再試行(次のサーバーへの再試行)を行う際の条件
-
off
で指定しておくと、再試行を行わない
プロセスまわり
worker_processes
- Nginxのグローバルセッション設定項目
- 起動されるワーカプロセス数を示す。ワーカプロセスがクライアントからのコネクションをすべて処理する
-
auto
にしておくと、CPUコア数を元に自動的に設定してくれる。参考 - CPUコア数の確認方法は、こちらの記事が参考になります。
nproc
コマンド実行でいけるはず - Fargateの場合、CPUコア数はFargateタスクサイズで変動します。タスクサイズとの関係性については、こちらの記事でもまとめられています。
worker_connections
- worker毎のclient最大同時接続数
- つまり、Nginxサーバー自体の最大同時接続数は、worker_connections × worker_processes
- 設定できる最大値は、OSのエフェメラルポート最大数まで。以下コマンドなどで確認可能。
cat /proc/sys/net/ipv4/ip_local_port_range
- また、
worker_rlimit_nofile
より小さい値で設定する必要がある
worker_rlimit_nofile
- Nginxのworker_connectionsの1workerプロセスにおける、 ファイルディスクリプタ の上限値。
-
Too many open filesエラー
の発生を防ぐために、設定する必要がある。参考 -
worker_connections
の2〜4倍の値で設定するのが良いといわれている - OSで扱うことができるファイルディスクリプタの上限数を超えてはいけない。OSのファイルディスクリプタ上限数は以下コマンドなどで確認可能。
cat /proc/sys/fs/file-max
multi_accept
- onにすると、workerプロセスが1つ以上の新しいコネクションを受け入れられるようになる。無効の場合、ワーカープロセスは一度に1つの新しい接続を受け入れる。
- あくまで、workerプロセスのコネクション受け入れ方の話であって、同時接続数とは関係ない(1度に1つの接続しか受け入れられないからといって、最大同時接続数が1になるわけではない)
- onにすると1つのワーカープロセスを集中的に使うようになるため、リソースを節約できる などの利点があるようだ。(要調査)
-
multi_accept: on
に設定している例が多そうな印象だが、以下などを参考に、挙動として何が変わるのかはもう少し調べたい
セキュリティ考慮
server_tokens
- エラーメッセージやレスポンスのServerヘッダに、Nginxのバージョン情報を含めないようにする
- http, server, locationコンテキストにて設定可能
ALB
アイドルタイムアウト
以下の2つに利用される。
- ALBがリクエストを送ったインスタンスに対し、何秒待ってから504を返すかに使われる秒数
- TCPコネクション確立後、ALBが解放し始めるまでの時間(ロードバランサーが、アイドル状態にあると判断した接続を閉じるまでの秒数)
PHP-FPM
タイムアウトまわり
request_terminate_timeout
- 単一のリクエストを処理する際のタイムアウト
- この時間を過ぎるとプロセスがkillされる
ユーザ権限まわり
listen.owner, listen.group, listen.mode
- Nginx <> PHP-FPM間がUNIXソケット通信の場合に、パーミッションを設定する
プロセスまわり
pm
- プロセスマネージャが子プロセスの数を制御する方法。
- static, ondemand, dynamic の3つのうちいずれかを指定することができる。
-
static
: 子プロセスの数は固定 (pm.max_children) -
ondemand
: プロセスを必要に応じて立ち上げる。 dynamicとは対照的に、リクエストされると pm.start_serversで指定しただけサービスを開始する -
dynamic
: 子プロセスの数は、 pm.max_children, pm.start_servers, pm.min_spare_servers, pm.max_spare_servers の内容に基づいて動的に設定される
-
pm.max_children
- pmがstaticの場合は作成される子プロセスの数、pmがdynamicの場合は作成される子プロセスの最大数
- pmがdynamicの場合、OSのメモリ(使用可能なメモリ)に応じて、適切な値を設定する必要がある。(値が適切でないと、たとえばアクセスが集中した際に、メモリオーバーになったりする)
- こちらの記事がわかりやすいです。母体がFargate, OSがAlpine Linuxの場合は、以下の流れで実施しました。
-
apk add procps
を実行して、procpsを追加する。(プロセスごとのメモリ使用量を分析するため) - PHPプロセスが使用可能なメモリを計算する
- freeコマンドで確認する方法もありますが、自分は
ps aux | grep -v php-fpm
で表示される、php-fpm以外のプロセスのRSSを合計して算出しました
- freeコマンドで確認する方法もありますが、自分は
- PHP-FPMプロセスが確保している平均メモリ量を計算する
ps --no-headers -o "rss,cmd" -C php-fpm | awk '{ sum+=$1 } END { printf ("%d%s\n", sum/NR/1024,"M") }'
-
2 / 3
の結果が、pm.max_children
の設定値目安です。
pm.start_servers
- 起動時に作成される子プロセスの数。pmがdynamicの場合にのみ使われる
-
max_children * 1/4
が、設定値の目安
pm.min_spare_servers
- アイドル状態のサーバープロセス数の最小値。 pmがdynamicの場合にのみ使われる
-
max_children * 1/4
が、設定値の目安
pm.max_spare_servers
- アイドル状態のサーバープロセス数の最大値。pmがdynamicの場合にのみ使われる
-
max_children * 3/4
が、設定値の目安
その他
decorate_workers_output
-
decorate_workers_output = no
にしておくと、[pool www] child 105 said into stderr:
みたいな接頭辞をログから取り除くことができる。(PHP7.3以降。参考)
PHP(php.ini)
タイムアウトまわり
max_execution_time
- PHPスクリプトの実行時間制限。
- デフォルトは30秒。
- ただし、sleep()やfile_get_contents()の関数実行中の待機時間は、時間のカウントに含まれない点に注意。詳しくは、PHPマニュアルの注釈を参照。
注意:
関数 set_time_limit() と設定ディレクティブ max_execution_time は、 このスクリプト自体の実行時間にのみ影響を与えます。 system() を用いたシステムコール、ストリーム操作、 データベースクエリ等のスクリプト実行以外で発生する処理にかかった時間は スクリプトが実行される最大時間を定義する際には含まれません。 ただし、Windows ではこれは当てはまりません。 計測された時間は実際の時間と等しくなります。
セキュリティ考慮
expose_php
- onにすると、サーバーで利用しているPHPのバージョンがHTTPヘッダー(
X-Powered-By
)に含まれる - なので、不必要に公開したくない場合は、offにするのが好ましい(Nginxの、
server_tokens
と合わせて確認する)
display_errors, display_startup_errors
- エラー, 起動時のエラーを画面に出力するかどうか
- エラー内容が画面表示されると、セキュリティ的に危険なため、本番環境, その他一般公開するサイトでは
off
にしておくのが好ましい。
その他
error_reporting
- どのレベルのエラーを出力するかを設定する
- PHP8前後で、デフォルトの設定値が変わっているので、バージョンUp対応時は要確認
- PHP8以前:
E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED
(E_NOTICE, E_STRICT, E_DEPRECATEDレベルのエラーは出力されない) - PHP8以降:
E_ALL
(すべてのエラー, 警告が出力される)
- PHP8以前:
ECS Fargate タスク定義
ulimits
- コンテナが使用できるオープンファイルの数の制限
- デフォルトのnofileソフト制限は1024で、ハード制限は4096
- Nginxが使用する可能性のあるファイルディスクリプタ(= worker_processes × worker_rlimit_nofile)以上である必要がある
その他
タイムアウト設定について
タイムアウトまわりは、複数リソースの設定値が関連しあっているが、ポイントは以下。
- 基本的に前段(ロードバランサ, Webサーバー)ほどタイムアウトの設定値を大きく、後段(アプリケーション稼働本体)ほど小さくする必要がある。
- もし後段のシステムでのタイムアウト値が長いと、クライアントにエラーが返ってるのに裏で処理は成功している、という事態になりかねない。
参考情報
- https://developers.play.jp/entry/2023/04/13/184019
- https://log.dot-co.co.jp/nginx-perfomance/
- https://gakumon.tech/nginx/nginx_simple_directives.html
- https://qiita.com/mikene_koko/items/85fbe6a342f89bf53e89
- https://qiita.com/hclo/items/35f00b266506a707447e
- https://qiita.com/taichitk/items/5cf2e6778f1209620e72
- https://qiita.com/shiba_it/items/313ebf890872301fd06d
- https://www.yuulinux.tokyo/21674/
- https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_definition_parameters.html#ContainerDefinition-ulimits
- https://www.php.net/manual/ja/install.fpm.configuration.php
- https://www.php.net/manual/ja/ini.list.php