Ubuntu20.04 と Apache2 で HTTP/2 化

nginx が Apache HTTP Server を抜いてWebサーバの１位になったそうですが、PHPな世界では Apache は未だ沢山使われています。
Apache2.4 で **HTTP/1.1** から **HTTP/2** に切り替える機会があったので、その手順と、高負荷テスト方法を残しておきます。

**HTTP/2** は、より多くのスレッドやメモリが必要となるため、アクセス数の多いサイトでは十分注意の上で導入して下さい。

# 環境
## バージョン情報

```
$ cat /etc/os-release 
NAME="Ubuntu"
VERSION="20.04.3 LTS (Focal Fossa)"

$ apache2 -v
Server version: Apache/2.4.41 (Ubuntu)

$ apachectl -V | grep MPM
Server MPM:
```

## リソース情報

```
$ free -m
              total        used        free      shared  buff/cache   available
Mem:           3928         613        1790          67        1524        2986
Swap:          6143         443        5700

$ mysql
mysql> show variables like 'max_allowed_packet';
+--------------------+----------+
| Variable_name      | Value    |
+--------------------+----------+
| max_allowed_packet | 67108864 |
+--------------------+----------+

mysql> show variables like 'innodb_buffer_pool_size';
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| innodb_buffer_pool_size | 134217728 |
+-------------------------+-----------+

mysql> show variables like 'innodb_log_file_size';
+----------------------+----------+
| Variable_name        | Value    |
+----------------------+----------+
| innodb_log_file_size | 50331648 |
+----------------------+----------+

mysql> show engine innodb status\G
Total large memory allocated 137035776
Dictionary memory allocated 753192
Buffer pool size   8192
Free buffers       6630
```


# 手順
**MPM event** ＋ **PHP-FPM** の構成にします。

## PHP-FPM の導入

```
apt install php7.4-fpm

a2enmod proxy_fcgi setenvif
a2enconf php7.4-fpm
a2dismod php7.4

apache2ctl configtest
```

## prefork から event に変更

```
a2dismod mpm_prefork
a2enmod mpm_event

systemctl restart apache2
systemctl restart php7.4-fpm
```

## HTTP/2 の有効化

```
a2enmod http2
systemctl restart apache2
```

## バーチャルホストの設定変更

```:/etc/apache2/sites-available/virtual-host.conf
Protocols h2 http/1.1
```

## チューニング
アクセスが集中してもサーバがダウンしないよう調整します。

### mpm_event パラメータの調整

```:/etc/apache2/mods-available/mpm_event.conf
ServerLimit  700
StartServers   2
MinSpareThreads  25
MaxSpareThreads  75
ThreadLimit  64
ThreadsPerChild  25
MaxRequestWorkers  700
MaxConnectionsPerChild  1000
```

### php.ini パラメータの調整
FPM で php.ini の場所が変わるので注意。

```:/etc/php/7.4/fpm/php.ini
memory_limit = 256M
post_max_size = 128M
upload_max_filesize = 128M
```

### FPM パラメータの調整

```:/etc/php/7.4/fpm/pool.d/www.conf
pm = dynamic
pm.max_children = 100
pm.start_servers = 8
pm.min_spare_servers = 4
pm.max_spare_servers = 8
pm.max_requests = 1000
```

実際には、Excelで設定ワークシートを作成しましたが、計算式については十分な裏どりが出来ていないので、公にしません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/b4eb9b4a-5529-6a64-6e08-b44d9a836e6d.png)

Chromeでサイトに接続し、デベロッパーツールでプロトコルが **h2** になっていることを確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/a7b8d245-0192-b17a-488b-17a6c25b0180.png)

# 高負荷テスト
## Apache JMeterを使う場合
JMeterは、昔からあるWebアプリケーションストレスツールです。[ここ](https://jmeter.apache.org/download_jmeter.cgi)からダウンロードできます。
Windowsの場合、setenvを次のようにすると日本語化できます。

```:apache-jmeter-x.x.x/bin/setenv.bat
set JMETER_LANGUAGE=-Duser.language="ja" -Duser.region="JP"
```

binフォルダにあるjmeter.batで起動します。

スレッドグループを作成し、HTTPリクエストの量と期間を設定していきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/1337f0fa-724a-5817-1c4c-19e3b0d47bdf.png)

スレッドグループの下にHTTPリクエストを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/49c9f055-a30c-38c2-91ed-2ba25cc61153.png)

接続先の情報を入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/b07dbe38-c7e3-40ec-385d-7844344ef28d.png)

結果を受け取るリスナーを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/6771aaaa-7768-c1df-e923-0fbc71a99297.png)

準備ができたので走らせてみましょう。
が、その前に、サーバ側で`vmstat 1`コマンドを投入し、リソースをモニタリングしておきます。

ここでは、5秒間100スレッド（つまり秒間20アクセス）で指定してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/7be5beba-b0fd-d701-4b78-ee976d1d88c4.png)

サーバ側で`vmstat`の表示を確認します。プロセス数がみるみる増えていくはずです。

```
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache    si   so    bi    bo   in   cs us sy id wa st
 6  0  13312 2070800  91720 722412   16    0    16  2920 3102 4801 59  5 32  4  0
 9  0  13312 2029308  91720 723908    0    0     0  2776 4019 5632 92  5  2  2  0
10  0  13312 2015832  91724 724564    0    0     0  3940 4815 7285 92  8  0  0  0
14  0  13312 1961132  91724 725220    0    0     0  3300 3821 6217 93  7  0  0  0
```

もしCPU利用率が100%近くで推移しているなら、CPUがボトルネック（メモリに対してCPUの能力が低い）と判断できます。
この場合、プロセスの起動に伴うオーバーヘッドがあるので、`pm`を`dynamic`から`static`に変えると改善されるかもしれません。

Latency（リクエストを投げてからレスポンスが戻るまでの時間）は、JMeterのリスナーから確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/aa746b68-3c6a-75c5-7750-54b67babe22f.png)

## Apache Benchを使う場合
コマンドラインで使えるシンプルなツールです。

```
ab -n 100 -c 10 https://example.com
```

という感じで指定します。
`-n`は総リクエスト数、`-c`は同時リクエスト数です。
最後にレポートが表示されます。