PHP-FPM完全ガイド|設定最適化からセキュリティ対策まで徹底解説

この記事では、PHP-FPMの基本概念から実践的な運用テクニックまで網羅的に解説しています。メモリ使用量増加やプロセス管理の問題への対処法、NginxやApacheとの連携設定、7つの最適化テクニック、セキュリティ対策、モニタリング手法を具体的に説明。PHP-FPMの導入を検討中の方や、パフォーマンス改善・安定運用を目指す開発者・インフラ担当者に必要な実践的知識が得られます。

目次

PHP-FPMとは – 基本概念と仕組みの理解

php+fpm+server

Webアプリケーション開発において、PHPの実行環境をどのように構築するかは、パフォーマンスや安定性を左右する重要な選択です。PHP-FPM(FastCGI Process Manager)は、現代のPHP実行環境において最も広く採用されている実装の一つであり、高速なPHP処理を実現するための仕組みです。このセクションでは、PHP-FPMの基本的な概念と動作原理について、その歴史的背景や従来の方式との違いを交えながら詳しく解説していきます。

PHP-FPMの基本構造と動作原理

PHP-FPMは、FastCGIプロトコルを使用してPHPスクリプトを実行するためのプロセスマネージャーです。マスタープロセスと複数のワーカープロセスから構成される、プール型のアーキテクチャを採用しています。この構造により、効率的なリソース管理と高いパフォーマンスを実現しています。

PHP-FPMの動作プロセスは以下のような流れで進行します。まず、システム起動時にマスタープロセスが起動し、設定ファイルを読み込みます。マスタープロセスは、設定に基づいて必要な数のワーカープロセス(子プロセス)を生成し、これらのプロセスが実際のPHPスクリプトの実行を担当します。

リクエスト処理の流れを具体的に見ると、次のようになります。

  1. WebサーバーがPHPリクエストを受信: NginxやApacheなどのWebサーバーがクライアントからのHTTPリクエストを受け取ります
  2. FastCGIプロトコルでPHP-FPMへ転送: WebサーバーはPHPファイルの処理が必要と判断すると、UnixソケットまたはTCPソケットを通じてPHP-FPMにリクエストを転送します
  3. ワーカープロセスがリクエストを処理: 待機状態にあるワーカープロセスの一つがリクエストを受け取り、PHPスクリプトを実行します
  4. 結果をWebサーバーへ返却: 処理が完了すると、結果がFastCGIプロトコルを通じてWebサーバーに返され、最終的にクライアントへ送信されます

PHP-FPMの特徴的な機能として、プロセスプールの動的管理機能があります。アクセス負荷に応じて、ワーカープロセスの数を自動的に増減させることができます。これにより、低負荷時にはリソースを節約し、高負荷時には十分な処理能力を確保することが可能です。

また、PHP-FPMはプロセスの再利用を行います。一度リクエストを処理したワーカープロセスは終了せず、次のリクエストを待機します。ただし、メモリリークを防ぐため、設定された回数のリクエストを処理した後には自動的に再起動される仕組みが備わっています。

従来のモジュール版PHPとの主な違い

PHP-FPMが登場する以前は、ApacheのモジュールとしてPHPを実行するmod_phpが主流でした。この二つの実行方式には、アーキテクチャの根本的な違いがあり、それぞれに明確なメリットとデメリットが存在します。

mod_phpの場合、PHPインタープリタがApacheプロセスに直接組み込まれます。これは、静的ファイルを配信するだけのApacheプロセスにもPHPインタープリタがロードされるため、メモリ効率が悪くなるという課題がありました。例えば、画像やCSSファイルを配信するプロセスにも、使用されないPHPモジュールが常駐することになります。

比較項目PHP-FPMmod_php
プロセス分離WebサーバーとPHP実行環境が完全に分離Apacheプロセス内に統合
メモリ効率PHPが必要なリクエストのみでメモリを使用すべてのApacheプロセスでPHPモジュールを読み込み
設定の柔軟性プール単位で独立した設定が可能サーバー全体またはVirtualHost単位での設定
ユーザー権限分離プールごとに異なるユーザー権限で実行可能基本的にApacheユーザーで実行
プロセス管理独立したプロセスマネージャーによる詳細な制御Apacheのプロセス管理に依存
再起動の影響PHP設定変更時にWebサーバーを再起動不要PHP設定変更にはApache再起動が必要

プロセス分離によるメリットは多岐にわたります。PHP-FPMでは、PHPスクリプトの実行とWebサーバーの動作が完全に独立しているため、一方に問題が発生しても他方への影響を最小限に抑えられます。例えば、PHPスクリプトが無限ループに陥った場合でも、Webサーバー自体は静的コンテンツの配信を継続できます。

また、マルチサイト環境における権限分離の面でも、PHP-FPMは大きな利点があります。複数のWebサイトを一つのサーバーでホスティングする場合、各サイトを異なるユーザー権限で実行することで、セキュリティを大幅に向上させることができます。mod_phpでは実現が困難だったこの機能は、共有ホスティング環境では特に重要です。

現代のWebアプリケーションでPHP-FPMが採用される理由

現代のWeb開発環境において、PHP-FPMは事実上の標準となっています。この広範な採用には、技術的な優位性だけでなく、開発運用の効率性やインフラの進化といった複数の要因が関係しています。

パフォーマンスとスケーラビリティの向上は、PHP-FPMが選ばれる最大の理由の一つです。プロセスプールを活用した効率的なリソース管理により、同一のハードウェアでより多くの同時接続を処理できます。負荷に応じてワーカープロセス数を動的に調整する機能により、トラフィックの変動に柔軟に対応できる点も高く評価されています。

Nginxとの親和性の高さも重要な採用理由です。Nginxは高性能な静的ファイル配信とリバースプロキシ機能で知られており、PHP処理をPHP-FPMに委譲する構成が最適なパフォーマンスを発揮します。この組み合わせは、以下のような利点をもたらします。

  • 役割の明確な分離: Nginxが静的コンテンツとリクエストルーティングを担当し、PHP-FPMが動的コンテンツ生成に専念
  • 効率的なリソース使用: 軽量なNginxプロセスが多数のコネクションを処理し、重いPHP処理は必要な時のみ実行
  • 独立したスケーリング: WebサーバーとPHP処理を別々のサーバーに配置するなど、柔軟な構成が可能

コンテナ化とクラウドネイティブ環境への適合性も、PHP-FPMの現代的な価値を高めています。DockerやKubernetesといったコンテナオーケストレーションツールとの相性が良く、マイクロサービスアーキテクチャにおいても有効に機能します。プロセス管理が明確に定義されているため、コンテナ内での動作が予測可能で、オートスケーリングの実装も容易です。

セキュリティ面での強化も見逃せません。プールごとのユーザー権限分離、chroot環境での実行、open_basedirによるファイルアクセス制限など、多層的なセキュリティ対策が標準機能として提供されています。これらは、WordPressやLaravelなどの主要なPHPフレームワークを安全に運用する上で不可欠な要素となっています。

運用管理の効率性も重要な要素です。PHP-FPMは詳細なステータス情報を提供し、リアルタイムでプロセスの状態を監視できます。スローログ機能により、パフォーマンスボトルネックの特定も容易です。設定変更時にWebサーバーを再起動する必要がないため、サービスの可用性を維持しながら運用パラメータを調整できる点も、本番環境では大きな利点となります。

さらに、エコシステムの成熟も採用を後押ししています。豊富なドキュメント、コミュニティの知見、監視ツールとの統合、ホスティングプロバイダーの標準サポートなど、PHP-FPMを中心とした開発運用環境が確立されています。これにより、新規プロジェクトでPHP-FPMを選択することは、技術的なリスクを最小化しながら、実績のある安定した環境を構築することを意味しています。

“`html

PHP-FPMの導入とインストール手順

php+server+configuration

PHP-FPMを実際に運用環境で活用するためには、各オペレーティングシステムに応じた適切なインストールと設定が必要です。ここでは、主要なOS環境であるUbuntu、CentOS、Windowsそれぞれにおける具体的なセットアップ手順を詳しく解説します。各環境特有の注意点や設定のポイントを押さえることで、スムーズな導入が可能になります。

Ubuntuでのセットアップ方法

Ubuntuは開発環境からプロダクション環境まで幅広く利用されているLinuxディストリビューションであり、PHP-FPMのインストールも非常にシンプルです。aptパッケージマネージャーを使用することで、必要なパッケージを容易に導入できます。

パッケージのインストール手順

Ubuntu環境でPHP-FPMをインストールする際は、まずパッケージリストを最新の状態に更新してから作業を開始します。以下のコマンドを順番に実行することで、PHP-FPMとその依存関係を含めて一括でインストールできます。

sudo apt update
sudo apt install php-fpm

特定のPHPバージョンを指定してインストールしたい場合は、バージョン番号を明示的に指定します。例えば、PHP 8.2をインストールする場合は次のようになります。

sudo apt install php8.2-fpm

複数のPHPバージョンを共存させる必要がある場合は、それぞれのバージョンを個別にインストールすることも可能です。また、よく使われる拡張モジュールも同時にインストールしておくと効率的です。

sudo apt install php8.2-fpm php8.2-mysql php8.2-curl php8.2-gd php8.2-mbstring php8.2-xml

インストールが完了したら、以下のコマンドでPHP-FPMのバージョンを確認できます。

php-fpm8.2 -v

設定ファイルの構造と配置場所

Ubuntuにおけるphp-fpmの設定ファイルは、体系的な構造で整理されています。メインの設定ファイルとプール設定ファイルが分離されており、管理がしやすい構成となっています。

主要な設定ファイルの配置場所は以下の通りです。

  • /etc/php/8.2/fpm/php-fpm.conf – PHP-FPMのメイン設定ファイル
  • /etc/php/8.2/fpm/pool.d/www.conf – デフォルトプールの設定ファイル
  • /etc/php/8.2/fpm/php.ini – PHP自体の設定ファイル

プール設定ファイルの構造を理解することは重要です。/etc/php/8.2/fpm/pool.d/ディレクトリ内に複数の.confファイルを配置することで、異なるWebサイトやアプリケーションごとに独立したプール設定を持つことができます。

デフォルトのwww.confファイルには以下のような重要な設定項目が含まれています。

設定項目説明デフォルト値
userプロセスの実行ユーザーwww-data
groupプロセスの実行グループwww-data
listenソケットのリスンアドレス/run/php/php8.2-fpm.sock
pmプロセスマネージャーのタイプdynamic
pm.max_children最大子プロセス数5

設定ファイルを編集する際は、元のファイルのバックアップを取っておくことを推奨します。

sudo cp /etc/php/8.2/fpm/pool.d/www.conf /etc/php/8.2/fpm/pool.d/www.conf.bak

サービスの起動と管理コマンド

Ubuntuでは、systemdを使用してPHP-FPMサービスを管理します。サービスの起動、停止、再起動、ステータス確認など、日常的な運用に必要なコマンドを把握しておくことが重要です。

PHP-FPMサービスを起動するには、以下のコマンドを実行します。

sudo systemctl start php8.2-fpm

システム起動時に自動的にPHP-FPMを起動させるには、enableコマンドを使用します。

sudo systemctl enable php8.2-fpm

サービスの現在の状態を確認するには、statusコマンドが便利です。

sudo systemctl status php8.2-fpm

設定ファイルを変更した後は、サービスの再起動が必要です。ただし、実行中のリクエストを中断せずに設定を反映させるには、reloadコマンドを使用します。

sudo systemctl reload php8.2-fpm

完全に再起動する場合は、restartコマンドを使用します。

sudo systemctl restart php8.2-fpm

サービスを停止する場合は、以下のコマンドを実行します。

sudo systemctl stop php8.2-fpm

PHP-FPMのプロセスが正常に動作しているかを確認するには、psコマンドも活用できます。

ps aux | grep php-fpm

CentOSでのセットアップ方法

CentOSおよびRHEL系のLinuxディストリビューションでは、yumまたはdnfパッケージマネージャーを使用してPHP-FPMをインストールします。また、SELinuxやファイアウォールなど、セキュリティ機能が標準で有効になっているため、それらの設定も併せて行う必要があります。

パッケージのインストール手順

CentOS 7以前ではyum、CentOS 8以降ではdnfを使用しますが、コマンドの使い方はほぼ同じです。まず、EPELリポジトリとRemiリポジトリを有効にすることで、最新バージョンのPHPをインストールできるようになります。

CentOS 8/9系でのインストール手順は以下の通りです。

sudo dnf install epel-release
sudo dnf install https://rpms.remirepo.net/enterprise/remi-release-8.rpm
sudo dnf module reset php
sudo dnf module enable php:remi-8.2
sudo dnf install php-fpm

CentOS 7系では、yumを使用します。

sudo yum install epel-release
sudo yum install https://rpms.remirepo.net/enterprise/remi-release-7.rpm
sudo yum install yum-utils
sudo yum-config-manager --enable remi-php82
sudo yum install php-fpm

必要な拡張モジュールも同時にインストールします。

sudo dnf install php-mysqlnd php-curl php-gd php-mbstring php-xml php-opcache

インストール後、以下のコマンドでバージョンを確認できます。

php-fpm -v

SELinux環境での設定ポイント

CentOS系のディストリビューションでは、SELinux(Security-Enhanced Linux)が標準で有効になっています。SELinuxはセキュリティを強化する重要な機能ですが、PHP-FPMとWebサーバーの連携において適切な設定を行わないと動作しない場合があります。

まず、SELinuxの現在の状態を確認します。

getenforce

SELinuxを有効にしたままPHP-FPMを正常に動作させるためには、適切なSELinuxコンテキストとブール値の設定が必要です。セキュリティを維持しながら運用できるため、SELinuxを無効化するのではなく適切に設定することを推奨します。

NginxやApacheがPHP-FPMのソケットにアクセスできるようにするには、以下のコマンドでSELinuxブール値を設定します。

sudo setsebool -P httpd_can_network_connect on
sudo setsebool -P httpd_execmem on

Unixソケットを使用する場合は、ソケットファイルに適切なSELinuxコンテキストを設定する必要があります。

sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/run/php-fpm(/.*)?"
sudo restorecon -Rv /var/run/php-fpm

SELinuxのログを確認することで、アクセス拒否の原因を特定できます。

sudo ausearch -m avc -ts recent

特定の問題に対してSELinuxポリシーを生成するには、audit2allowツールが便利です。

sudo grep php-fpm /var/log/audit/audit.log | audit2allow -M php-fpm-custom
sudo semodule -i php-fpm-custom.pp

ファイアウォールの設定方法

CentOSでは、firewalldがデフォルトのファイアウォール管理ツールとして使用されています。PHP-FPMが外部のTCPポートでリスンする設定を使用している場合、ファイアウォールの設定が必要になります。

まず、firewalldの状態を確認します。

sudo firewall-cmd --state

PHP-FPMを通常の構成でNginxやApacheと連携させる場合、WebサーバーのHTTP/HTTPSポートのみを開放すれば十分です。

sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

もし、PHP-FPMがTCPソケット(例:9000番ポート)でリスンし、外部サーバーから接続を受け付ける必要がある場合は、以下のように特定のポートを開放します。

sudo firewall-cmd --permanent --add-port=9000/tcp
sudo firewall-cmd --reload

ただし、PHP-FPMのTCPポートを外部に公開することはセキュリティリスクが高いため、通常はUnixソケットを使用するか、localhostでのみリスンする設定を推奨します。

特定のIPアドレスからのアクセスのみを許可する場合は、リッチルールを使用します。

sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" port protocol="tcp" port="9000" accept'
sudo firewall-cmd --reload

現在のファイアウォール設定を確認するには、以下のコマンドを使用します。

sudo firewall-cmd --list-all

Windowsでのセットアップ方法

Windows環境でPHP-FPMを運用することは、Linux環境ほど一般的ではありませんが、IISと組み合わせることで高性能なPHP実行環境を構築できます。Windows特有の設定や管理方法を理解することで、安定したPHP環境を実現できます。

インストールの基本手順

Windows環境でPHP-FPMを導入するには、まずPHPのWindows版をダウンロードしてインストールする必要があります。公式サイトから、Non Thread Safe版のPHPをダウンロードすることを推奨します。

以下の手順でインストールを進めます。

  1. PHPの公式サイトから、Non Thread Safe版のPHPバイナリをダウンロードします
  2. ダウンロードしたZIPファイルを任意のディレクトリ(例:C:\php)に解凍します
  3. php.ini-productionファイルをphp.iniにコピーして、環境に合わせて編集します
  4. 必要な拡張モジュールを有効化します

php.iniファイルで最低限必要な設定は以下の通りです。

extension_dir = "C:\php\ext"
extension=mysqli
extension=mbstring
extension=openssl
extension=curl
cgi.force_redirect = 0
cgi.fix_pathinfo = 1
fastcgi.impersonate = 1

環境変数のPATHに、PHPのインストールディレクトリを追加します。これにより、コマンドプロンプトからphp-cgi.exeを実行できるようになります。

  1. Windowsの「システムのプロパティ」を開きます
  2. 「環境変数」をクリックします
  3. 「Path」変数を編集して、C:\phpを追加します

コマンドプロンプトを開いて、以下のコマンドでPHPが正常にインストールされているか確認します。

php -v

Windowsサービスとしての実行方法

Windows環境でPHP-FPMを安定して運用するには、Windowsサービスとして登録することが推奨されます。これにより、システム起動時の自動起動や、サービスとしての管理が可能になります。

Windows環境では、NSSMやWinSWといったサービスラッパーツールを使用してPHP-FPMをサービス化できます。ここではNSSMを使用した方法を解説します。

NSSMをダウンロードして、任意のディレクトリに配置します。その後、管理者権限でコマンドプロンプトを開き、以下のコマンドを実行します。

nssm install PHP-FPM "C:\php\php-cgi.exe" "-b 127.0.0.1:9000"

サービスの詳細設定を行う場合は、GUIを使用します。

nssm edit PHP-FPM

サービスを起動します。

nssm start PHP-FPM

または、Windowsのサービス管理コンソールから起動することもできます。

services.msc

サービスとして登録することで、サーバーの再起動後も自動的にPHP-FPMが起動し、安定した運用が可能になります。

サービスの状態を確認するには、以下のコマンドを使用します。

sc query PHP-FPM

サービスを停止する場合は、以下のコマンドを実行します。

nssm stop PHP-FPM

IISとの連携設定

Windows環境でPHPを運用する場合、IIS(Internet Information Services)との連携が一般的です。IISとPHP-FPMを連携させるには、FastCGIモジュールを使用します。

まず、IISでFastCGIモジュールが有効になっていることを確認します。Windowsの機能から「インターネット インフォメーション サービス」→「World Wide Web サービス」→「アプリケーション開発機能」→「CGI」を有効化します。

IISマネージャーを開き、サーバーレベルで「ハンドラー マッピング」を設定します。

  1. IISマネージャーを開きます
  2. サーバー名をクリックして、「ハンドラー マッピング」をダブルクリックします
  3. 右側の「モジュール マップの追加」をクリックします
  4. 以下のように設定します:
    • 要求パス: *.php
    • モジュール: FastCgiModule
    • 実行可能ファイル: C:\php\php-cgi.exe
    • 名前: PHP_FastCGI

FastCGIの詳細設定を行うには、「FastCGI設定」を開きます。

設定項目推奨値説明
最大インスタンス4-8同時に実行するPHP-CGIプロセスの最大数
インスタンス最大要求数10000プロセスがリサイクルされるまでの処理リクエスト数
アクティビティ タイムアウト300リクエストのタイムアウト時間(秒)
要求タイムアウト90リクエストの待機タイムアウト(秒)

環境変数も適切に設定します。FastCGI設定で「環境変数」セクションを開き、以下を追加します。

PHP_FCGI_MAX_REQUESTS=10000
PHPRC=C:\php

web.configファイルを使用して、サイトごとに詳細な設定を行うこともできます。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <handlers>
            <add name="PHP_FastCGI" path="*.php" verb="*" modules="FastCgiModule" scriptProcessor="C:\php\php-cgi.exe" resourceType="Either" />
        </handlers>
        <defaultDocument>
            <files>
                <add value="index.php" />
            </files>
        </defaultDocument>
    </system.webServer>
</configuration>

設定完了後、IISを再起動して変更を反映させます。

iisreset

動作確認として、簡単なphpinfo.phpファイルをWebルートに配置して、ブラウザからアクセスします。

<?php
phpinfo();
?>

正常に設定されていれば、PHPの情報ページが表示され、Server APIに「CGI/FastCGI」と表示されます。

“`

“`html

WebサーバーとPHP-FPMの連携設定

web+server+configuration

PHP-FPMを実際の本番環境で利用するには、WebサーバーとPHP-FPMを適切に連携させる設定が不可欠です。WebサーバーはクライアントからのHTTPリクエストを受け付け、静的ファイルは直接配信し、PHPファイルの実行が必要な場合にのみPHP-FPMへリクエストを転送します。この連携設定の方法はWebサーバーの種類によって異なり、また通信方式にもいくつかの選択肢があります。本セクションでは、代表的なWebサーバーであるNginxとApacheそれぞれにおけるPHP-FPMとの連携方法、そしてUnixソケットとTCPソケットという2つの通信方式の選択基準について詳しく解説します。

NginxとPHP-FPMの統合方法

NginxはPHP-FPMとの組み合わせで最も広く利用されているWebサーバーです。Nginxは軽量で高速な動作が特徴であり、PHP-FPMとの連携も標準的にサポートされています。ここでは、NginxとPHP-FPMを連携させるための基本的な設定方法から、セキュリティやパフォーマンスを向上させる応用設定まで詳しく説明します。

リクエスト処理の流れ

NginxとPHP-FPMが連携してWebリクエストを処理する流れを理解することは、適切な設定を行う上で重要です。まず、クライアントからHTTPリクエストがNginxに到達します。Nginxはリクエストされたファイルの拡張子や設定されたルールに基づいて、そのリクエストをどう処理するか判断します。

静的ファイル(HTML、CSS、JavaScript、画像など)の場合、Nginxは自身でファイルを読み込み、直接クライアントに返信します。一方、PHPファイルへのリクエストの場合、Nginxはfastcgi_passディレクティブで指定されたPHP-FPMプロセスへFastCGIプロトコルを使ってリクエストを転送します。

PHP-FPMはリクエストを受け取ると、利用可能なワーカープロセスにそれを割り当てます。ワーカープロセスはPHPスクリプトを実行し、その結果をNginxに返します。最後にNginxがクライアントに最終的なHTTPレスポンスを返すという流れになります。

基本的な設定ファイルの構造

NginxでPHP-FPMを利用するための基本的な設定ファイルの構造を理解しましょう。通常、Nginxの設定ファイルは/etc/nginx/nginx.confがメインファイルとなり、サイトごとの設定は/etc/nginx/sites-available/ディレクトリに配置します。

基本的なPHP処理の設定例は以下の通りです:

server {
    listen 80;
    server_name example.com;
    root /var/www/html;
    index index.php index.html;

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php/php8.1-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

この設定の各要素を解説します:

  • location ~ \.php$ – .phpで終わるファイルへのリクエストにマッチする正規表現パターン
  • try_files $uri =404 – ファイルが存在しない場合に404エラーを返す(セキュリティ対策)
  • fastcgi_pass – PHP-FPMへの接続先(UnixソケットまたはTCPポート)
  • fastcgi_param SCRIPT_FILENAME – 実行するPHPファイルの絶対パスを指定
  • include fastcgi_params – FastCGI標準パラメータを読み込み

セキュリティを強化する設定項目

NginxとPHP-FPMの連携においてセキュリティを強化するための重要な設定項目がいくつかあります。適切なセキュリティ対策を施すことで、脆弱性を悪用した攻撃からWebアプリケーションを守ることができます。

最も重要なのが、存在しないPHPファイルへのリクエストを適切に処理することです。try_files $uri =404;の設定を必ず含めることで、ファイルが存在しない場合にPHP-FPMへリクエストを転送しないようにします。この設定がないと、一部の設定では任意のファイルをPHPとして実行される脆弱性が発生する可能性があります。

追加のセキュリティ設定として、以下の項目を推奨します:

location ~ \.php$ {
    try_files $uri =404;
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass unix:/run/php/php8.1-fpm.sock;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    
    # セキュリティ強化設定
    fastcgi_param PHP_VALUE "open_basedir=/var/www/html:/tmp";
    fastcgi_hide_header X-Powered-By;
    fastcgi_intercept_errors on;
    fastcgi_buffer_size 16k;
    fastcgi_buffers 4 16k;
    
    # アクセス制限
    location ~ /\.ht {
        deny all;
    }
}
  • fastcgi_param PHP_VALUE "open_basedir" – PHPがアクセスできるディレクトリを制限
  • fastcgi_hide_header X-Powered-By – PHPバージョン情報の露出を防止
  • fastcgi_intercept_errors on – PHP-FPMのエラーページをNginxでカスタマイズ可能に
  • deny allで.htaccessなどの隠しファイルへのアクセスを拒否

タイムアウト設定の最適化

NginxとPHP-FPM間の通信におけるタイムアウト設定は、パフォーマンスとユーザー体験に直接影響します。適切なタイムアウト値を設定することで、長時間実行されるスクリプトによるリソース枯渇を防ぎつつ、正当な処理が途中で切断されることを回避できます。

主要なタイムアウト関連の設定項目は以下の通りです:

location ~ \.php$ {
    # 前述の基本設定に加えて
    fastcgi_connect_timeout 60s;
    fastcgi_send_timeout 180s;
    fastcgi_read_timeout 180s;
    fastcgi_buffering on;
    fastcgi_buffer_size 16k;
    fastcgi_buffers 16 16k;
    fastcgi_busy_buffers_size 256k;
}
  • fastcgi_connect_timeout – PHP-FPMへの接続確立までの待機時間(デフォルト60秒)
  • fastcgi_send_timeout – PHP-FPMへのデータ送信タイムアウト(デフォルト60秒)
  • fastcgi_read_timeout – PHP-FPMからのレスポンス待機時間(デフォルト60秒)
  • fastcgi_buffering – レスポンスのバッファリングを有効化してパフォーマンス向上

一般的なWebアプリケーションでは、fastcgi_read_timeoutを60〜180秒に設定するのが適切です。ただし、画像処理やデータエクスポートなど長時間実行される処理がある場合は、特定のlocationブロックでのみタイムアウトを延長する設定を検討してください。

location ~ ^/admin/export\.php$ {
    fastcgi_read_timeout 600s;
    # その他の設定
}

ApacheとPHP-FPMの統合方法

ApacheはNginxと並んで広く利用されているWebサーバーです。従来はmod_phpモジュールでPHPを実行するのが一般的でしたが、PHP-FPMを利用することで、より柔軟なプロセス管理やリソース分離が可能になります。Apache 2.4以降では、PHP-FPMとの連携が公式にサポートされており、mod_proxyやmod_fcgiモジュールを使用して統合できます。

mod_proxyとmod_fcgiを使った連携

Apache 2.4以降で推奨されるPHP-FPMとの連携方法は、mod_proxy_fcgiモジュールを使用する方法です。この方法では、Apacheがリバースプロキシとして機能し、FastCGIプロトコルでPHP-FPMと通信します。

まず、必要なモジュールを有効化します:

# Ubuntu/Debianの場合
sudo a2enmod proxy
sudo a2enmod proxy_fcgi
sudo a2enmod setenvif
sudo systemctl restart apache2

# CentOS/RHELの場合(httpd.confで有効化確認)
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so

基本的なVirtualHost設定は以下のようになります:

<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/html
    
    <Directory /var/www/html>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
    
    # PHP-FPMへのプロキシ設定
    <FilesMatch \.php$>
        SetHandler "proxy:unix:/run/php/php8.1-fpm.sock|fcgi://localhost"
    </FilesMatch>
    
    # ディレクトリインデックスでPHPを優先
    DirectoryIndex index.php index.html
</VirtualHost>

TCPソケットを使用する場合の設定は以下です:

<FilesMatch \.php$>
    SetHandler "proxy:fcgi://127.0.0.1:9000"
</FilesMatch>

mod_proxy_fcgiを使用することで、Apacheの既存の機能(.htaccessサポート、mod_rewriteなど)を維持しながらPHP-FPMの利点を享受できます。

モジュール版からの移行時の注意点

従来のmod_phpからPHP-FPM(mod_proxy_fcgi)への移行には、いくつかの重要な注意点があります。動作の仕組みが根本的に異なるため、単純に切り替えるだけでは期待通りに動作しないことがあります。

主な注意点は以下の通りです:

  • Apache環境変数の扱い: mod_phpでは直接利用できたApacheの環境変数が、PHP-FPMでは明示的に渡す必要があります
  • php.ini設定の場所: mod_phpでは.htaccessでPHP設定を上書きできましたが、PHP-FPMではプール設定で管理します
  • ファイルパーミッション: 実行ユーザーがApacheからPHP-FPMのプールユーザーに変わるため、ファイルの所有者やパーミッションの見直しが必要です
  • パフォーマンス特性: プロセス管理の方法が変わるため、リソース設定の再調整が必要になります

環境変数を適切に渡す設定例:

<FilesMatch \.php$>
    SetHandler "proxy:unix:/run/php/php8.1-fpm.sock|fcgi://localhost"
    SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
</FilesMatch>

# ProxyPassMatchを使う方法
ProxyPassMatch ^/(.*\.php(/.*)?)$ unix:/run/php/php8.1-fpm.sock|fcgi://localhost/var/www/html/

.htaccessでphp_valueやphp_flagを使用していた場合は、それらをPHP-FPMのプール設定ファイルに移行する必要があります。

; /etc/php/8.1/fpm/pool.d/www.conf
php_admin_value[upload_max_filesize] = 50M
php_admin_value[post_max_size] = 50M
php_admin_value[memory_limit] = 256M

複数のPHPバージョンを共存させる設定

ApacheとPHP-FPMの組み合わせでは、複数のPHPバージョンを同時に運用し、サイトやディレクトリごとに異なるバージョンを使い分けることが容易に実現できます。これは、レガシーアプリケーションと新規アプリケーションを同一サーバーで運用する際や、段階的なPHPバージョンアップグレードを行う際に非常に有用です。

まず、複数のPHPバージョンをインストールし、それぞれ独立したPHP-FPMプールを設定します:

# Ubuntu/Debianの例
sudo apt install php7.4-fpm php8.1-fpm

# それぞれのサービスを起動
sudo systemctl start php7.4-fpm
sudo systemctl start php8.1-fpm

VirtualHostごとに異なるPHPバージョンを指定する設定例:

# PHP 8.1を使用するサイト
<VirtualHost *:80>
    ServerName newsite.example.com
    DocumentRoot /var/www/newsite
    
    <FilesMatch \.php$>
        SetHandler "proxy:unix:/run/php/php8.1-fpm.sock|fcgi://localhost"
    </FilesMatch>
</VirtualHost>

# PHP 7.4を使用するレガシーサイト
<VirtualHost *:80>
    ServerName legacy.example.com
    DocumentRoot /var/www/legacy
    
    <FilesMatch \.php$>
        SetHandler "proxy:unix:/run/php/php7.4-fpm.sock|fcgi://localhost"
    </FilesMatch>
</VirtualHost>

同一VirtualHost内でディレクトリごとに異なるPHPバージョンを使用する設定:

<VirtualHost *:80>
    ServerName mixed.example.com
    DocumentRoot /var/www/mixed
    
    # デフォルトはPHP 8.1
    <FilesMatch \.php$>
        SetHandler "proxy:unix:/run/php/php8.1-fpm.sock|fcgi://localhost"
    </FilesMatch>
    
    # 特定ディレクトリのみPHP 7.4
    <Directory /var/www/mixed/legacy>
        <FilesMatch \.php$>
            SetHandler "proxy:unix:/run/php/php7.4-fpm.sock|fcgi://localhost"
        </FilesMatch>
    </Directory>
</VirtualHost>

この柔軟性により、アプリケーションごとに最適なPHPバージョンを選択でき、段階的な移行やテストが可能になります。

UnixソケットとTCPソケットの選択基準

WebサーバーとPHP-FPM間の通信方式には、Unixソケット(Unixドメインソケット)とTCPソケット(ネットワークソケット)の2つの選択肢があります。それぞれに特徴と利点があり、環境や要件に応じて適切な方式を選択することが重要です。この選択は、システムのパフォーマンス、セキュリティ、拡張性に影響を与えます。

Unixソケットの特徴と利用シーン

Unixソケットは、同一サーバー上のプロセス間通信(IPC)に使用されるファイルシステムベースの通信方式です。ファイルパスを通じて接続するため、ネットワークスタックを経由しない効率的な通信が実現できます。

Unixソケットの主な特徴:

  • 高速な通信: TCP/IPスタックをバイパスするため、オーバーヘッドが少なく高速
  • セキュリティ: ファイルシステムパーミッションでアクセス制御が可能
  • ネットワーク露出なし: 外部ネットワークから直接アクセスできない
  • ポート番号不要: ポート競合の心配がない

Unixソケットの設定例:

; PHP-FPM側の設定 (/etc/php/8.1/fpm/pool.d/www.conf)
listen = /run/php/php8.1-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

; Nginx側の設定
fastcgi_pass unix:/run/php/php8.1-fpm.sock;

; Apache側の設定
SetHandler "proxy:unix:/run/php/php8.1-fpm.sock|fcgi://localhost"

Unixソケットは、WebサーバーとPHP-FPMが同一サーバー上で動作する一般的な構成において最も推奨される選択肢です。パフォーマンスとセキュリティの両面で優れており、特別な理由がない限りUnixソケットを選択すべきです。

適している利用シーン:

  • WebサーバーとPHP-FPMが同じサーバー上にある標準的な構成
  • 高トラフィックサイトでパフォーマンスを最大化したい場合
  • セキュリティ要件が厳しい環境
  • シンプルな単一サーバー構成

TCPソケットの特徴と利用シーン

TCPソケットは、IPアドレスとポート番号を使用したネットワーク通信方式です。同一サーバー内でも使用できますが、主にネットワークを介した通信に適しています。

TCPソケットの主な特徴:

  • リモート通信: ネットワークを介して別サーバーのPHP-FPMに接続可能
  • 負荷分散: 複数のPHP-FPMサーバーへの振り分けが容易
  • 柔軟性: スケールアウト構成が実現しやすい
  • 監視の容易さ: ネットワークツールでの監視や診断が可能

TCPソケットの設定例:

; PHP-FPM側の設定 (/etc/php/8.1/fpm/pool.d/www.conf)
listen = 127.0.0.1:9000
; または特定IPでリスン
listen = 192.168.1.10:9000
; セキュリティ設定
listen.allowed_clients = 127.0.0.1,192.168.1.5

; Nginx側の設定
fastcgi_pass 127.0.0.1:9000;
; または複数サーバーへの負荷分散
upstream php_backend {
    server 192.168.1.10:9000;
    server 192.168.1.11:9000;
    server 192.168.1.12:9000;
}
fastcgi_pass php_backend;

; Apache側の設定
SetHandler "proxy:fcgi://127.0.0.1:9000"

適している利用シーン:

  • WebサーバーとPHP-FPMを物理的に分離したい場合
  • 複数のWebサーバーから1つのPHP-FPMサーバー群にアクセスする構成
  • PHP処理を専用サーバーにオフロードしてスケールアウトする場合
  • コンテナ環境やKubernetesなどのオーケストレーション環境
  • 開発環境で異なるマシン間での接続が必要な場合

TCPソケットを使用する場合、listen.allowed_clientsでアクセス元IPアドレスを制限し、ファイアウォールでポートを適切に保護することが重要です。設定を誤ると外部から直接PHP-FPMにアクセスされる危険性があります。

パフォーマンスとセキュリティの観点での比較

UnixソケットとTCPソケットを、パフォーマンスとセキュリティの観点から詳しく比較し、適切な選択を行うための判断材料を提供します。

パフォーマンス比較:

項目UnixソケットTCPソケット
通信速度高速(TCP/IPスタックを経由しない)やや遅い(ネットワークスタック経由)
レイテンシ低い(数マイクロ秒)やや高い(数十マイクロ秒)
CPU使用率低いやや高い(プロトコル処理)
スループット高い中程度
同時接続数への対応優れている優れている

ベンチマークの傾向として、Unixソケットは同一サーバー内の通信においてTCPソケット(127.0.0.1)に比べて5〜15%程度のパフォーマンス向上が見られることが多いです。高トラフィックサイトではこの差が積み重なって顕著な影響となります。

セキュリティ比較:

項目UnixソケットTCPソケット
アクセス制御ファイルパーミッションIPアドレス制限
外部露出リスクなし(ファイルシステムのみ)あり(設定ミスで露出の可能性)
通信の傍受リスク極めて低いローカルネットワーク内では可能性あり
設定の複雑さシンプルやや複雑(ファイアウォール等も考慮)
認証メカニズム不要(ファイルアクセスで制御)必要に応じて実装可能

Unixソケットはファイルシステムのパーミッション(listen.owner、listen.group、listen.mode)で厳密にアクセス制御できるため、設定ミスによる外部露出のリスクがありません。一方、TCPソケットは0.0.0.0でリッスンしてしまうと外部からアクセス可能になるリスクがあります。

選択のための判断基準:

以下のフローチャートに従って選択することを推奨します:

  1. WebサーバーとPHP-FPMは同じサーバー上にありますか?
    • はい → Unixソケットを選択(パフォーマンスとセキュリティで優位)
    • いいえ → TCPソケットを選択(物理的に分離されている場合は必須)
  2. 複数のWebサーバーから1つのPHP-FPMプールにアクセスしますか?
    • はい → TCPソケットを選択
    • いいえ → Unixソケットを継続検討
  3. コンテナ環境やクラウドネイティブな構成ですか?
    • はい → TCPソケットが柔軟性の面で有利な場合が多い
    • いいえ → Unixソケットを選択

結論として、一般的なWebホスティング環境では、WebサーバーとPHP-FPMを同一サーバー上で運用し、Unixソケットで接続する構成が最も推奨されます。パフォーマンス、セキュリティ、設定の簡潔さの全ての面で優れています。TCPソケットは、分散構成やスケールアウトが必要な大規模環境で選択すべきです。

“`

“`html

PHP-FPMの設定最適化テクニック

php+fpm+server

PHP-FPMのパフォーマンスを最大限に引き出すには、適切な設定最適化が不可欠です。デフォルト設定のままでは、トラフィックの増加やアプリケーションの複雑化に対応できず、レスポンスの遅延やサーバーリソースの無駄遣いが発生します。このセクションでは、プロセス管理からメモリ最適化、リクエスト処理の高速化まで、実践的な最適化テクニックを体系的に解説します。

プロセス管理の最適化手法

PHP-FPMのプロセス管理は、システム全体のパフォーマンスとリソース効率に直接影響する重要な要素です。適切なプロセス管理設定により、リクエストの待ち時間を最小化し、サーバーリソースを効率的に活用できます。ここでは、プロセスマネージャーの選択から子プロセス数の算出、再起動制御まで、最適なプロセス管理の実現方法を説明します。

プロセスマネージャータイプの選択方法

PHP-FPMには3つのプロセスマネージャータイプ(pm)があり、それぞれ異なる特性を持っています。staticは固定数のプロセスを常に維持し、予測可能なメモリ使用量とレスポンス時間を実現します。高トラフィックで安定した負荷が予想される環境に最適です。

dynamicは負荷に応じてプロセス数を動的に調整するタイプで、最も柔軟性が高く一般的に使用されます。トラフィックの変動が大きい環境では、リソースを無駄にせず必要に応じてスケールできるため推奨されます。設定ファイルでは以下のように記述します。

pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15

ondemandは必要なときだけプロセスを起動するタイプで、メモリリソースが限られた環境や低トラフィックのサイトに適しています。ただし、初回リクエストに遅延が発生するというデメリットがあるため、レスポンス時間が重要な本番環境では注意が必要です。

最適な子プロセス数の算出方法

適切な子プロセス数の算出は、サーバーのメモリ容量とPHPアプリケーションのメモリ使用量に基づいて行います。まず、単一のPHP-FPMプロセスが使用する平均メモリ量を測定します。

ps -ylC php-fpm --sort:rss
# または
ps aux | grep php-fpm | awk '{sum+=$6} END {print sum/NR/1024 " MB"}'

例えば、1プロセスあたり50MBのメモリを使用し、サーバーの利用可能メモリが4GBの場合、単純計算では80プロセスまで起動可能です。ただし、OSやその他のサービスにもメモリを確保する必要があるため、実際には60〜70%程度に抑えることが推奨されます。

dynamicモードでの推奨設定式は以下の通りです。

  • pm.max_children:(利用可能メモリ × 0.7)÷ プロセスあたりのメモリ使用量
  • pm.start_servers:pm.max_childrenの20〜30%
  • pm.min_spare_servers:pm.start_serversと同等かやや少なめ
  • pm.max_spare_servers:pm.start_serversの1.5〜2倍

具体的な計算例として、4GBメモリのサーバーで1プロセス50MBの場合:

pm.max_children = (4096 * 0.7) / 50 = 57
pm.start_servers = 57 * 0.25 = 14
pm.min_spare_servers = 10
pm.max_spare_servers = 20

プロセスの再起動制御設定

長時間稼働するPHP-FPMプロセスは、メモリリークや内部状態の蓄積により徐々にパフォーマンスが低下する可能性があります。これを防ぐため、プロセスの再起動制御設定が重要です。

pm.max_requestsパラメータは、1つの子プロセスが処理できるリクエスト数の上限を設定します。この値に達したプロセスは自動的に再起動され、メモリリークの影響を最小化できます。

pm.max_requests = 500

適切な値は、アプリケーションの特性により異なりますが、一般的には500〜1000が推奨されます。値が小さすぎるとプロセスの頻繁な再起動によりオーバーヘッドが増加し、大きすぎるとメモリリークの影響が蓄積します。

また、pm.process_idle_timeout(ondemandおよびdynamicモード)は、アイドル状態のプロセスが終了するまでの時間を秒単位で指定します。

pm.process_idle_timeout = 10s

メモリ使用量の最適化

PHP-FPMのメモリ使用量を最適化することは、サーバーリソースの効率的な活用とコスト削減に直結します。適切なメモリ制限とキャッシング機構の活用により、同じハードウェアでより多くのリクエストを処理できるようになります。

メモリ制限値の適切な設定

PHPのmemory_limit設定は、スクリプトが使用できる最大メモリ量を制限します。PHP-FPMでは、プール単位でこの値をカスタマイズできます。

php_admin_value[memory_limit] = 128M

php_admin_valueを使用すると、実行時にini_set()で変更できない強制的な制限となります。一方、php_valueを使用すると、アプリケーション側での変更が可能です。

適切なメモリ制限値の決定には、以下のアプローチが有効です。

  1. 開発環境で典型的なリクエストのメモリ使用量を測定する
  2. ピーク時のメモリ使用量に20〜30%のバッファを追加する
  3. 本番環境でモニタリングしながら調整する

メモリ制限が低すぎると Fatal error が発生し、高すぎるとメモリリークが発生した際に1プロセスで大量のメモリを消費してしまいます。一般的なWebアプリケーションでは128MB〜256MBが適切ですが、画像処理や大量データ処理を行う場合は512MB以上が必要になることもあります。

OpCodeキャッシュの活用方法

OPcache(OpCode Cache)は、PHPスクリプトのコンパイル結果をメモリにキャッシュし、パフォーマンスを大幅に向上させる機能です。PHP 5.5以降は標準で組み込まれており、PHP-FPMとの組み合わせで強力なパフォーマンス改善が可能です。

PHP-FPMのプール設定でOPcacheを最適化するには、以下の設定を追加します。

php_admin_value[opcache.enable] = 1
php_admin_value[opcache.memory_consumption] = 256
php_admin_value[opcache.interned_strings_buffer] = 16
php_admin_value[opcache.max_accelerated_files] = 10000
php_admin_value[opcache.validate_timestamps] = 0
php_admin_value[opcache.revalidate_freq] = 0

opcache.memory_consumptionはOPcacheが使用するメモリ量をMB単位で指定します。大規模なアプリケーションでは256MB以上が推奨されます。opcache.max_accelerated_filesは、キャッシュできるファイル数の上限で、プロジェクトのPHPファイル数より多めに設定します。

本番環境では、opcache.validate_timestampsを0に設定することで、タイムスタンプチェックを無効化し、パフォーマンスを最大化できます。ただし、コードの変更がすぐに反映されなくなるため、デプロイ時にOPcacheのクリアが必要です。

# OPcacheのクリア方法
service php-fpm reload
# または専用のスクリプトを使用

ガベージコレクションの調整

PHPのガベージコレクション(GC)は、循環参照を持つ変数を自動的にメモリから解放する機能です。PHP-FPMでは、GCの動作を調整することでメモリ効率を改善できます。

php_admin_value[zend.enable_gc] = 1
php_admin_value[gc_probability] = 1
php_admin_value[gc_divisor] = 1000

zend.enable_gcでガベージコレクションを有効化します。通常の循環参照を含まないシンプルなアプリケーションでは、GCを無効にすることでわずかにパフォーマンスが向上しますが、現代的なフレームワークを使用している場合は有効化が推奨されます。

gc_probability と gc_divisor の組み合わせで、GCが実行される確率を制御します。上記の例では、1/1000(0.1%)の確率でリクエスト終了時にGCが実行されます。GCの実行頻度が高すぎるとパフォーマンスに影響し、低すぎるとメモリ使用量が増加します。

リクエスト処理の高速化設定

リクエスト処理の高速化は、ユーザー体験の向上とサーバー負荷の軽減に直結します。接続設定、バッファ設定、ファイル制限値などの細かな調整により、PHP-FPMのスループットを大幅に向上させることができます。

接続設定のチューニング

PHP-FPMとWebサーバー間の接続設定は、リクエスト処理速度に大きく影響します。特に、listen.backlogパラメータは、待機中の接続キューの長さを制御します。

listen = /var/run/php-fpm.sock
listen.backlog = 511
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

listen.backlogのデフォルト値は511ですが、高トラフィック環境では1024以上に増やすことで、リクエストの取りこぼしを防げます。ただし、OSのカーネルパラメータ(net.core.somaxconn)も同時に調整する必要があります

# /etc/sysctl.conf
net.core.somaxconn = 1024
net.core.netdev_max_backlog = 5000

また、listen.allowed_clients(TCPソケット使用時)で、接続を許可するIPアドレスを制限することでセキュリティを強化できます。

listen = 127.0.0.1:9000
listen.allowed_clients = 127.0.0.1

バッファ設定の最適化

PHP-FPMのバッファ設定は、Webサーバーとの通信効率を左右します。特にFastCGIプロトコルのバッファサイズは、レスポンスサイズが大きい場合に重要です。

Nginxとの連携では、以下のバッファ設定が推奨されます。

# Nginx設定ファイル
fastcgi_buffer_size 32k;
fastcgi_buffers 8 16k;
fastcgi_busy_buffers_size 32k;
fastcgi_temp_file_write_size 256k;

fastcgi_buffer_sizeは、レスポンスヘッダーを格納するバッファのサイズです。デフォルトの4kでは不足することが多く、32k程度に設定すると安定します。fastcgi_buffersは、レスポンスボディ用のバッファで、数と個別サイズを指定します。

バッファサイズが小さすぎると一時ファイルへの書き込みが発生し、I/Oオーバーヘッドでパフォーマンスが低下します。逆に大きすぎるとメモリを浪費するため、アプリケーションの典型的なレスポンスサイズに基づいて調整します。

ファイル制限値の調整

PHP-FPMプロセスが開けるファイル数の制限は、同時接続数やデータベース接続数に影響します。rlimit_filesパラメータで、プロセスごとのファイルディスクリプタ上限を設定できます。

rlimit_files = 65536
rlimit_core = unlimited

rlimit_filesのデフォルト値はシステムのデフォルト(通常1024)ですが、多数のファイルを同時に開くアプリケーションや高同時接続環境では、65536程度に増やすことが推奨されます。

システム全体のファイル数制限も同時に確認・調整する必要があります。

# /etc/security/limits.conf
www-data soft nofile 65536
www-data hard nofile 65536

# /etc/sysctl.conf
fs.file-max = 2097152

rlimit_coreを unlimited に設定すると、クラッシュ時にコアダンプが生成され、デバッグが容易になります。ただし、本番環境ではディスク容量を考慮する必要があります

php.iniと連携した総合的な最適化

PHP-FPMの設定ファイルとphp.iniを連携させることで、プール単位で細かなPHP動作をカスタマイズできます。この柔軟性により、複数のアプリケーションを異なる要件で最適化しながら同一サーバー上で稼働させることが可能になります。

プール単位でのPHP設定カスタマイズ

PHP-FPMのプール設定ファイルでは、php_value、php_admin_value、php_flag、php_admin_flagディレクティブを使用してPHP設定を上書きできます。

[www]
user = www-data
group = www-data
listen = /var/run/php-fpm-www.sock

php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 256M
php_value[max_execution_time] = 30
php_value[max_input_time] = 60
php_value[post_max_size] = 100M
php_value[upload_max_filesize] = 100M

php_admin_valuephp_admin_flagは、管理者が設定する値で、スクリプト内からini_set()で変更できません。一方、php_valuephp_flagは、デフォルト値を設定しますが、実行時の変更が可能です。

セキュリティに関わる設定(disable_functions、open_basedirなど)は、必ずphp_admin_valueで設定することが推奨されます。

php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,popen
php_admin_value[open_basedir] = /var/www/html:/tmp:/var/tmp

セッション管理の最適化

PHPのセッション管理は、Webアプリケーションのパフォーマンスに大きく影響します。PHP-FPMでは、プールごとに異なるセッション設定を適用することで、最適化が可能です。

php_value[session.save_handler] = files
php_value[session.save_path] = /var/lib/php/sessions/www
php_value[session.gc_probability] = 1
php_value[session.gc_divisor] = 1000
php_value[session.gc_maxlifetime] = 1440

session.save_pathをプール専用のディレクトリに設定することで、セッションファイルの分離とパフォーマンス向上が実現できます。大量のセッションファイルが1つのディレクトリに集中すると、ファイルシステムのI/O性能が低下します。

高トラフィック環境では、ファイルベースではなくRedisやMemcachedを使用したセッション管理が推奨されます。

php_value[session.save_handler] = redis
php_value[session.save_path] = "tcp://127.0.0.1:6379?weight=1&timeout=2.5"

また、session.gc_probabilitysession.gc_divisorの比率を調整することで、セッションガベージコレクションの実行頻度を制御できます。上記の設定では1/1000(0.1%)の確率で実行され、パフォーマンスへの影響を最小化しています。

データベース接続の永続化設定

PHP-FPMでは、プロセスが再利用されるため、データベースの永続的接続(persistent connection)を活用することでパフォーマンスを向上させることができます。

MySQLの場合、PDOやmysqli拡張で永続接続を使用できますが、設定の最適化が重要です。

php_value[mysqli.allow_persistent] = On
php_value[mysqli.max_persistent] = 50
php_value[mysqli.max_links] = 100

mysqli.max_persistentは、プロセスごとの永続接続数の上限を制限します。制限を設けないとデータベース側の接続数上限に達する可能性があるため、データベースサーバーの max_connections 設定と調整が必要です。

永続接続の利点と注意点を理解して使用することが重要です。

項目利点注意点
接続オーバーヘッド接続確立のコストを削減適切に管理しないと接続数が増大
レスポンス時間初回クエリの遅延を短縮接続状態が引き継がれるため初期化が必要
リソース効率接続の再利用でリソース節約プロセス数×接続数のリソースが必要

マルチサイト環境での個別プール管理

複数のWebサイトやアプリケーションを単一サーバーで運用する場合、PHP-FPMの個別プール管理が強力なソリューションとなります。プールを分離することで、各サイトのリソース管理、セキュリティ、パフォーマンスを独立して最適化できます。

サイトごとのプール分離設定

プール分離の基本は、各サイト専用のプール設定ファイルを作成することです。/etc/php-fpm.d/ディレクトリに個別のconfファイルを配置します。

# /etc/php-fpm.d/site1.conf
[site1]
user = site1
group = site1
listen = /var/run/php-fpm-site1.sock

pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 10

php_admin_value[memory_limit] = 128M
php_admin_value[upload_max_filesize] = 50M
# /etc/php-fpm.d/site2.conf
[site2]
user = site2
group = site2
listen = /var/run/php-fpm-site2.sock

pm = dynamic
pm.max_children = 30
pm.start_servers = 8
pm.min_spare_servers = 5
pm.max_spare_servers = 15

php_admin_value[memory_limit] = 256M
php_admin_value[upload_max_filesize] = 100M

プール名([]内)は一意である必要があります。各プールで異なるUnixソケットを使用することで、Webサーバー側で明確にルーティングできます。

Nginx設定でプールごとにリクエストを振り分ける例:

# site1用
server {
    server_name site1.example.com;
    root /var/www/site1;
    
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm-site1.sock;
        include fastcgi_params;
    }
}

# site2用
server {
    server_name site2.example.com;
    root /var/www/site2;
    
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm-site2.sock;
        include fastcgi_params;
    }
}

ユーザー権限の分離によるセキュリティ強化

プールごとに異なるシステムユーザーを割り当てることで、セキュリティを大幅に強化できます。あるサイトが侵害されても、他のサイトへの影響を最小限に抑えられます。

# ユーザーとグループの作成
useradd -r -s /bin/false site1
useradd -r -s /bin/false site2

# ディレクトリ所有権の設定
chown -R site1:site1 /var/www/site1
chown -R site2:site2 /var/www/site2

# 適切なパーミッション設定
chmod 750 /var/www/site1
chmod 750 /var/www/site2

open_basedirを各プールで設定することで、ファイルシステムへのアクセスを制限できます。

[site1]
user = site1
group = site1
php_admin_value[open_basedir] = /var/www/site1:/tmp:/var/tmp

[site2]
user = site2
group = site2
php_admin_value[open_basedir] = /var/www/site2:/tmp:/var/tmp

この設定により、site1のプロセスはsite2のディレクトリにアクセスできなくなります。セッションディレクトリやアップロードディレクトリもプールごとに分離することが推奨されます。

php_admin_value[session.save_path] = /var/lib/php/sessions/site1
php_admin_value[upload_tmp_dir] = /var/lib/php/tmp/site1

リソース配分の調整方法

マルチサイト環境では、各サイトの重要度やトラフィック量に応じて、リソース配分を調整することが重要です。優先度の高いサイトにはより多くのプロセスとメモリを割り当てます。

サーバー全体で利用可能なメモリが8GBで、3つのサイトを運用する場合の配分例:

# メインサイト(高トラフィック)
[mainsite]
pm.max_children = 50
php_admin_value[memory_limit] = 256M
# 最大メモリ使用量: 50 * 256MB = 12.8GB(理論値)

# サブサイト1(中トラフィック)
[subsite1]
pm.max_children = 20
php_admin_value[memory_limit] = 128M
# 最大メモリ使用量: 20 * 128MB = 2.6GB

# サブサイト2(低トラフィック)
[subsite2]
pm.max_children = 10
php_admin_value[memory_limit] = 128M
# 最大メモリ使用量: 10 * 128MB = 1.3GB

理論上の最大メモリ使用量の合計がサーバーの物理メモリを超えないように注意が必要です。実際には全プールが同時に最大プロセス数に達することは稀ですが、余裕を持った設定が推奨されます。

CPU時間の制限も設定できます。

rlimit_cpu = 600  # 秒単位でCPU時間を制限

スローログとタイムアウトの設定

パフォーマンス問題の早期発見と対処には、スローログとタイムアウトの適切な設定が不可欠です。これらの機能により、実行時間の長いスクリプトを特定し、システム全体への影響を防ぐことができます。

スローログの有効化と分析

PHP-FPMのスローログは、指定した時間を超えるリクエストの詳細情報を記録する機能です。パフォーマンスボトルネックの特定に極めて有効です。

[www]
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/www-slow.log

request_slowlog_timeoutで指定した秒数を超えたリクエストが発生すると、スローログにスタックトレースが記録されます。上記の例では、5秒以上かかるリクエストがログに記録されます。

スローログの出力例:

[25-Dec-2024 10:15:32]  [pool www] pid 12345
script_filename = /var/www/html/slow-script.php
[0x00007f8b4c0a8d20] sleep() /var/www/html/slow-script.php:10
[0x00007f8b4c0a8c80] processData() /var/www/html/slow-script.php:25
[0x00007f8b4c0a8be0] main() /var/www/html/slow-script.php:50

このログから、どのファイルのどの関数で時間がかかっているかを特定できます。定期的にスローログを分析することで、パフォーマンス改善の優先順位を決定できます。

スローログの分析スクリプト例:

# 頻繁にスローログに記録されるスクリプトを特定
grep "script_filename" /var/log/php-fpm/www-slow.log | \
  sort | uniq -c | sort -rn | head -20

適切なタイムアウト値の設定

タイムアウト設定は、システムリソースの保護とレスポンシブネスの維持に重要です。PHP-FPMでは、複数のタイムアウトパラメータが存在し、それぞれ異なる役割を持ちます。

[www]
request_terminate_timeout = 30s
pm.process_idle_timeout = 10s

request_terminate_timeoutは、単一のリクエスト処理の最大時間を制限します。この時間を超えると、PHP-FPMプロセスが強制終了されます。一般的なWebページでは30秒程度が適切ですが、APIやバッチ処理では長めに設定することがあります。

この値を0に設定すると無制限になりますが、本番環境では推奨されません。無限ループやデッドロックが発生した場合、プロセスが永久に占有されてしまいます。

Webサーバー側のタイムアウト設定との整合性も重要です。Nginxの場合:

# Nginx設定
fastcgi_read_timeout 60s;
fastcgi_send_timeout 60s;

PHP-FPMのrequest_terminate_timeoutは、Nginxのfastcgi_read_timeoutより短く設定することが推奨されます。これにより、PHP側で適切にエラーハンドリングできます。

タイムアウトの種類推奨設定用途
request_terminate_timeout30〜60秒通常のWebページ処理
request_slowlog_timeout5〜10秒パフォーマンス監視
pm.process_idle_timeout10秒アイドルプロセスの解放

長時間処理への対応策

レポート生成、画像処理、大量データのエクスポートなど、長時間を要する処理への対応には、複数のアプローチがあります。

1. 専用プールの作成

長時間処理専用のPHP-FPMプールを作成し、通常のリクエスト処理から分離します。

[longrunning]
user = www-data
group = www-data
listen = /var/run/php-fpm-longrunning.sock

pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

request_terminate_timeout = 600s
php_admin_value[memory_limit] = 512M
php_admin_value[max_execution_time] = 600

Nginx側で特定のパスを専用プールにルーティングします。

location ~ ^/reports/.*\.php$ {
    fastcgi_pass unix:/var/run/php-fpm-longrunning.sock;
    fastcgi_read_timeout 600s;
    include fastcgi_params;
}

2. 非同期処理への移行

より本質的な解決策として、長時間処理を非同期ジョブキューに移行することが推奨されます。ユーザーリクエストは即座にレスポンスを返し、バックグラウンドで処理を実行します。

PHP-FPMのモニタリングとデバッグ
ocean+view

PHP-FPMを安定的に運用するためには、適切なモニタリングとデバッグ体制の構築が不可欠です。システムの状態をリアルタイムで把握し、問題が発生した際には迅速に原因を特定できる仕組みを整えることで、Webアプリケーションのパフォーマンスと可用性を維持できます。このセクションでは、PHP-FPMが提供するモニタリング機能とデバッグ手法について詳しく解説します。

ステータスページの設定と活用方法

PHP-FPMには、プロセスの稼働状況をリアルタイムで確認できるステータスページ機能が組み込まれています。この機能を活用することで、プール内のプロセス数、キューに溜まっているリクエスト数、メモリ使用量などの重要な指標を簡単に取得できます。ステータスページは運用監視の基盤となる機能であり、正しく設定することでシステムの健全性を常に把握できるようになります。

ステータスページの有効化手順

ステータスページを有効化するには、PHP-FPMのプール設定ファイルを編集します。通常、/etc/php-fpm.d/www.confまたは/etc/php/7.4/fpm/pool.d/www.confなどに配置されている設定ファイルを開き、以下のパラメータを追加または編集してください。

pm.status_path = /status

この設定により、/statusというパスでステータス情報にアクセスできるようになります。次に、Webサーバー側でこのパスへのアクセスを許可する設定が必要です。Nginxの場合は、以下のような設定をサーバーブロックに追加します。

location ~ ^/(status|ping)$ {
    access_log off;
    allow 127.0.0.1;
    deny all;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
}

セキュリティの観点から、ステータスページへのアクセスは特定のIPアドレスに制限することを強く推奨します。上記の例では、ローカルホストからのアクセスのみを許可しています。本番環境では、監視サーバーのIPアドレスを許可リストに追加するなど、適切なアクセス制御を実装してください。

設定後はPHP-FPMとWebサーバーを再起動し、curl http://localhost/statusなどのコマンドでアクセスして動作を確認します。

表示される情報の読み解き方

ステータスページにアクセスすると、以下のような情報がテキスト形式で表示されます。これらの指標を正しく理解することで、システムの状態を的確に把握できます。

項目名説明重要度
poolプール名基本
process managerプロセスマネージャーのタイプ(dynamic/static/ondemand)基本
start timeプールの起動時刻基本
start since起動してからの経過秒数
accepted conn受け付けた総接続数
listen queue現在キューで待機中のリクエスト数
max listen queue起動以降の最大キュー長
listen queue lenキューのサイズ上限
idle processesアイドル状態のプロセス数
active processesリクエスト処理中のプロセス数
total processes総プロセス数
max active processes起動以降の最大アクティブプロセス数
max children reachedプロセス数上限に達した回数
slow requestsスローログに記録されたリクエスト数

特に注意すべきは「listen queue」と「max children reached」の値です。listen queueの値が常に0以外を示している場合、プロセス数が不足しており、リクエストが待機状態になっていることを意味します。max children reachedの値が増加し続ける場合も、プロセス数の上限設定を見直す必要があります。

idle processesとactive processesのバランスも重要な指標です。idle processesが常に0に近い場合は、リソースが逼迫している可能性があります。逆にidle processesが極端に多い場合は、プロセス数を減らすことでメモリを節約できる可能性があります。

JSON形式での情報取得方法

ステータス情報は、URLに?jsonまたは?full&jsonパラメータを付加することで、JSON形式で取得できます。この形式は、監視ツールやスクリプトからの自動解析に適しています。

curl http://localhost/status?json

基本的なステータス情報がJSON形式で返されます。より詳細な情報が必要な場合は、fullパラメータを追加します。

curl http://localhost/status?full&json

このコマンドを実行すると、各プロセスの詳細情報も含まれた完全なステータスが取得できます。個々のプロセスがどのスクリプトを処理しているか、どのくらいの時間実行されているかなどの情報が確認できるため、ハングアップしているプロセスの特定やパフォーマンスボトルネックの発見に有効です。

監視システムに統合する場合の例として、ZabbixやPrometheusなどのツールから定期的にこのエンドポイントをポーリングし、取得したJSON データをパースして各種メトリクスを記録することができます。以下は、シェルスクリプトでの活用例です。

#!/bin/bash
STATUS=$(curl -s http://localhost/status?json)
ACTIVE=$(echo $STATUS | jq -r '.["active processes"]')
IDLE=$(echo $STATUS | jq -r '.["idle processes"]')
QUEUE=$(echo $STATUS | jq -r '.["listen queue"]')

if [ $QUEUE -gt 10 ]; then
    echo "WARNING: Listen queue is high: $QUEUE"
fi

ログ設定とトラブルシューティング

ログはPHP-FPM運用における最も重要なデバッグツールの一つです。適切なログ設定により、エラーの原因究明、パフォーマンス問題の特定、セキュリティインシデントの検出が可能になります。PHP-FPMは複数種類のログを出力でき、それぞれ異なる目的と用途があります。

エラーログの設定と解析

PHP-FPMのエラーログは、プロセスマネージャー自体のエラーやワーカープロセスで発生した問題を記録します。メインの設定ファイル(/etc/php-fpm.confなど)で、以下のようにエラーログの出力先とログレベルを設定します。

error_log = /var/log/php-fpm/error.log
log_level = notice

log_levelには以下の値を指定できます。

  • alert – 緊急の問題のみ
  • error – エラーレベル以上
  • warning – 警告レベル以上
  • notice – 通知レベル以上(デフォルト、推奨)
  • debug – デバッグ情報を含むすべて

本番環境では通常「notice」レベルが推奨されますが、問題調査時には一時的に「debug」に変更することで詳細な情報を取得できます。ただし、debugレベルでは大量のログが生成されるため、ディスク容量に注意が必要です。

エラーログに記録される代表的な問題には以下のようなものがあります。

  • プロセスの異常終了(segmentation fault等)
  • プロセス起動の失敗
  • 設定ファイルの構文エラー
  • リソース不足によるプロセス生成の失敗
  • タイムアウトによるプロセスの強制終了

ログ解析時には、タイムスタンプに注目して問題発生のパターンを把握することが重要です。例えば、特定の時間帯に集中してエラーが発生している場合、その時間帯のアクセス増加やバッチ処理が原因である可能性があります。

アクセスログの設定と分析手法

PHP-FPMのアクセスログは、各リクエストの処理情報を記録します。この機能はデフォルトでは無効化されているため、プール設定ファイルで明示的に有効化する必要があります。

access.log = /var/log/php-fpm/$pool.access.log
access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%"

access.formatでは、以下のような変数を使用してログのフォーマットをカスタマイズできます。

変数説明
%RリモートIPアドレス
%uリモートユーザー
%tタイムスタンプ
%mリクエストメソッド
%rリクエストURI
%Qクエリ文字列
%sステータスコード
%fスクリプトファイル名
%{mili}d処理時間(ミリ秒)
%{kilo}Mピークメモリ使用量(KB)
%C%%CPU使用率

アクセスログの分析により、以下のような情報を取得できます。

  • 各スクリプトの平均処理時間とその分布
  • メモリ消費量の多いリクエストの特定
  • リクエスト頻度の高いエンドポイントの把握
  • レスポンスタイムの経時的変化

例えば、awkやPythonスクリプトを使用して処理時間が長いリクエストを抽出する場合は、以下のようなコマンドが有効です。

awk '{if ($8 > 1000) print $0}' /var/log/php-fpm/www.access.log | sort -k8 -nr

このコマンドは、処理時間が1000ミリ秒(1秒)を超えるリクエストを抽出し、処理時間の降順でソートします。

スローログによるパフォーマンス問題の特定

スローログは、設定した閾値を超える処理時間がかかったリクエストの詳細情報を記録する機能です。この機能は、パフォーマンスボトルネックの特定において極めて有効です。プール設定ファイルで以下のように設定します。

slowlog = /var/log/php-fpm/$pool.slow.log
request_slowlog_timeout = 5s

この設定により、5秒以上かかるリクエストがスローログに記録されます。閾値の設定は、アプリケーションの特性に応じて調整してください。一般的なWebアプリケーションでは3~10秒程度が目安ですが、バッチ処理を含む場合はより長い時間を設定する場合もあります。

スローログには、単に処理時間が長かったという事実だけでなく、その時点でのPHPスクリプトのバックトレース(関数呼び出しの履歴)が記録されます。これにより、どの関数で時間を消費しているかを正確に特定できます。

スローログの出力例は以下のようになります。

[10-Jan-2024 15:23:45] [pool www] pid 12345
script_filename = /var/www/html/process.php
[0x00007f8b2c0d8a10] curl_exec() /var/www/html/includes/api.php:78
[0x00007f8b2c0d8970] callExternalAPI() /var/www/html/includes/api.php:45
[0x00007f8b2c0d88d0] processRequest() /var/www/html/process.php:23

この例では、外部APIを呼び出すcurl_exec()関数で時間がかかっていることが分かります。このような情報により、以下のような対策を検討できます。

  • 外部API呼び出しのタイムアウト設定の見直し
  • 非同期処理への変更
  • キャッシュの導入
  • 外部APIの問題の可能性を調査

スローログを有効化すると、該当するリクエストでオーバーヘッドが発生するため、閾値を極端に短く設定することは避けるべきです。本番環境では、実際に問題となるレベルの処理時間を設定することを推奨します。

ログローテーションの実装

PHP-FPMのログファイルは放置すると肥大化し、ディスク容量を圧迫したり、ログ解析のパフォーマンスを低下させたりします。適切なログローテーションの実装により、これらの問題を回避できます。

Linuxシステムでは、logrotateを使用してログローテーションを設定するのが一般的です。/etc/logrotate.d/php-fpmファイルを作成し、以下のような設定を記述します。

/var/log/php-fpm/*.log {
    daily
    rotate 14
    missingok
    notifempty
    compress
    delaycompress
    sharedscripts
    postrotate
        /usr/bin/systemctl reload php-fpm.service > /dev/null 2>/dev/null || true
    endscript
}

各ディレクティブの意味は以下の通りです。

  • daily – 毎日ローテーション
  • rotate 14 – 過去14世代分を保持
  • missingok – ログファイルが存在しなくてもエラーにしない
  • notifempty – ログファイルが空の場合はローテーションしない
  • compress – 古いログをgzip圧縮
  • delaycompress – 最新のローテーション済みログは圧縮しない
  • sharedscripts – postrotateスクリプトを一度だけ実行
  • postrotate – ローテーション後に実行するコマンド

postrotateセクションでPHP-FPMをリロードすることで、新しいログファイルへの書き込みが正しく行われるようになります。reloadコマンドを使用することで、プロセスを再起動せずにログファイルハンドルを再オープンできます。

ローテーション設定は、ログの出力量やディスク容量、調査に必要な保存期間に応じてカスタマイズしてください。高トラフィックなサイトでは、dailyではなくhourlyでのローテーションが必要になる場合もあります。

パフォーマンスボトルネックの発見と解消

PHP-FPMのパフォーマンス問題は、適切な調査とチューニングにより大幅に改善できる場合があります。ボトルネックの発見から解消までのプロセスを体系的に理解し、適切なツールを活用することで、効率的な最適化が可能になります。

ボトルネック特定のアプローチ

パフォーマンスボトルネックを特定するには、段階的なアプローチが効果的です。まず、問題の症状を正確に把握することから始めます。

第1段階:症状の確認

  • レスポンスタイムの遅延
  • タイムアウトエラーの発生
  • CPU使用率の高騰
  • メモリ使用量の異常な増加
  • 接続拒否やキューの蓄積

第2段階:メトリクスの収集

ステータスページから取得できる指標を定期的に記録し、正常時との比較を行います。特に以下の値に注目します。

  • active processes / total processesの比率
  • listen queueの値
  • max children reachedのカウント
  • slow requestsの増加率

第3段階:ログの分析

アクセスログやスローログから、以下の情報を抽出します。

  • 処理時間が長いエンドポイントの特定
  • メモリ消費量の多いスクリプトの洗い出し
  • エラーログでの異常なパターンの検出

第4段階:システムレベルの調査

top、htop、iostatなどのシステムツールを使用して、リソース使用状況を確認します。

# プロセス単位のリソース使用状況
top -p $(pgrep -d',' php-fpm)

# I/O待機の確認
iostat -x 1

# ネットワーク接続の状態
ss -tn | grep :9000

これらの段階的な調査により、ボトルネックがPHP-FPM自体にあるのか、アプリケーションコードにあるのか、あるいはデータベースやストレージなどの外部要因にあるのかを切り分けることができます。

よくあるパフォーマンス問題と解決策

PHP-FPM環境で頻繁に遭遇するパフォーマンス問題とその解決策を以下に示します。

問題1:プロセス数不足による待機

症状として、listen queueに値が蓄積し、max children reachedがカウントアップします。アクティブプロセスが常に上限に張り付いている状態です。

解決策:

  • pm.max_childrenの値を増やす(ただしメモリ容量を考慮)
  • dynamicモードの場合、pm.start_serversとpm.min_spare_serversを増やして初期プロセス数を確保
  • サーバーのメモリ増設を検討
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 15

問題2:メモリリークによるパフォーマンス劣化

症状として、時間経過とともにメモリ使用量が増加し続け、最終的にOOM Killerによってプロセスが強制終了されます。

解決策:

  • pm.max_requestsを設定し、定期的にプロセスを再生成
  • アプリケーションコードのメモリリークを修正
  • メモリ制限値(memory_limit)を適切に設定
pm.max_requests = 500
php_admin_value[memory_limit] = 256M

問題3:データベース接続の枯渇

症状として、データベース接続エラーが頻発し、アプリケーションレベルでタイムアウトが発生します。

解決策:

  • データベース接続プールの設定を見直す
  • 持続的接続(persistent connection)の活用を検討
  • 不要な接続は速やかにクローズするようコードを修正
  • データベースサーバーのmax_connectionsを増やす

問題4:ファイルI/Oのボトルネック

症状として、I/O waitが高く、ディスクアクセスが頻繁に発生しています。

解決策:

  • OPcacheを有効化してスクリプトのコンパイルキャッシュを活用
  • セッションストレージをファイルからRedisやMemcachedに変更
  • 頻繁にアクセスされるファイルをRAMディスクに配置
  • SSDの導入を検討
; OPcacheの有効化
opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=10000

これらの解決策を適用する際は、必ず変更前の状態を記録し、一つずつ段階的に実施することが重要です。複数の変更を同時に行うと、どの変更が効果的だったのか判別できなくなります。

パフォーマンス測定ツールの活用

正確なパフォーマンス測定には、専用のツールを活用することが効果的です。以下に代表的なツールとその使用方法を紹介します。

Apache Bench(ab)

シンプルな負荷テストツールで、基本的なスループットとレスポンスタイムを測定できます。

ab -n 1000 -c 10 http://localhost/test.php

この例では、10並列で合計1000リクエストを実行し、結果を表示します。Requests per secondやTime per requestなどの基本的な指標が取得できます。

wrk

より高度な負荷テストが可能なツールで、Luaスクリプトによるカスタマイズができます。

wrk -t4 -c100 -d30s http://localhost/api/endpoint

4スレッド、100接続で30秒間の負荷テストを実施します。Apache Benchよりも高い負荷をかけることができるため、本番環境に近い条件でのテストに適しています。

Blackfire

PHPアプリケーション専用のプロファイラーで、関数レベルでの詳細な実行時間とメモリ使用量を測定できます。本番環境でも低オーバーヘッドで使用できる点が特徴です。

Blackfireを使用すると、以下のような情報が視覚的に確認できます。

  • 各関数の実行回数と累計時間
  • コールグラフによる関数呼び出しの流れ
  • メモリアロケーションのホットスポット
  • データベースクエリの実行時間

New Relic / Datadog

商用のAPM(Application Performance Monitoring)ツールで、継続的なパフォーマンス監視が可能です。PHP-FPMの各種メトリクスと、アプリケーションレベルのトレース情報を統合して分析できるため、包括的なパフォーマンス管理が実現できます。

Xdebug Profiler

開発環境でのプロファイリングに適したツールです。出力されたcachegrindファイルをKCacheGrindやWebGrindで可視化することで、詳細な実行フローを分析できます。

; php.iniでの設定
xdebug.mode=profile
xdebug.output_dir=/tmp/xdebug
xdebug.profiler_output_name=cachegrind.out.%p

Xdebugは大きなオーバーヘッドがあるため、本番環境では使用しないでください。開発環境やステージング環境で問題を再現させてプロファイリングを行います。

これらのツールを組み合わせて使用することで、マクロな負荷特性からミクロな関数レベルのボトルネックまで、多角的にパフォーマンスを分析できます。測定結果に基づいて設定を調整し、再度測定するというサイクルを繰り返すことで、最適なパフォーマンスを実現できます。

“`

“`html

PHP-FPMのセキュリティ対策

php+security+server

PHP-FPMを本番環境で運用する際には、適切なセキュリティ対策を施すことが不可欠です。Webアプリケーションの実行環境であるPHP-FPMは、攻撃者にとって重要なターゲットとなり得るため、多層的なセキュリティ対策を実装する必要があります。ここでは、権限設定からchroot機能の活用まで、PHP-FPMのセキュリティを強化するための具体的な手法を解説します。

権限設定とユーザー分離の実装

PHP-FPMのセキュリティ対策において最も基本となるのが、適切な権限設定とユーザー分離です。デフォルト設定のまま運用すると、すべてのアプリケーションが同じユーザー権限で実行されるため、一つのアプリケーションが侵害されると他のアプリケーションにも影響が及ぶリスクがあります。ユーザー分離を実装することで、セキュリティインシデントの影響範囲を最小限に抑えることができます。

専用ユーザーとグループの作成

PHP-FPMを安全に運用するためには、各プールごとに専用のユーザーとグループを作成することが推奨されます。以下のコマンドで、アプリケーション専用のユーザーを作成できます。

# アプリケーション専用ユーザーの作成
sudo useradd -r -s /bin/false -d /var/www/app1 app1user
sudo groupadd app1group
sudo usermod -a -G app1group app1user

# 複数のアプリケーションを運用する場合
sudo useradd -r -s /bin/false -d /var/www/app2 app2user
sudo groupadd app2group
sudo usermod -a -G app2group app2user

専用ユーザーを作成する際は、ログインシェルを無効化(/bin/false)することで、万が一そのユーザーアカウントが侵害された場合でも、直接のシェルアクセスを防ぐことができます。また、システムアカウント(-rオプション)として作成することで、通常のユーザーアカウントとは明確に区別されます。

プール設定での権限管理

作成した専用ユーザーをPHP-FPMのプール設定ファイル(通常は/etc/php-fpm.d/または/etc/php/7.x/fpm/pool.d/配下)で指定します。以下は、セキュアなプール設定の例です。

[app1]
user = app1user
group = app1group

listen = /var/run/php-fpm/app1.sock
listen.owner = nginx
listen.group = nginx
listen.mode = 0660

pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35

; プロセスの実行ユーザー設定
catch_workers_output = yes
security.limit_extensions = .php

ユーザー分離の設定では、usergroupパラメータで実行ユーザーを指定するだけでなく、Unixソケットを使用する場合はlisten.ownerlisten.grouplisten.modeも適切に設定する必要があります。listen.modeを0666などの緩い設定にすると、他のユーザーからもソケットにアクセスできてしまうため、必ず最小限の権限(0660)に設定してください。

ファイルシステム権限の適切な設定

PHP-FPMプロセスが実行するアプリケーションファイルには、適切なファイルシステム権限を設定することが重要です。基本的な権限設定の方針は以下の通りです。

  • PHPファイル(実行ファイル): 読み取り専用(644)に設定し、所有者はアプリケーション用ユーザー
  • アップロードディレクトリ: 書き込み権限が必要(755または775)だが、実行権限は付与しない
  • 設定ファイル: 厳格な権限(600または640)で、機密情報を含むファイルは特に注意
  • ログディレクトリ: アプリケーションユーザーが書き込み可能(755または775)

以下は、実際のコマンド例です。

# アプリケーションディレクトリの基本権限設定
sudo chown -R app1user:app1group /var/www/app1
sudo find /var/www/app1 -type d -exec chmod 755 {} \;
sudo find /var/www/app1 -type f -exec chmod 644 {} \;

# アップロード用ディレクトリの設定
sudo chmod 775 /var/www/app1/uploads
sudo chown app1user:nginx /var/www/app1/uploads

# 設定ファイルの保護
sudo chmod 600 /var/www/app1/config/database.php

アプリケーションファイルとデータファイルを別のディレクトリに分離し、それぞれに適切な権限を設定することで、セキュリティリスクを大幅に軽減できます。

環境変数による機密情報の管理

データベース認証情報やAPIキーなどの機密情報をPHPコード内にハードコーディングすることは、セキュリティ上の重大なリスクとなります。PHP-FPMでは、プール設定ファイルを通じて環境変数を安全に管理し、アプリケーションに渡すことができます。この手法を活用することで、バージョン管理システムに機密情報を含めることなく、環境ごとに異なる設定を柔軟に管理できます。

プール設定での環境変数設定方法

PHP-FPMのプール設定ファイルでは、envディレクティブを使用して環境変数を定義できます。以下は、データベース接続情報を環境変数として設定する例です。

[app1]
user = app1user
group = app1group

; 環境変数の設定
env[DB_HOST] = localhost
env[DB_NAME] = app1_production
env[DB_USER] = app1_dbuser
env[DB_PASSWORD] = "SecurePassword123!@#"
env[API_KEY] = "your_api_key_here"
env[APP_ENV] = production
env[APP_DEBUG] = false

; システム環境変数を継承する場合
env[PATH] = /usr/local/bin:/usr/bin:/bin
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp

PHP側では、getenv()関数または$_ENVスーパーグローバルを使用してこれらの値にアクセスできます。

<?php
$dbHost = getenv('DB_HOST');
$dbName = getenv('DB_NAME');
$dbUser = getenv('DB_USER');
$dbPassword = getenv('DB_PASSWORD');

$pdo = new PDO(
    "mysql:host=$dbHost;dbname=$dbName",
    $dbUser,
    $dbPassword
);
?>

環境変数を使用することで、開発環境、ステージング環境、本番環境で異なる設定を簡単に管理でき、誤って本番の認証情報をリポジトリにコミットするリスクを排除できます。プール設定ファイル自体は、rootユーザーのみが読み取れるよう権限を設定(640)しておくことが重要です。

open_basedirによるアクセス制御

open_basedirディレクティブは、PHPスクリプトがアクセスできるディレクトリを制限する強力なセキュリティ機能です。これにより、仮にアプリケーションに脆弱性があった場合でも、攻撃者が任意のファイルにアクセスすることを防ぐことができます。

[app1]
user = app1user
group = app1group

; open_basedirの設定
php_admin_value[open_basedir] = /var/www/app1:/tmp:/usr/share/php:/var/log/app1

; その他のセキュリティ設定
php_admin_flag[allow_url_fopen] = off
php_admin_flag[allow_url_include] = off

上記の設定では、PHPスクリプトは/var/www/app1/tmp/usr/share/php/var/log/app1のディレクトリ内のファイルのみにアクセスできます。open_basedirを設定する際は、アプリケーションが必要とするすべてのディレクトリを含める必要があります。不足していると、アプリケーションが正常に動作しなくなる可能性があります。

特に以下のディレクトリを含めることを検討してください。

  • アプリケーションディレクトリ: Webアプリケーションのファイルが格納されているディレクトリ
  • 一時ディレクトリ: セッションファイルやアップロード一時ファイル用(/tmpなど)
  • ライブラリディレクトリ: 共通PHPライブラリが格納されているディレクトリ
  • ログディレクトリ: アプリケーションログを書き込むディレクトリ

chroot機能の活用方法

chroot(change root)は、プロセスが認識できるルートディレクトリを変更することで、アプリケーションを隔離された環境で実行するセキュリティ機能です。PHP-FPMはchroot機能をサポートしており、これを活用することで、仮にアプリケーションが侵害された場合でも、システム全体への影響を最小限に抑えることができます。

chrootの基本概念と仕組み

chrootは、プロセスのルートディレクトリ(/)を指定したディレクトリに変更する仕組みです。chroot環境内で実行されるプロセスは、指定されたディレクトリより上位のファイルシステムにアクセスできなくなります。

例えば、/var/www/app1をchrootディレクトリとして設定した場合、PHP-FPMプロセスからは/var/www/app1/として認識されます。これにより、以下のようなセキュリティ上の利点があります。

  • ファイルシステムの隔離: システムの重要なファイル(/etc/passwd、/etc/shadowなど)にアクセスできなくなる
  • 攻撃範囲の限定: ディレクトリトラバーサル攻撃などの影響範囲が限定される
  • マルチテナント環境での分離: 複数のアプリケーションを安全に共存させることができる

chrootは「完全な仮想化」ではなく「軽量な隔離」であるため、コンテナ技術(Docker等)と比較してリソースオーバーヘッドが小さく、パフォーマンスへの影響が最小限です。

chroot環境の構築手順

PHP-FPMでchroot環境を構築するには、まずchroot用のディレクトリ構造を準備する必要があります。以下は、基本的な構築手順です。

# 1. chrootディレクトリの作成
sudo mkdir -p /chroot/app1/{etc,tmp,usr,var,dev}
sudo mkdir -p /chroot/app1/var/www/html

# 2. 必要なデバイスファイルの作成
sudo mknod -m 666 /chroot/app1/dev/null c 1 3
sudo mknod -m 666 /chroot/app1/dev/random c 1 8
sudo mknod -m 666 /chroot/app1/dev/urandom c 1 9

# 3. 必要な設定ファイルのコピー
sudo cp /etc/hosts /chroot/app1/etc/
sudo cp /etc/resolv.conf /chroot/app1/etc/
sudo cp /etc/nsswitch.conf /chroot/app1/etc/

# 4. 必要なライブラリのコピー(PHPが依存するもの)
sudo mkdir -p /chroot/app1/usr/lib
sudo cp -r /usr/lib/x86_64-linux-gnu /chroot/app1/usr/lib/

# 5. タイムゾーン設定
sudo mkdir -p /chroot/app1/usr/share/zoneinfo
sudo cp -r /usr/share/zoneinfo/* /chroot/app1/usr/share/zoneinfo/

# 6. 一時ディレクトリの権限設定
sudo chmod 1777 /chroot/app1/tmp

次に、PHP-FPMのプール設定ファイルでchroot環境を有効化します。

[app1]
user = app1user
group = app1group

; chroot設定
chroot = /chroot/app1
chdir = /var/www/html

; chrootディレクトリ内での設定
listen = /var/run/php-fpm-app1.sock
listen.owner = nginx
listen.group = nginx

; open_basedirはchroot後の絶対パスで指定
php_admin_value[open_basedir] = /var/www/html:/tmp

; セッションディレクトリ(chroot内のパス)
php_admin_value[session.save_path] = /tmp

chroot環境を構築する際の最も重要な注意点は、アプリケーションが必要とするすべてのファイル、ライブラリ、デバイスファイルをchroot内に配置する必要があることです。不足があると、予期しないエラーが発生します。特に以下の要素を確認してください。

  • PHPが依存する共有ライブラリ(lddコマンドで確認可能)
  • データベース接続に必要なソケットファイルやライブラリ
  • タイムゾーン情報
  • DNS解決に必要なファイル(/etc/resolv.conf、/etc/hostsなど)

chrootの利点と制限事項

chroot機能には明確な利点がある一方で、いくつかの制限事項も理解しておく必要があります。

chrootの主な利点:

  • セキュリティの向上: ファイルシステムレベルでの隔離により、攻撃の影響範囲を限定できる
  • マルチテナント対応: 複数の顧客のアプリケーションを同一サーバー上で安全に運用できる
  • 軽量な実装: コンテナやVMと比較して、リソースオーバーヘッドが小さい
  • 柔軟な権限管理: アプリケーションごとに異なるファイルシステム構造を提供できる

chrootの制限事項:

  • 完全な隔離ではない: プロセス空間やネットワーク空間は共有されるため、完全な仮想化ではない
  • root権限の必要性: chroot環境の構築と管理にはroot権限が必要
  • メンテナンスの複雑性: システムアップデート時に、chrootディレクトリ内のファイルも更新する必要がある
  • ストレージ使用量の増加: 各chroot環境に必要なファイルを複製するため、ディスク使用量が増加する
  • デバッグの困難さ: 問題発生時のトラブルシューティングが通常の環境より複雑になる

現代的なアプローチとしては、本格的な隔離が必要な場合はDockerなどのコンテナ技術を検討し、軽量な隔離で十分な場合はchrootを使用するという使い分けが推奨されます。

追加のセキュリティ強化策

基本的な権限設定やchroot機能に加えて、PHP-FPMとPHPの設定レベルでさらなるセキュリティ強化を実施することで、多層防御の体制を構築できます。これらの追加対策は、アプリケーションの脆弱性が悪用されるリスクを大幅に低減させます。

危険な関数の無効化設定

PHPには、システムコマンドを実行したり、任意のファイルを操作したりできる強力な関数が多数存在します。これらの関数はアプリケーションで使用しない場合、無効化することでセキュリティリスクを軽減できます。

[app1]
user = app1user
group = app1group

; 危険な関数の無効化
php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,eval,assert

; ファイル操作関連の制限
php_admin_flag[file_uploads] = on
php_admin_value[upload_max_filesize] = 10M
php_admin_value[max_file_uploads] = 5

; リモートファイルアクセスの無効化
php_admin_flag[allow_url_fopen] = off
php_admin_flag[allow_url_include] = off

; メール送信関数の制限(必要に応じて)
php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f noreply@example.com

無効化を検討すべき主な関数:

  • コマンド実行系: exec, passthru, shell_exec, system, proc_open, popen
  • コード評価系: eval, assert, create_function
  • ファイル操作系: show_source, highlight_file(本番環境では不要)
  • 情報漏洩リスク: phpinfo, getmypid, getmyuid

disable_functionsでアプリケーションが実際に使用している関数を無効化してしまうと、アプリケーションが動作しなくなります。必ず開発環境やステージング環境で十分にテストしてから本番環境に適用してください。

PHP情報の非表示化

PHPのバージョン情報やエラーメッセージは、攻撃者にとって有用な情報となり得ます。本番環境では、これらの情報を外部に漏らさないよう設定することが重要です。

[app1]
user = app1user
group = app1group

; エラー表示の制御
php_admin_flag[display_errors] = off
php_admin_flag[display_startup_errors] = off
php_admin_value[error_reporting] = E_ALL & ~E_DEPRECATED & ~E_STRICT
php_admin_flag[log_errors] = on
php_admin_value[error_log] = /var/log/php-fpm/app1-error.log

; PHPバージョン情報の非表示
php_admin_flag[expose_php] = off

; 詳細なエラー情報の抑制
php_admin_flag[html_errors] = off
php_admin_flag[xmlrpc_errors] = off

; セキュリティ関連のヘッダー
php_admin_value[session.cookie_httponly] = 1
php_admin_value[session.cookie_secure] = 1
php_admin_value[session.use_strict_mode] = 1

エラーメッセージは外部に表示しない設定にしつつ、ログファイルには詳細に記録することで、セキュリティとデバッグの両立が可能になります。ログファイルは定期的にローテーションし、適切な権限で保護してください。

セッションセキュリティの強化

セッション管理の脆弱性は、セッションハイジャックやクロスサイトリクエストフォージェリ(CSRF)などの攻撃につながります。PHP-FPMの設定でセッションセキュリティを強化できます。

[app1]
user = app1user
group = app1group

; セッションの保存先(アプリケーション専用ディレクトリ)
php_admin_value[session.save_path] = /var/lib/php/sessions/app1
php_admin_value[session.save_handler] = files

; セッションCookieのセキュリティ設定
php_admin_value[session.name] = APP1_SESSID
php_admin_flag[session.cookie_httponly] = on
php_admin_flag[session.cookie_secure] = on
php_admin_value[session.cookie_samesite] = Strict

; セッション設定の強化
php_admin_flag[session.use_strict_mode] = on
php_admin_flag[session.use_only_cookies] = on
php_admin_flag[session.use_trans_sid] = off
php_admin_value[session.cookie_lifetime] = 0
php_admin_value[session.gc_maxlifetime] = 3600

; セッションID生成のエントロピー強化
php_admin_value[session.entropy_length] = 32
php_admin_value[session.hash_function] = sha256
php_admin_value[session.sid_length] = 48
php_admin_value[session.sid_bits_per_character] = 6

各設定項目の意味:

  • session.cookie_httponly: JavaScriptからのCookieアクセスを防止し、XSS攻撃によるセッション盗用を防ぐ
  • session.cookie_secure: HTTPS接続でのみCookieを送信し、中間者攻撃を防止
  • session.cookie_samesite: CSRF攻撃を防止(Strict、Lax、Noneから選択)
  • session.use_strict_mode: 未初期化のセッションIDの使用を防止
  • session.use_only_cookies: URLパラメータでのセッションID送信を無効化
  • session.gc_maxlifetime: セッションの有効期限(秒単位)

セッション保存ディレクトリの権限設定も重要です。

# セッション保存ディレクトリの作成と権限設定
sudo mkdir -p /var/lib/php/sessions/app1
sudo chown app1user:app1group /var/lib/php/sessions/app1
sudo chmod 700 /var/lib/php/sessions/app1

アプリケーションごとに専用のセッション保存ディレクトリを用意することで、マルチテナント環境でもセッション情報が他のアプリケーションから読み取られるリスクを排除できます。

これらのセキュリティ対策を組み合わせることで、PHP-FPMの実行環境を多層的に保護し、様々な攻撃ベクトルに対する防御を実現できます。ただし、これらの設定はアプリケーションの要件に応じて調整する必要があり、セキュリティと機能性のバランスを取ることが重要です。

“`

“`html

PHP-FPMでよくある問題と対処法

php+fpm+server

PHP-FPMを運用していると、さまざまな問題に直面することがあります。特にメモリ使用量の増加やプロセス管理に関するトラブルは、パフォーマンスやサーバーの安定性に直接影響を与えるため、適切な対処が必要です。ここでは、実際の現場でよく遭遇する問題とその解決策について、実践的な視点から解説します。

メモリ使用量の増加問題への対応

PHP-FPMを長期間運用していると、メモリ使用量が徐々に増加し、最終的にサーバーのリソースを圧迫する問題が発生することがあります。この問題は段階的に悪化するため、早期発見と適切な対処が重要です。メモリ増加の原因は多岐にわたりますが、アプリケーション側のメモリリーク、キャッシュの過剰な保持、プロセスの長期稼働などが主な要因となります。

メモリリークの原因と特定方法

メモリリークとは、プログラムが確保したメモリを適切に解放せず、使用可能なメモリ領域が徐々に減少していく現象です。PHP-FPMにおけるメモリリークの主な原因は以下の通りです。

  • 循環参照によるメモリ解放の遅延 – オブジェクト間で相互参照が発生すると、ガベージコレクションが正常に機能しない場合があります
  • グローバル変数や静的変数への大量データの蓄積 – リクエスト間でデータが蓄積され続けることがあります
  • リソースのクローズ漏れ – データベース接続やファイルハンドルを適切に閉じていない場合に発生します
  • サードパーティライブラリのバグ – 使用しているライブラリ自体にメモリリークが存在することがあります

メモリリークを特定するには、以下のアプローチが有効です。

# プロセスごとのメモリ使用量を監視
watch -n 1 'ps aux | grep php-fpm | grep -v grep'

# より詳細なメモリ情報を取得
pmap -x [プロセスID]

また、PHP側でメモリ使用状況を記録するログ設定も有効です。

; php-fpm.confまたはプール設定
php_admin_flag[log_errors] = on
php_admin_value[error_log] = /var/log/php-fpm/error.log
php_admin_value[memory_limit] = 256M

アプリケーションコード内ではmemory_get_usage()memory_get_peak_usage()を使用して、リクエスト処理の各段階でのメモリ消費を追跡できます。特定のリクエストで異常にメモリ消費が多い場合は、そのコードパスを重点的に調査する必要があります。

メモリ増加を抑える設定変更

メモリ増加を抑制するには、PHP-FPMの設定を適切に調整することが重要です。以下の設定項目が効果的です。

[www]
; プロセスのメモリ制限
php_admin_value[memory_limit] = 256M

; 最大リクエスト処理回数の設定(重要)
pm.max_requests = 500

; プロセス管理方式
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20

pm.max_requestsパラメータは特に重要で、各ワーカープロセスが指定した回数のリクエストを処理した後に自動的に再起動されます。これにより、メモリリークが発生していても、定期的にプロセスが再生成されることで影響を最小限に抑えられます。一般的には300〜1000の範囲で設定されることが多く、アプリケーションの特性に応じて調整します。

また、PHPのガベージコレクション設定を調整することも有効です。

; gc(ガベージコレクション)の有効化
php_admin_flag[zend.enable_gc] = on

; OpCacheの適切な設定
php_admin_value[opcache.memory_consumption] = 128
php_admin_value[opcache.max_accelerated_files] = 10000

プロセス再起動による対策

メモリ問題への根本的な対策として、定期的なプロセス再起動を組み込むことが実践的なアプローチとなります。

前述のpm.max_requestsによる自動再起動に加えて、cron等を使った定期的なサービス再起動も効果的です。

# crontabの例(深夜の低負荷時に再起動)
0 3 * * * systemctl reload php-fpm.service

ただし、単純な再起動では進行中のリクエストが中断される可能性があるため、Gracefulな再起動(後述)を実装することが推奨されます。

また、監視スクリプトを実装してメモリ閾値を超えた場合に自動的に対処する方法もあります。

#!/bin/bash
# メモリ使用率チェックスクリプトの例
THRESHOLD=80
USAGE=$(free | grep Mem | awk '{print ($3/$2) * 100.0}')

if (( $(echo "$USAGE > $THRESHOLD" | bc -l) )); then
    systemctl reload php-fpm.service
    echo "$(date): PHP-FPM reloaded due to high memory usage: ${USAGE}%" >> /var/log/php-fpm-monitor.log
fi

このスクリプトをcronで5〜10分ごとに実行することで、メモリ使用率が一定以上になった際に自動的にPHP-FPMを再起動できます。ただし、頻繁な再起動はパフォーマンスに影響を与えるため、根本原因の解決を優先し、この方法は一時的な対処として位置づけるべきです。

予期しないプロセス起動の原因と解決

PHP-FPMで「設定した以上のプロセス数が起動している」「プロセス数が想定外に増減する」といった問題が発生することがあります。これらは主にプロセスマネージャーの設定理解不足や、設定の競合が原因となっています。

まず、プロセス管理方式(pm)の動作を正確に理解する必要があります。

管理方式特徴プロセス数の挙動
static固定数のプロセスを常時維持pm.max_childrenで指定した数が常に起動
dynamic負荷に応じて動的に増減min_spare_serversからmax_childrenの範囲で変動
ondemandリクエスト時のみ起動0からmax_childrenまで、必要時のみ起動

予期しないプロセス起動の主な原因は以下の通りです。

  • 複数のプール設定が有効になっている – /etc/php-fpm.d/ディレクトリ内に複数の設定ファイルがあり、それぞれがプロセスを生成している場合があります
  • systemdやinitスクリプトによる重複起動 – サービス管理の設定ミスで同じPHP-FPMが複数回起動されることがあります
  • 子プロセスのゾンビ化 – 正常に終了しないプロセスが残り続けている状態です
  • dynamic設定での急激な負荷変動 – アクセス急増時にmax_childrenまで一気にプロセスが生成されます

問題を診断するには、まず現在のプロセス状況を確認します。

# プロセス一覧の確認
ps aux | grep php-fpm

# プール別のプロセス数確認
ps aux | grep php-fpm | awk '{print $NF}' | sort | uniq -c

# 設定ファイルの確認
php-fpm -tt  # 設定テストと全設定の表示

解決策としては、以下のアプローチが有効です。

[global]
; グローバルプロセス制限(全プール合計)
process.max = 100

[www]
; プール個別設定
pm = dynamic
pm.max_children = 50
pm.start_servers = 10
pm.min_spare_servers = 5
pm.max_spare_servers = 20

; プロセス制御パラメータの調整
pm.process_idle_timeout = 10s
pm.max_requests = 500

process.maxパラメータをグローバルセクションで設定することで、すべてのプールを通じた最大プロセス数を制限できます。これにより、複数プールがある環境でもサーバー全体のリソースを保護できます。

Gracefulなreloadの実現方法

PHP-FPMの設定変更やアプリケーションのデプロイ時に、サービスの再起動が必要になることがあります。しかし、単純な再起動では処理中のリクエストが中断され、ユーザーにエラーが返される可能性があります。Gracefulなreload(優雅な再起動)は、既存のリクエストを完了させつつ、新しい設定を適用する手法です。

デフォルトの挙動と課題

PHP-FPMのデフォルトの再起動動作には、いくつかの種類があります。

# 即座に全プロセスを停止して再起動(ダウンタイムあり)
systemctl restart php-fpm.service

# 設定の再読み込み(Graceful reload)
systemctl reload php-fpm.service

# シグナルによる直接制御
kill -USR2 [マスタープロセスID]  # Graceful reload

systemctl reloadUSR2シグナルによるreloadは、以下の動作をします。

  1. マスタープロセスが新しい設定ファイルを読み込む
  2. 新しいワーカープロセスを新設定で起動
  3. 古いワーカープロセスに終了シグナル(QUIT)を送信
  4. 古いプロセスは新規リクエストを受け付けず、処理中のリクエストを完了後に終了

ただし、デフォルト設定では以下の課題があります。

  • process_control_timeoutが短すぎると、長時間実行中のリクエストが強制終了される
  • 新旧プロセスが同時に稼働するため、一時的にメモリ使用量が倍増する可能性がある
  • OpCacheのクリアタイミングによっては、新旧コードが混在することがある
  • Unix socketを使用している場合、socket権限の問題で再起動に失敗することがある

安全な再起動設定の実装

安全なGraceful reloadを実現するには、適切な設定とプロセス管理が必要です。

[global]
; プロセス終了時の待機時間(秒)
process_control_timeout = 30

; 緊急再起動時の設定
emergency_restart_threshold = 10
emergency_restart_interval = 1m

[www]
; リクエスト処理のタイムアウト
request_terminate_timeout = 30s

; プロセスごとの最大リクエスト数
pm.max_requests = 500

; アイドルタイムアウト
pm.process_idle_timeout = 10s

process_control_timeoutは、想定される最長リクエスト処理時間より長く設定することが重要です。例えば、バッチ処理やレポート生成など、最大30秒かかる処理がある場合は、余裕を持って60秒程度に設定します。

より安全な再起動を実現するスクリプト例です。

#!/bin/bash
# safe-reload.sh - PHP-FPMの安全な再起動スクリプト

echo "Starting graceful reload of PHP-FPM..."

# 1. ヘルスチェック
if ! systemctl is-active --quiet php-fpm.service; then
    echo "PHP-FPM is not running. Starting instead of reloading."
    systemctl start php-fpm.service
    exit $?
fi

# 2. 設定ファイルの構文チェック
if ! php-fpm -t 2>/dev/null; then
    echo "Configuration test failed. Aborting reload."
    exit 1
fi

# 3. OpCacheのクリア(Webサーバー経由で実行)
curl -s http://localhost/opcache-clear.php > /dev/null

# 4. Graceful reload実行
systemctl reload php-fpm.service
RELOAD_STATUS=$?

if [ $RELOAD_STATUS -eq 0 ]; then
    echo "Reload signal sent successfully."
    
    # 5. 古いプロセスの終了を待機
    sleep 5
    
    # 6. 新プロセスの起動確認
    NEW_PROCESSES=$(pgrep -c php-fpm)
    if [ $NEW_PROCESSES -gt 0 ]; then
        echo "Reload completed. Active processes: $NEW_PROCESSES"
        exit 0
    else
        echo "Warning: No PHP-FPM processes found after reload."
        exit 1
    fi
else
    echo "Reload failed with status: $RELOAD_STATUS"
    exit $RELOAD_STATUS
fi

デプロイメント時には、以下のような段階的なアプローチも有効です。

# Blue-Green型デプロイメントの例

# 1. 新しいプールを別ポートで起動
[www-new]
listen = /var/run/php-fpm-new.sock
; ... その他の設定

# 2. Webサーバー側で段階的に切り替え
# Nginxの例
upstream php_backend {
    server unix:/var/run/php-fpm.sock weight=1;
    server unix:/var/run/php-fpm-new.sock weight=9;  # 徐々に重みを増やす
}

この方法では、新旧バージョンを並行稼働させながら、トラフィックを徐々に新バージョンに移行できます。問題が発生した場合は即座にロールバック可能で、ダウンタイムゼロでのデプロイメントが実現できます。

また、systemdのサービス定義を工夫することで、より堅牢な再起動制御が可能です。

# /etc/systemd/system/php-fpm.service.d/override.conf
[Service]
# Graceful stopのタイムアウトを延長
TimeoutStopSec=60

# プロセス終了の方法を指定
KillMode=mixed
KillSignal=SIGQUIT

# 再起動時の待機時間
RestartSec=5s

これらの設定により、PHP-FPMのGraceful reloadがより確実に動作し、サービスの可用性を維持しながら設定変更やデプロイメントを実施できます。

“`

“`html

PHP-FPMの最新動向と将来展望

php+server+performance

PHP-FPMは長年にわたりPHPアプリケーションの実行環境として広く採用されてきましたが、技術の進化とともに新たな選択肢も登場しています。このセクションでは、PHP-FPMの最新バージョンにおける改善点を確認しつつ、次世代のPHP実行環境と比較しながら、今後の技術選定において考慮すべきポイントについて解説します。

最新バージョンでの機能追加と改善点

PHP-FPMは単独のプロジェクトではなく、PHPコア開発の一部として継続的に改善が進められています。最新バージョンでは、パフォーマンス向上とセキュリティ強化の両面で重要な進化を遂げています。

PHP 8.x系での主要な変更点

PHP 8.0以降のバージョンでは、言語仕様そのものに大きな変更が加えられました。JIT(Just-In-Time)コンパイラの導入により、特定のワークロードにおいて大幅なパフォーマンス向上が実現されています。JITは主に計算集約型の処理で効果を発揮し、従来のOpcacheと組み合わせることで最適な実行環境を構築できます。

また、PHP 8.0ではnamed argumentsやattributes、union typesなど、開発効率を向上させる言語機能が多数追加されました。これらの機能は直接的にPHP-FPMの動作には影響しませんが、アプリケーションコードの品質向上とメンテナンス性の改善に貢献します。PHP 8.1では、enumやfibers、readonly propertiesが追加され、さらに近代的な開発スタイルをサポートしています。

パフォーマンス面では、PHP 8.0以降で内部構造の最適化が進み、メモリ使用量の削減と実行速度の向上が図られています。特にオブジェクト指向プログラミングにおけるオーバーヘッドが軽減され、大規模なアプリケーションでも効率的に動作するようになりました。

PHP-FPM固有の進化

PHP-FPM自体も継続的に改善が加えられており、近年のバージョンでは以下のような機能強化が行われています。

まず、ステータスページの情報拡充が挙げられます。プロセスの状態やリクエスト処理の詳細情報がより詳細に取得できるようになり、運用時のトラブルシューティングが容易になりました。JSON形式での出力もサポートされ、監視ツールとの連携がスムーズに行えます。

また、プロセス管理機能の改善により、graceful shutdownの動作がより安定しました。設定変更時やメンテナンス時に、処理中のリクエストを適切に処理してからプロセスを終了させることができ、サービスの可用性が向上しています。

セキュリティ面では、systemd統合の強化により、サービス管理がより安全かつ効率的になっています。systemdのソケットアクティベーション機能を利用することで、必要なタイミングでのみプロセスを起動させることが可能です。

さらに、ログ機能の拡張により、デバッグ情報やパフォーマンス指標がより詳細に記録できるようになりました。スローログの精度向上により、パフォーマンス問題の特定がより正確に行えます。

次世代PHP実行環境との比較

PHP-FPMは成熟した技術ですが、近年では異なるアーキテクチャを持つPHP実行環境も登場しています。それぞれの特徴を理解し、適切な場面で使い分けることが重要です。

RoadRunnerの特徴と使い分け

RoadRunnerは、Go言語で開発された高性能なPHPアプリケーションサーバーです。最大の特徴は、ワーカープロセスの永続化により、リクエストごとにPHPプロセスを起動する必要がない点です。

従来のPHP-FPMでは、各リクエストでPHPスクリプトを読み込み、フレームワークを初期化する必要がありました。RoadRunnerでは、ワーカープロセスが常駐し、アプリケーションの初期化を一度だけ実行します。その結果、リクエスト処理のレイテンシが大幅に削減され、特にフレームワークの起動コストが高いLaravelやSymfonyなどで顕著な効果が得られます。

また、HTTPサーバー機能に加えて、キューワーカーやCronジョブ、gRPCサーバーなど、複数の機能を統合して提供している点も特徴です。一つのツールで多様なワークロードに対応できるため、インフラ構成をシンプルにできます。

ただし、メモリリークへの注意が必要です。プロセスが永続化されるため、アプリケーションコードにメモリリークがあると、長時間稼働させた際に問題が顕在化します。定期的なワーカーの再起動設定や、メモリ使用量の監視が重要になります。

使い分けとしては、高トラフィックのAPIサーバーや、リアルタイム性が求められるアプリケーションに適しています。一方、既存のPHP-FPM環境からの移行には、アプリケーションコードの見直しが必要な場合があるため、段階的な導入が推奨されます。

Swooleの特徴と使い分け

SwooleはPHPの拡張モジュールとして実装された、非同期・コルーチンベースの実行環境です。イベント駆動型のアーキテクチャにより、従来の同期的なPHPの制約を超えた高性能な処理を実現します。

Swooleの最大の特徴は、コルーチンによる非同期I/O処理です。データベースクエリやHTTPリクエストなどのI/O待ち時間中に、他のタスクを処理できるため、同時接続数が多い環境でも効率的にリソースを活用できます。WebSocketやHTTP/2のサーバー実装も含まれており、リアルタイム通信が必要なアプリケーションに最適です。

また、PHPの拡張モジュールとして実装されているため、既存のPHPコードとの互換性が比較的高く、段階的な導入が可能です。ただし、非同期プログラミングモデルの理解が必要であり、従来の同期的なPHPとは異なる設計思想が求められます。

Swooleは中国を中心に広く採用されており、HyperfやEasySwooleなど、Swooleをベースとしたフレームワークも登場しています。チャットアプリケーション、ゲームサーバー、IoTデータ収集など、多数の同時接続を処理する必要がある場面で威力を発揮します。

使い分けとしては、WebSocketを使ったリアルタイム通信や、マイクロサービスアーキテクチャにおける高速なサービス間通信が必要な場合に適しています。一方、学習コストが高く、開発者のスキルセットによっては導入障壁となる可能性があります。

ReactPHPの特徴と使い分け

ReactPHPは、PHPで非同期プログラミングを実現するためのライブラリ群です。Node.jsのイベントループモデルをPHPに持ち込んだ設計となっており、純粋なPHPコードで非同期処理を記述できます。

ReactPHPの特徴は、外部の拡張モジュールに依存せず、純粋なPHPコードのみで動作する点です。インストールはComposerで完結し、環境構築が容易です。イベント駆動型のアーキテクチャにより、長時間稼働するデーモンプロセスや、ストリーミング処理などに適しています。

また、モジュラーな設計により、必要な機能のみを選択して利用できます。HTTPサーバー、WebSocketサーバー、DNSリゾルバ、データベースクライアントなど、各コンポーネントが独立しており、柔軟な構成が可能です。

ただし、パフォーマンスはSwooleに劣る場合があります。PHPレベルでの実装であるため、C言語で実装されたSwooleと比較すると、絶対的な速度では不利になります。また、エコシステムがSwooleほど成熟していない点も考慮が必要です。

使い分けとしては、特定の非同期処理が必要だが、Swooleのような大規模な導入は不要な場合や、環境制約により拡張モジュールのインストールが困難な場合に適しています。また、学習目的でイベント駆動型プログラミングを理解したい場合にも有用です。

サーバーレスPHPの動向

クラウドネイティブな開発スタイルの普及に伴い、サーバーレス環境でのPHP実行も注目されています。AWS LambdaやGoogle Cloud Functions、Azure Functionsなどのプラットフォームで、PHPを実行するための仕組みが提供されています。

サーバーレスアーキテクチャでは、インフラ管理の負担が大幅に軽減され、リクエスト量に応じた自動スケーリングが実現されます。Bref(Serverless PHP on AWS Lambda)のようなツールを使用することで、既存のPHPアプリケーションをサーバーレス環境に比較的容易に移行できます。

特に、不定期に発生するバッチ処理や、トラフィックの変動が大きいアプリケーションにおいて、コスト効率が高くなります。使用した分だけの課金モデルにより、アイドル時のコストを削減できます。

一方で、コールドスタートの問題があります。関数が一定時間使用されないと、実行環境が破棄され、次回のリクエスト時に初期化が必要となり、レイテンシが増加します。また、実行時間の制限(多くのプラットフォームで15分程度)があるため、長時間実行される処理には適していません。

現状では、PHPのサーバーレス環境は発展途上であり、Node.jsやPythonと比較するとツールやドキュメントの充実度で劣る面があります。しかし、コンテナベースのサーバーレス(AWS FargateやCloud Run)の普及により、より柔軟な実行環境の選択が可能になりつつあります。

今後の技術選定のポイント

多様な選択肢が存在する現在、適切な技術を選定するためには、短期・中期の視点を持ちながら、プロジェクトの要件や組織の状況を総合的に判断する必要があります。

短期的な対応方針

既存システムの運用や、新規プロジェクトの立ち上げにおいて、短期的な視点では安定性と実績を重視することが重要です。

PHP-FPMは成熟した技術であり、膨大な運用実績とノウハウが蓄積されています。トラブルシューティングの情報が豊富で、問題発生時の対応が容易です。チームメンバーのスキルセットが従来型のPHP開発である場合、学習コストを最小限に抑えながらプロジェクトを進められます。

新規プロジェクトであっても、要件が明確でない初期段階では、PHP-FPMのような標準的な技術を選択し、パフォーマンス要件が明確になった段階で最適化を検討するアプローチが有効です。premature optimization(早すぎる最適化)を避けることで、開発速度を維持できます。

また、既存システムの改善においては、アプリケーションコードの最適化やデータベースクエリのチューニング、キャッシュ戦略の見直しなど、実行環境を変更せずに行える施策を優先すべきです。多くの場合、これらの改善だけで十分なパフォーマンス向上が得られます。

中期的な展望と戦略

中期的な視点では、技術トレンドとビジネス要件のバランスを考慮した戦略が必要です。

トラフィックの増加が予測される場合や、リアルタイム性が求められる機能の追加が計画されている場合は、RoadRunnerやSwooleなどの次世代実行環境の導入を検討する価値があります。ただし、段階的な移行計画を立て、リスクを最小化することが重要です。

具体的には、まずマイクロサービスアーキテクチャの一部として新しい技術を導入し、運用ノウハウを蓄積するアプローチが効果的です。特定のAPIエンドポイントやバックグラウンドジョブ処理など、比較的独立性の高い部分から始めることで、既存システムへの影響を最小限に抑えられます。

また、開発チームのスキル向上も並行して進める必要があります。非同期プログラミングやイベント駆動型アーキテクチャの理解には時間がかかるため、研修や勉強会を通じて計画的に知識を蓄積していくべきです。

クラウドネイティブな環境への移行を検討している場合は、コンテナ化(Docker/Kubernetes)を先行させることで、実行環境の選択肢を広げることができます。コンテナベースの環境では、PHP-FPMから他の実行環境への切り替えが比較的容易になります。

PHP-FPMが今後も重要である理由

新しい技術が登場する中でも、PHP-FPMは引き続き重要な選択肢として位置づけられます。その理由は複数あります。

第一に、安定性と互換性です。PHP-FPMはPHP公式の一部として開発されており、新しいPHPバージョンとの互換性が保証されています。長期間の運用が求められるエンタープライズアプリケーションでは、この安定性が大きな価値を持ちます。

第二に、エコシステムの成熟度です。主要なLinuxディストリビューションすべてで標準パッケージとして提供され、NginxやApacheとの統合も確立されています。監視ツール、デプロイツール、セキュリティツールなど、周辺ツールとの連携も充実しており、運用効率が高くなります。

第三に、運用ノウハウの蓄積です。多くの組織で長年の運用経験があり、トラブルシューティングのベストプラクティスが確立されています。新しいメンバーの教育も容易で、採用市場でもPHP-FPMの経験を持つエンジニアは豊富です。

第四に、適度なパフォーマンスです。最先端の技術と比較すると絶対的な性能は劣りますが、多くのWebアプリケーションにとっては十分なパフォーマンスを提供します。過度な最適化は複雑性を増すため、要件に対して適切なレベルの技術を選択することが重要です。

最後に、リスクの低さが挙げられます。新しい技術には未知の問題や、将来的なサポート継続の不確実性が伴います。PHP-FPMはPHPコミュニティ全体でサポートされており、長期的な継続性が期待できます。

これらの理由から、PHP-FPMは今後も多くのプロジェクトにおいて第一選択肢となり続けるでしょう。新しい技術を検討する際も、PHP-FPMをベースラインとして比較評価することで、適切な判断が可能になります。技術選定においては、最新性だけでなく、プロジェクトの特性や組織の能力に合った最適な選択を行うことが成功の鍵となります。

“`