FrankenPHPを使ってみた
Published:
Updated:
FrankenPHPとは
FrankenPHPとはGo言語で書かれた、新しいPHPサーバーです。
主な特徴は、
- Caddy上に構築されたPHP8.2以降のサーバー
- シングルバイナリ
- ハイパフォーマンス
- HTTP/2およびHTTP/3のサポート
- Early Hintsのサポート
- Mercureを利用したWebSocket
- Let’s EncryptまたはZeroSSLを利用した証明書の自動更新
また、Laravel OctaneによりLaravelは完全にサポートされています。
FrankenPHPを使う
PHPサーバーを構築する際、一般的には次のような構成になると思います。
- Apache + mod_php
- Nginx + FPM
FrankenPHPの場合は、FrankenPHPのみで動作します。
Dockerを利用する場合は、次のコマンドで実行できます。
docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp dunglas/frankenphp
1つのコンテナだけで済むので、Amazon ECSなどとの相性はいいです。
WordPressをFrankenPHPで動かしてみる
WordPressを動かす場合、次のようなDockerfileとcompose.ymlになります。
Dockerfile
FROM dunglas/frankenphp:php8.3
RUN docker-php-ext-install pdo_mysql mysqli
COPY wordpress /app/public
compose.yml
services:
php:
build: .
ports:
- "80:80"
- "443:443"
- "443:443/udp"
mysql:
image: 'mysql/mysql-server:8.0'
environment:
MYSQL_ROOT_PASSWORD: 'password'
MYSQL_ROOT_HOST: '%'
MYSQL_DATABASE: 'mysql'
MYSQL_USER: 'mysql'
MYSQL_PASSWORD: 'password'
パフォーマンス比較
DockerでFrankenPHP、FPM + Nginx、Apacheの環境に対して、WordPressの表示測定しました。 測定条件はNginxのFastCGIまわりの設定を除き、サーバーもWordPressもデフォルトの設定で、siegeを用い記事の表示を60秒間、同接100と200ので測定しました。
実行環境
FrankenPHPの環境
FROM dunglas/frankenphp:php8.3
ADD https://ja.wordpress.org/latest-ja.zip .
RUN apt-get update && \
apt-get install -y unzip && \
rm -rf /var/lib/apt/lists/* && \
docker-php-ext-install pdo_mysql mysqli && \
unzip latest-ja.zip && \
mv wordpress/* /app/public/ && \
rm -r wordpress latest-ja.zip
services:
php:
build: .
ports:
- "80:80"
environment:
SERVER_NAME: ':80'
mysql:
image: 'mysql/mysql-server:8.0'
environment:
MYSQL_ROOT_PASSWORD: 'password'
MYSQL_ROOT_HOST: '%'
MYSQL_DATABASE: 'mysql'
MYSQL_USER: 'mysql'
MYSQL_PASSWORD: 'password'
MYSQL_ALLOW_EMPTY_PASSWORD: 1
volumes:
- 'mysql-volume:/var/lib/mysql'
volumes:
mysql-volume:
driver: local
php-fpm + Nginxの環境
FROM php:8.3-fpm
ADD https://ja.wordpress.org/latest-ja.zip .
RUN apt-get update && \
apt-get install -y unzip && \
rm -rf /var/lib/apt/lists/* && \
docker-php-ext-install pdo_mysql mysqli && \
unzip latest-ja.zip && \
mv wordpress/* /var/www/html/ && \
rm -r wordpress latest-ja.zip
FROM nginx:1.27
COPY nginx.conf /etc/nginx/conf.d/default.conf
ADD https://ja.wordpress.org/latest-ja.zip .
RUN apt-get update && \
apt-get install -y unzip && \
rm -rf /var/lib/apt/lists/* && \
unzip latest-ja.zip && \
mkdir -p /var/www/html/ && \
mv wordpress/* /var/www/html/ && \
rm -r wordpress latest-ja.zip
server {
listen 80;
server_name localhost;
root /var/www/html;
index index.php;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
services:
php:
build:
context: .
dockerfile: fpm.Dockerfile
web:
build:
context: .
dockerfile: nginx.Dockerfile
ports:
- "80:80"
mysql:
image: 'mysql/mysql-server:8.0'
environment:
MYSQL_ROOT_PASSWORD: 'password'
MYSQL_ROOT_HOST: '%'
MYSQL_DATABASE: 'mysql'
MYSQL_USER: 'mysql'
MYSQL_PASSWORD: 'password'
MYSQL_ALLOW_EMPTY_PASSWORD: 1
volumes:
- 'mysql-volume:/var/lib/mysql'
volumes:
mysql-volume:
driver: local
Apacheの環境
FROM php:8.3-apache
ADD https://ja.wordpress.org/latest-ja.zip .
RUN apt-get update && \
apt-get install -y unzip && \
rm -rf /var/lib/apt/lists/* && \
docker-php-ext-install pdo_mysql mysqli && \
unzip latest-ja.zip && \
mv wordpress/* /var/www/html/ && \
rm -r wordpress latest-ja.zip
services:
php:
build: .
ports:
- "80:80"
mysql:
image: 'mysql/mysql-server:8.0'
environment:
MYSQL_ROOT_PASSWORD: 'password'
MYSQL_ROOT_HOST: '%'
MYSQL_DATABASE: 'mysql'
MYSQL_USER: 'mysql'
MYSQL_PASSWORD: 'password'
MYSQL_ALLOW_EMPTY_PASSWORD: 1
restart: always
volumes:
- 'mysql-volume:/var/lib/mysql'
volumes:
mysql-volume:
driver: local
結果
| FrankenPHP | FPM | Apache | FrankenPHP | FPM | Apache | |
|---|---|---|---|---|---|---|
| 同接 | 100 | 100 | 100 | 200 | 200 | 200 |
| Transactions | 28000 hits | 23366 hits | 21972 hits | 27807 hits | 23344 hits | 6144 hits |
| Availability | 100.00% | 100.00% | 100.00% | 100.00% | 100.00% | 84.64% |
| Elapsed time | 60.21 secs | 60.98 secs | 60.19 secs | 60.94 secs | 60.98 secs | 33.76 secs |
| Data transferred | 275.79 MB | 306.99 MB | 84.93 MB | 273.92 MB | 306.72 MB | 26.34 MB |
| Response time | 0.21 secs | 0.26 secs | 0.27 secs | 0.42 secs | 0.50 secs | 1.01 secs |
| Transaction rate | 465.04 trans/sec | 383.17 trans/sec | 365.04 trans/sec | 456.30 trans/sec | 382.81 trans/sec | 181.99 trans/sec |
| Throughput | 4.58 MB/sec | 5.03 MB/sec | 1.41 MB/sec | 4.49 MB/sec | 5.03 MB/sec | 0.78 MB/sec |
| Concurrency | 99.03 | 98.45 | 98.57 | 193.72 | 192.61 | 183.57 |
| Successful transactions | 28014 | 23374 | 21982 | 27822 | 23356 | 6144 |
| Failed transactions | 0 | 0 | 0 | 0 | 0 | 1115 |
| Longest transaction | 2.27 | 2.89 | 19.78 | 4.34 | 5.90 | 23.74 |
| Shortest transaction | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 |
vs. FPM
パフォーマンス面ではやや劣るものの、ほぼ同じようなスコアになりました。
一方構築面ではFPMコンテナのほか、Nginxなどのコンテナが必要となり、常に2つのコンテナを動かすことになります。 同じコンテナ内に収める方法もありますが、1プロセス1コンテナのベストプラクティスに反します。 また、NginxにFastCGIの設定を書いたファイルが必要になるなど、動かすまでに少し手間がかかります。
vs. Apache
パフォーマンス面では、FrankenPHPや、FPMと比べると大きく劣る結果になりました。 とくに同接200では唯一、正常応答が100%になっていません。
一方構築面ではFrankenPHP同様、Apache自身がPHPを実行するためコンテナは1つになります。 またとくに設定しなくても、ドキュメントルートにPHPファイルを置くだけで実行できます。
FrankenPHPの問題
一見、FrankenPHPは便利そうに見えますが、 既知の問題もあります。
2024年10月時点では、PHP拡張機能のimapとnewrelicがサポートされていません。また、ext-opensslとparallelは不具合が報告されています。
PHP拡張機能以外では、get_browser()関数のパフォーマンス低下などがあります。
いずれの場合も回避策はありますが、ほかと比べると安定性や互換性の面がやや気になるところです。
また、FrankenPHPは共有メモリを利用して高速化しています。 状況によってはメモリーリークなどが発生します。
おわりに
FrankenPHPは従来のApache、FPMとは異なるアプローチでPHP用のWebサーバーです。 サクッとPHPサーバーを構築できパフォーマンスもよく、使いやすいと思います。 安定性や互換性によほどシビアな要求がなければ、十分に実戦投入できると思います。