ECS Fargate, ABL, Nginx, PHP-FPM構成時にチェックする設定値

# この記事は何か
- Nginx + PHP-FPM 構成にてPHPアプリケーションを構築する
- ALBでロードバランシングさせる
- 母体はECS Fargateで構築する

上記構成にてアプリケーションを構築する際、（個人的に）意外と見逃しがちだけど重要だと思う設定値についてまとめました。
あくまで個人メモのため、随時更新したり、記述が正確でない箇所があるかと思いますが、ご容赦いただけますと幸いです。

# この記事で書いてないこと
必要な設定値は他にもたくさんありますが、全部書くと量が大変多くなってしまうため、**（個人的に）意外と見逃しがちだけど重要だと思う**設定値に絞って記載しています。
繰り返しになりますが、「何が重要か」については個人の意見・所感ですので、ご容赦いただけますと幸いです。

# Nginx設定値
## タイムアウトまわり
#### send_timeout
- Nginxが、クライアント(ブラウザ)にレスポンスを返すまでのタイムアウト時間

#### keepalive_timeout
- Keep-Alive接続がアイドル状態でどのくらいの時間待機するかを指定する
- **ALBのアイドルタイムアウト時間以上の秒数を設定する必要がある。**[参考](https://repost.aws/ja/knowledge-center/apache-backend-elb)

#### 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コア数を元に自動的に設定してくれる。[参考](https://qiita.com/toshihirock/items/f3aac142882c9c320b7f)
- CPUコア数の確認方法は、[こちらの記事](https://codenote.net/linux/4076.html)が参考になります。`nproc`コマンド実行でいけるはず
- Fargateの場合、CPUコア数はFargateタスクサイズで変動します。タスクサイズとの関係性については、[こちらの記事](https://kenzo0107.github.io/2021/04/29/2021-04-30-nginx-process-count-on-fargate/)でもまとめられています。

#### worker_connections
- worker毎のclient最大同時接続数
- つまり、Nginxサーバー自体の最大同時接続数は、worker_connections × worker_processes
- 設定できる最大値は、OSのエフェメラルポート最大数まで。以下コマンドなどで確認可能。
```shell-session
cat /proc/sys/net/ipv4/ip_local_port_range
```
- また、`worker_rlimit_nofile`より小さい値で設定する必要がある

#### worker_rlimit_nofile
- Nginxのworker_connectionsの1workerプロセスにおける、 ファイルディスクリプタ の上限値。
- `Too many open filesエラー`の発生を防ぐために、設定する必要がある。[参考](https://www.1x1.jp/blog/2013/02/nginx_too_many_open_files_error.html)
- `worker_connections`の2〜4倍の値で設定するのが良いといわれている
- OSで扱うことができるファイルディスクリプタの上限数を超えてはいけない。OSのファイルディスクリプタ上限数は以下コマンドなどで確認可能。
```shell-session
cat /proc/sys/fs/file-max
```

#### multi_accept
- onにすると、workerプロセスが1つ以上の新しいコネクションを受け入れられるようになる。無効の場合、ワーカープロセスは一度に1つの新しい接続を受け入れる。
- あくまで、workerプロセスのコネクション受け入れ方の話であって、同時接続数とは関係ない（1度に1つの接続しか受け入れられないからといって、最大同時接続数が1になるわけではない）
- onにすると1つのワーカープロセスを集中的に使うようになるため、リソースを節約できる などの利点があるようだ。（要調査）
- `multi_accept: on`に設定している例が多そうな印象だが、以下などを参考に、挙動として何が変わるのかはもう少し調べたい
    - https://doudonn.com/saba/2571/
    - https://ryuichi1208.hateblo.jp/entry/2022/01/09/112908

## セキュリティ考慮
#### server_tokens
- エラーメッセージやレスポンスのServerヘッダに、Nginxのバージョン情報を含めないようにする
- http, server, locationコンテキストにて設定可能

# ALB
#### アイドルタイムアウト
以下の2つに利用される。

1. ALBがリクエストを送ったインスタンスに対し、何秒待ってから504を返すかに使われる秒数
2. 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のメモリ（使用可能なメモリ）に応じて、適切な値を設定する必要がある。（値が適切でないと、たとえばアクセスが集中した際に、メモリオーバーになったりする）
- [こちらの記事](https://thoames.hatenadiary.jp/entry/2020/05/12/073656)がわかりやすいです。母体がFargate, OSがAlpine Linuxの場合は、以下の流れで実施しました。

1. `apk add procps`を実行して、procpsを追加する。（プロセスごとのメモリ使用量を分析するため）
1. PHPプロセスが使用可能なメモリを計算する
    1. freeコマンドで確認する方法もありますが、自分は`ps aux | grep -v php-fpm`で表示される、php-fpm以外のプロセスのRSSを合計して算出しました
1. PHP-FPMプロセスが確保している平均メモリ量を計算する
    1. `ps --no-headers -o "rss,cmd" -C php-fpm | awk '{ sum+=$1 } END { printf ("%d%s\n", sum/NR/1024,"M") }'`
4. `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以降。[参考](https://qiita.com/takamichi/items/cbb1a9edb44c0b7f5594)）

# PHP（php.ini）
## タイムアウトまわり
#### max_execution_time
- PHPスクリプトの実行時間制限。
- デフォルトは30秒。
- ただし、sleep()やfile_get_contents()の関数実行中の待機時間は、時間のカウントに含まれない点に注意。詳しくは、[PHPマニュアルの注釈](https://www.php.net/manual/ja/function.set-time-limit.php#refsect1-function.set-time-limit-notes)を参照。

> 注意:
関数 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`（すべてのエラー, 警告が出力される）

# 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