php-fpmのエラーにぶつかりながら設定の最適化を図る

# はじめに

php-fpmのconfについて、各数値の設定を決定する基準をまとめた記事が見つからず、エラーに体当たりしながらこんな感じかなーってまとめました。
php-fpmへの理解そのものの理解が間違っている可能性も十分に有り得ますので識者の方々のアドバイスを心からお待ちしております。

# php-fpmのエラーについて

php-fpmを'pm = dynamic'で設定している時、他の設定がうまくいっていないと以下のようなエラーが発生する

```/var/log/php-fpm/error.log
WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning X children, there are X idle, and X total children
WARNING: [pool www] server reached pm.max_children setting (XXX), consider raising it
```

そもそも、'pm = dynamic'というのはどういう設定なのか

> the number of child processes are set dynamically based on the following directives. With this process management, there will be always at least 1 children.

和訳するとこういうこと

> 子プロセスの数は、pm.max_children, pm.start_servers, pm.min_spare_server, pm.max_spare_serverの設定に基づいて設定される。このプロセス管理によって、常に最低一つの子プロセスが立ち上がっている。

**pm.max_children, pm.start_servers, pm.min_spare_server, pm.max_spare_server の値次第で子プロセスの数が左右しますよ。**というものである。

では、pm.max_children, pm.start_servers, pm.min_spare_server, pm.max_spare_serverとはそれぞれどのような設定であるのかを以下にまとめた。

## pm.max_children

> the maximum number of children that can be alive at the same time.

→ (和訳)同時に処理をする可能性がある子プロセス数
## pm.start_servers

> the number of children created on startup.

→ (和訳)プロセス開始時に生成される子プロセス

## pm.min_spare_server

> the minimum number of children in 'idle' state (waiting to process). If the number of 'idle' processes is less than this number then some children will be created.

→ (和訳) 待ち状態の子プロセスの最小の数。もし待ち状態のプロセスの数がここに設定されている数値より少ない場合、 何個かの子プロセスが作成される。

## pm.max_spare_server

> the maximum number of children in 'idle' state (waiting to process). If the number of 'idle' processes is greater than this number then some children will be killed.

→ (和訳) 待ち状態の子プロセスの最大の数。もし待ち状態のプロセスの数がここに設定されている数値より多い場合、 何個かの子プロセスが消される。

以上の内容を前提に、php-fpmのwarningエラー発生時の設定のチューニングについてまとめてみる

# 使うもの(あると良い物)

## htop(コマンド)

htopコマンドを使うと、プロセスの様子を確認できる

<img width="500" alt="htopコマンド" src="https://qiita-image-store.s3.amazonaws.com/0/57635/c2c0ca2b-dccd-9add-b221-9182950b083b.png">

無い場合はyumでinstall

```
yum install htop
```

## tailコマンド

php-fpmのエラーを垂れ流しにする

```
tail -f /var/log/php-fpm/error.log
```

## abコマンド(Apache Bench)

負荷を測定する
以下コマンドの場合、100ユーザーが同時に1リクエスト(合計100リクエスト)をしたことになる。

```
ab -n 100 -c 100 [テストするページのURL]
```

### cf) Apache Bench テストに関して
[Apache Benchでサクッと性能テスト](http://qiita.com/flexfirm/items/ac5a2f53cfa933a37192)
[Apache Benchの結果の見方](http://qiita.com/nnmr/items/2be82691e19da261c4e4)

# 手順

## 1.とりあえず一番シンプルな設定をする

**数字は何れもなるべく低い方がメモリの負荷は低いがfpmとしての効果も低い**ということが原則で、メモリの負荷を見つつ、少しずつ数字を調整 ＆ エラーが出たら見直しを繰り返す。

```/etc/php-fpm.d/www.conf
; The number of child processes to be created when pm is set to 'static' and the
; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'.
; This value sets the limit on the number of simultaneous requests that will be
; served. Equivalent to the ApacheMaxClients directive with mpm_prefork.
; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP
; CGI.
; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand'
; Note: This value is mandatory.
pm.max_children = 10

; The number of child processes created on startup.
; Note: Used only when pm is set to 'dynamic'
; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2
pm.start_servers = 3

; The desired minimum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.min_spare_servers = 2

; The desired maximum number of idle server processes.
; Note: Used only when pm is set to 'dynamic'
; Note: Mandatory when pm is set to 'dynamic'
pm.max_spare_servers = 5
```

## php-fpmとhttpの再起動

```
/etc/init.d/php-fpm restart
/etc/init.d/httpd restart
```

# 2.負荷の測定

## tailコマンドの準備

```
tail -f /var/log/php-fpm/error.log
```

## Apache Benchテスト

tailコマンドでログ待機している画面とは違う画面でやってください。
(-n,-cの数値は各自の測定したい設定で可)

今回は100ユーザーが同時に1リクエスト(合計100リクエスト)をした想定で負荷テストを行う。

```
ab -n 100 -c 100 [テストするページのURL]
```

# 3.ログの確認

ここでエラーログが出ていなかったら、一旦OK
ログが出ていなかったら、[4]の各数値を調整して、再び負荷テストでメモリの負荷とエラーログの有無を確認して、各数値を確定していく。

# 4.confの調整

以下に挙げるような調整を行う。

* pm.start_serversを上げてスタート段階での待機プロセスの数を増やす。  
→ pm.max_childrenの上限を超える場合があるので、pm.max_childrenと共に調整する。
* pm.start_serversを上げてプロセス数を増やす。  
→ 実行中の待機プロセスが増える可能性があるため、pm.start_serversを上げてスタート段階での待機プロセス(処理済)の数を増やす。  
→ pm.max_childrenの上限を超える場合があるので、pm.max_childrenと共に調整する。

## チューニングに於ける注意点

### 負荷確認

負荷テスト中にhtopでcpu,メモリの負荷を確認し、数値を無闇に上げ過ぎないように気をつける。

### 各数値のルール

* pm.start_serversはpm.max_children,pm.max_spare_serversを超えてはならない
* pm.min_spare_serversはpm.max_children,pm.start_servers,pm.max_spare_serversを超えてはならない
* pm.max_spare_serversはpm.max_childrenを超えてはならない

これを守らないとエラーになったり、デフォルトで設定した数値を使用して処理が行われるので想定した挙動にならなくなるので注意する必要がある。

### 注意点:www.conf に変更を加えたら必ず再起動

```
/etc/init.d/php-fpm restart
/etc/init.d/httpd restart
```

# 5.各エラーログの対応

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

```/var/log/php-fpm/error.log
WARNING: [pool www] server reached pm.max_children setting (XXX), consider raising it
```
これは、pm.max_childrenの値が小さい場合に発生する。
ここで、htopコマンドでphp-fpmプロセスがどう立ち上がっているかを確認してみる。
htopコマンドでphp-fpmプロセスのみを表示したい場合、F4キーを押下し、"php-fpm"を入力してフィルタリングする。

<img width="500" alt="スクリーンショット 2016-06-30 19.52.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/57635/7eb96671-4867-86fd-6b13-c55902ebf1e1.png">

php-fpm(master process)がの下に、"php-fpm: pool www"(子process)が3つ存在していることが分かる。
/etc/php-fpm.d/www.conf で設定したpm.start_serversの数値が、php-fpmを起動させている時にデフォルトで待機するプロセスの数になる。

```/etc/php-fpm.d/www.conf
pm.start_servers = 3
```

<img width="500" alt="スクリーンショット 2016-06-30 20.07.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/57635/7d6f751c-f230-52ee-c1dd-6f8f8137f6a7.png">

これで負荷テストを行うと、デフォルトで待機している3プロセス分に追加するような形で子プロセスが増えていく。
今回の場合、pm.max_childrenは10に設定しているため、増加する子プロセスが10に達した時点で、残りの子プロセスがあったとしても中断され、エラーが発生する。

```/etc/php-fpm.d/www.conf
pm.max_children = 10
```

### pm.max_childrenの値を大きくする

という訳で、残りの子プロセスも全部裁ききるために、pm.max_childrenの値を上げる。

### 注意点:www.conf に変更を加えたら必ず再起動

```
/etc/init.d/php-fpm restart
/etc/init.d/httpd restart
```

### 負荷テスト

[2]の負荷テストを行いエラーログを確認する。
再度同じエラーが出た場合は、更に数値を上げて再起動 → 負荷テストを繰り返す。


## WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning X children, there are X idle, and X total children

```/var/log/php-fpm/error.log
WARNING: [pool www] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning X children, there are X idle, and X total children
```

このエラーは単純にpm.start_serversの値を上げることで対応できるが、以下パターンで対応が若干別れる。
また、pm.max_spare_serversを上げていくと、一回のプロセスで生成される子プロセスの数が増加するため、pm.max_childrenの数値を上げる必要も場合によっては出てくる。

* "there are 0 idle"が連続する場合
* "there are X idle"の数値が何かしらの数字で連続する場合

### "there are 0 idle"が連続する場合

こちらは実質で待機プロセスが発生していないのでそのままでも問題はない。
参考 : http://kakakakakku.hatenablog.com/entry/2016/04/13/222912

もし、メモリに余裕があるのならば、エラーが無くなるまでpm.start_serversの値を上げていく。

### "there are X idle"の数値が何かしらの数字で連続する場合

こちらも、数値がpm.max_spare_serversを超えるタイミングが無いのであればそのままでも恐らく問題はない。
超ている場合は、pm.max_spare_serversを超えるエラーが無くなるまでpm.start_serversの値を上げていく。

### 注意点:www.conf に変更を加えたら必ず再起動

```
/etc/init.d/php-fpm restart
/etc/init.d/httpd restart
```