突然、何の話かというと、PHPer Tea Night でLTをすることになったので、そのブログ版です。
https://phper-tea-night.connpass.com/event/291604/
PHPが動くとは!?
みんな大好き @sji さんのブログ記事が非常に参考になります。
https://tech.respect-pal.jp/php-helloworld/
この話を理解すると実行環境に対する解像度が上がる。つまり、PHPが動く実行環境というのは、以下のように整理できる。
- 実行環境
- SAPI
ちなみに、利用しているSAPIは、下記のメソッドで確認することができる。
1 | $ php -r "echo php_sapi_name();" |
例えば、bref は php-fpm を使っているので fpm-fcgi
Apach mod_php なら apache2handler
実行環境の種類
- 物理マシン
- 仮想マシン (EC2, LightSail, VirtualBox….)
- コンテナ (Docker, ECS, App Runner….)
- Serverless https://www.serverless.com/ (Lambda)
※コンテナとServerlessは意味合いとしては似てるけど、分けておきました。
LinuxやWindowsの物理マシンから、直接Webサービスを配信したことがある人は、もはや少数かもしれないけど、これはこれで実に大変。まずOSインストールできるの?から始まり、NICは認識されるの?アップデートで壊れたりしないの?と考慮することが多い。
仮想マシンは直感的で、実行環境に直接触れることも簡単。初心者にもある種優しいが、CI/CDなどを適切に考えていくのは難しい。世の中 Blue/Greenデプロイだなどと叫ばれるときに、仮想マシンとはいえ、環境依存しやすいタイプの実行環境を使うと、後々の引き剥がしが面倒くさい。例えば、依存が環境変数で制御できるなら良いけど、ソースコードにべったり書かれてたりすると……(ここで古傷が痛む人が続出する)
コンテナ、Serverlessは、さらに実行環境が載ってるPAASの知識も必要になってくる。コンテナはどうやって動いているのか、Lambdaはどんな仕組みなのか、そんなことを知らなければいけない。想像よりも構築が難しく、初心者が初手でやるのは大変。しかし、実行環境が使い捨てであることが明確であり、アプリケーションエンジニアがリリース単位で実行環境に変更を加えられるのはとても魅力がある。
何が言いたいかというと、実行環境は好きなの選んでくれという感じ。ただ、12 Factor App のことを頭の片隅にいれて、実行環境依存をなるべく排除していくのは大切。
よくある実行環境依存
- ローカルのログファイル
- ローカルの
/tmp
に雑に置かれたファイル - 特定OSに特化したワークアラウンド
- オーケストレーションの途中でコケる問題
- 多分、他にも色々ある
SAPI を選ぶ
実行環境が決まったら、SAPIを選びましょう。まあ、Swoole選んだらcli
だし、bref 選んだらfpm-fcgi
だしと、アーキテクチャーに縛られるパターンはあるけど、最近主流(だと思う)なDockerの開発環境 -> ECS, 仮想マシン みたいな開発スタイルだと、SAPI は自分たちで選択することになる。
っていうか、選択してる……よね?
SAPI の2大潮流
Apache + mod_php
-> prefork なので、PHPも処理できるスレッドが静的ファイルの処理にも使われる。この意味で、Apache + mod_php の組み合わせは効率が悪くなる可能性がある。この構成の素晴らしさは、HTTPサーバーが同梱されているということ。難しいこと考えなくてもPHPが動く!
1 | docker run --rm -p 8080:80 -v /tmp:/var/www/html php:8.2-apache-bullseye |
爆裂に簡単である。コンテナを実行環境に選んだ場合でも、1コンテナ1プロセスの考えに合致するし、メンテナンスの手間も少ない。
欠点としては、Apache mod_php はHTTP2に対応していないため、別途前段にロードバランサーなどを配置する必要があることです。
Nginx + php-fpm
-> ここ10年弱くらいは、脳死でこの組み合わせが選ばれている気がする。Nginx じゃなくても、Fastcgiが使えればいいので、Apache の MPM を event にして使うパターンも有る。
フロントに立つWebサーバーは、静的ファイルをWebサーバーとして処理を行い、PHPの処理はphp-fpmに移譲する形なので、それぞれ得意分野を対応する構成となっている。
欠点としては、Nginx と php-fpm のプロセスを同居させるのはベストプラクティスからは外れているので、真面目に考えるとNginx コンテナ、php-fpm コンテナ の2つに分かれる。
しかし、実行環境が複数コンテナに別れているのは運用的には面倒くさい。例えば、App Runner なんかでは ECR に push されたHTTPを喋るコンテナイメージを選択して動かすが、複数コンテナパターンだとこれができない。
Fargateで動かすとして、複数コンテナを使った構成は定義も面倒くさい。多分、慣れたら大したことは無いとは思うけど。
おすすめの構成
というわけで、面倒くさくないという1点において、実行環境はコンテナ、SAPIは mod_php を使うのが楽で良いです。AWSならALBを前段に配置するので、HTTP2問題も解決です。
CPUアーキテクチャーは関係ないのか?
そんなことより、CPUアーキテクチャーに気を使うことも大切です。例えば、AWSの App Runner は AMD64 でビルドしたコンテナイメージじゃないと動作しません。
※Apple Sillicon でビルドした ARM のコンテナイメージを使うと動きません。
仮想OSが本番環境の場合、ローカルはApple Silicon(ARM)で開発してて、本番はAMD64だとすると、 厳密には実行環境が変わってますよね?その辺意識しないと深いバグが出たときにつらいです。 もっというと AMD64 だとしても、Alpineイメージは gcc じゃないので、乱数生成など細かい箇所で違いがでてきます。コンテナはインフラも一緒だから動くって言ったじゃないかバカーってなりそうですが、現実はこんなもんです。
PHPerは黙って、PHP公式のdebian系イメージ!これが私のおすすめ。異論はありそうw
CPUアーキテクチャーの話は、JITコンパイラは関係がありまくりです。マシン語への置換が上手く行かずにSegVが出てしまうこともイシューで何度も報告されています。また、場合によっては負荷がかかったときのパフォーマンスの挙動も変わます。なので、現実問題として開発陣の使うCPUアーキテクチャーで多数派になるCPUアーキテクチャーを選んで、本番環境も合わせると良いと思います。
SAPIのパフォーマンス
バトルということなので、やっぱりベンチマーク取りたいよね?
実施環境
環境 |
---|
M2 MacBook Air (メモリ24GB) |
Docker Desktop for Mac |
macOS Ventura 13.4 |
ベンチマーク
誤差とかとってませんので、雑にオーダーを比べてください。
静的ファイル (html)
1 | $ curl localhost:8080/index.html |
実行環境 | trans/sec |
---|---|
Apache mod_php | 3217 |
Nginx php-fpm | 3133 |
動的ファイル (php)
1 | $ curl localhost:8080 |
実行環境 | trans/sec |
---|---|
Apache mod_php | 2811 |
Nginx php-fpm | 3187 |
動的ファイル with IO負荷 usleep(50000)
1 | $ curl localhost:8080/index.php |
実行環境 | trans/sec |
---|---|
Apache mod_php | 432 |
Nginx php-fpm | 412 |
静的動的ファイルmix
実行環境 | trans/sec |
---|---|
Apache mod_php | 2758 |
Nginx php-fpm | 2894 |
静的動的ファイルmix with IO負荷 usleep(50000)
実行環境 | trans/sec |
---|---|
Apache mod_php | 918 |
Nginx php-fpm | 918 |
まとめ
もう少し、差が出るかと思ったけど、両者のパフォーマンスのオーダーはあまり変わらない。もしかしたら、静的ファイルがすごく多い場合は、Nginx の方が性能が出るかもしれないけど、Apache は相当頑張れそう
-> AWSなんかだと、cloudfront で静的ファイルキャッシュしちゃえば影響がないとも言える
というわけで、PHPを動かすときは、SAPIの特性を理解した上で実行環境を選びましょう。コンテキストによって求められる性能が変わりますし、運用の難易度も変わります。みんながこうしているからではなく、自分の意志で最適なものを選択しましょう!