PHP-FPMからOpenMetrics形式でメトリクス取得するときに遭遇した落とし穴と外部ツールを使った場合との比較

はじめに

この記事は, CYBOZU SUMMER BLOG FES '25の記事です.

こんにちは, Garoon開発チームの森脇です.

PHP-FPMを使ったWebシステムで, 応答が遅くなった, 突然繋がらなくなった, という経験はありませんか? このような状況において, PHP-FPMのメトリクスが取得できると, プロセスやリクエストの状況を把握でき, これら原因の究明や問題の解決に役立てることができます.

私たちが開発している中堅・大規模組織向けグループウェアGaroonでも, PHP-FPMを使っています. これまでGaroon開発チームでは, 主にログベースでPHP-FPMの状態を調査していましたが, メトリクスを活用することで, より効率的な監視や迅速な障害対応が可能になると考えました.

メトリクス取得には2種類の方法があります.

  1. PHP-FPMからOpenMetrics形式でメトリクスを取得する方法(ref).
  2. php-fpm_exporterなどのデータ形式を変換する外部ツールを用いる方法.

Garoon開発チームでは前者の方法を採用してメトリクスの取得に取り組みました.

本記事では, 取り組んだ内容をもとに以下の3つのお話をします.

  • PHP-FPMからOpenMetrics形式でメトリクスを取得する方法と遭遇した落とし穴
  • 外部ツールと比較してわかった違い
  • Garoon開発チームが前者の方法を採用した理由

本記事は, PHP-FPMを使ってWebシステムを運用しているが, PHP-FPMのメトリクスをまだ取得していない方を対象としています. この記事が, PHP-FPMのメトリクスを取得する際の判断材料の1つとなれば嬉しいです.

PHP-FPMからOpenMetrics形式でメトリクスを取得する方法と遭遇した落とし穴

本章では, まず最初に検証環境を例に設定方法の説明を行い, 次に設定の際に遭遇した落とし穴について説明します. そして最後にどのような種類のメトリクスが取得できるのかをまとめます.

検証環境は以下のリポジトリで公開しています.
GitHub - Daichi-1637/PHP-FPM_metrics

設定方法

PHP-FPMにはFPMの情報ページという, FPMプールの状態を取得できる仕組みがあります. PHP8.1.0から, FPMの情報ページをOpenMetrics形式で出力できるようになりました. OpenMetricsとはメトリクスを送信する際のデータの仕様のことで, 多くの監視ツールがこの仕様に対応しています.つまり, OpenMetrics形式でFPMの情報ページを公開しておくことにより, PrometheusやDatadogなどの監視ツールを使って, データ形式の変更を行うことなくPHP-FPMの情報をメトリクスとして収集できるということです.

メトリクスの取得には, 監視ツールがメトリクスを収集できるようにPHP-FPMとWebサーバーを設定する必要があります. 検証環境ではPHP-FPMとWebサーバーを以下のように設定しています.

なお, 検証環境のリポジトリでは, TCP Socketを使った検証環境を tcp_socket ディレクトリに, UNIX Domain Socketを使った検証環境を unix_domain_socket ディレクトリに用意しています.

  • PHP-FPMのプロセスプールの設定例
[www]
# TCP Socket で通信を行う場合の設定例
pm.status_listen = 0.0.0.0:9001
# UNIX Domain Socket で通信を行う場合の設定例
# pm.status_listen = /var/run/php-fpm/php-fpm-status.sock
pm.status_path = "/metrics"
  • Webサーバ(NGINX)の設定例
server {
    # ...(中略)...
    location /metrics {
        # TCP Socket で通信を行う場合の設定例
        fastcgi_pass    php-fpm:9001; 
        # UNIX Domain Socket で通信を行う場合の設定例
        # fastcgi_pass    unix:/var/run/php-fpm/php-fpm-status.sock;
        include         fastcgi_params;
        fastcgi_param   REQUEST_METHOD     GET;
        fastcgi_param   SCRIPT_FILENAME    /dev/null;
        fastcgi_param   SCRIPT_NAME        /metrics;
        fastcgi_param   QUERY_STRING       openmetrics;
    }
}

PHP-FPMとWebサーバーの設定方法については, 公式ドキュメントに記載されているため, 詳しい説明は割愛します: PHP: FPM の情報ページ - Manual

上記の設定を行った上で, NGINXの/metricsエンドポイントにアクセスすると, PHP-FPM の情報をOpenMetrics形式で取得できます.

$ curl --request GET --url http://localhost:8081/metrics

# HELP phpfpm_up Could pool www using a dynamic PM on PHP-FPM be reached?
# TYPE phpfpm_up gauge
phpfpm_up 1
# HELP phpfpm_start_since The number of seconds since FPM has started.
# TYPE phpfpm_start_since counter
phpfpm_start_since 66
# HELP phpfpm_accepted_connections The number of requests accepted by the pool.
# TYPE phpfpm_accepted_connections counter
phpfpm_accepted_connections 0
# HELP phpfpm_listen_queue The number of requests in the queue of pending connections.
# TYPE phpfpm_listen_queue gauge
...(以下略)...

設定の際に遭遇した落とし穴

実際にメトリクスを取得してみて遭遇した落とし穴を3つ紹介します.

その1: クエリパラメータfullをつけてもプロセスごとの詳細な情報は取れない

FPMの情報ページは基本的にクエリパラメータ full を指定することで, プロセスごとの詳細な情報を出力できます.

しかし, OpenMetrics形式で出力している場合だと, full パラメータを指定しても追加の情報を取得できません.(php-src の該当コード).

そのため, この方法だとプロセスごとの詳細な情報は取得できないことになります.

その2: Linux系OS上でアプリケーションと通信するためのsocketとしてUNIX Domain Socketを使っているとlisten queueに関するメトリクスは常に0の値を取る

Linux系OS上でアプリケーションと通信するためのsocketとしてUNIX Domain Socketを使う場合, phpfpm_max_listen_queuephpfpm_listen_queue_length, phpfpm_listen_queue, のメトリクスは常に0の値を取ります. なお, アプリケーションと通信するためのsocketとは, PHP-FPMのプール設定ファイルで指定するlistenの値のことを指しています.

この問題は, キューの長さを取得するためにPHP-FPMで内部で使用しているgetsockoptというC言語の標準ライブラリ関数の一部機能が, Linux系OSでは対応できていないことが関係しているようです(ref).

以下の図は, 左上がphpfpm_max_listen_queue, 右上がphpfpm_listen_queue_length, 左下がphpfpm_slow_requests, 右下がphpfpm_listen_queueのグラフを示しています.

メトリクスは取得できているように見えますが, 左下のphpfpm_slow_requestsを除いた全てのグラフが常に0になっていることが読み取れます. 一方, 下図はTCP socketを使った場合の図ですが, 左下のグラフ以外も0以外の値を取っており, こちらの方が正しそうです.

なお, メトリクス取得のために使用するsocket(pm.status_listen)に関しては, どちらの通信方法であっても問題ないようです.

その3: PHP8.1.0以降のバージョンであっても一部メトリクスは取れない

phpfpm_memory_peak のメトリクスは, PHP 8.4.0から実装されたメトリクスなので, それ以前のバージョンでは取得できません.

GaroonではPHP 8.3を使っているため, 残念ながらこのメトリクスは取得することができませんでした.

取得できるメトリクスのまとめ

PHP-FPMからOpenMetrics形式でメトリクスを取得する方法では, おおまかに, プロセスの状態を示すメトリクス, リクエストに関するメトリクス, メモリに関するメトリクスの3種類のメトリクスが取れるようでした.

これに対して, Linux系OSでアプリケーションと通信するためのsocketとしてUNIX Domain Socketを使っている場合だと, リクエストに関するメトリクスがphpfpm_slow_requestsphpfpm_accepted_connectionsしか取れないことになります. また, PHP 8.4.0よりも前のバージョンを使っている場合だと, メモリに関するメトリクスのphpfpm_memory_peakが取れないようになっています.

使用するsocketの種類やPHPのバージョンで取得できるメトリクスの数が変わるので注意が必要です.

構築する環境と取得できるメトリクスの対応表を以下に示します.

外部ツールとの比較

PHP-FPMからOpenMetrics形式でメトリクスを取得するための設定方法と, 取得できるメトリクスの種類がわかったところで, 次は外部ツールを使う方法と比較します.

ここでは, 外部ツールとしてphp-fpm_exporterを使います.

本章では, 最初に検証環境を例に設定方法の説明を行い, 最後に取得できるメトリクスのまとめを行います.

設定方法

php-fpm_exporterは, PHP-FPMの情報ページからデータを取得して, それをHTTP形式でメトリクスとして出力することができます. つまり, php-fpm_exporterを使う場合でも, PHP-FPMのプロセスプールの設定でFPMの情報ページを公開しておく必要はありますが, Webサーバーを用意する必要はありません. PHP-FPMからphp-fpm_exporterを経由して, 監視ツールはHTTP形式でメトリクスを取得できるようになっています.

検証環境ではPHP-FPMとphp-fpm_exporterを以下のように設定しています.

なお, 検証環境のリポジトリでは, php-fpm_exporterディレクトリにphp-fpm_exporterを使った検証環境を用意しています.

  • PHP-FPMのプロセスプールの設定
[www]
# UNIX Domain Socket で通信を行う場合の設定例
pm.status_listen = /var/run/php-fpm/php-fpm-status.sock
# TCP Socket で通信を行う場合の設定例
# pm.status_listen = 0.0.0.0:9001
pm.status_path = "/metrics"
  • php-fpm_exporter の設定(compose.yaml)
php-fpm_exporter:
  image: hipages/php-fpm_exporter:2.2.0
  container_name: php-fpm_exporter
  ports:
    - 9253:9253
  environment:
    - PHP_FPM_WEB_LISTEN_ADDRESS=:9253
    - PHP_FPM_SCRAPE_URI=unix:///var/run/php-fpm/php-fpm-status.sock;/metrics
  volumes:
    - type: bind
      source: php-fpm_socket_volume
      target: /var/run/php-fpm
      read_only: true 

php-fpm_exporterの設定方法についても, php-fpm_exporterのリポジトリのREADMEに書かれているため, 詳しい説明は割愛します.

上記の設定を行った上で, php-fpm_exporterの/metricsエンドポイントにアクセスすると, メトリクスが取得できます.

$ curl --request GET --url http://localhost:9253/metrics

# HELP go_gc_cycles_automatic_gc_cycles_total Count of completed GC cycles generated by the Go runtime.
# TYPE go_gc_cycles_automatic_gc_cycles_total counter
go_gc_cycles_automatic_gc_cycles_total 7
# HELP go_gc_cycles_forced_gc_cycles_total Count of completed GC cycles forced by the application.
# TYPE go_gc_cycles_forced_gc_cycles_total counter
go_gc_cycles_forced_gc_cycles_total 0
# HELP go_gc_cycles_total_gc_cycles_total Count of all completed GC cycles.
# TYPE go_gc_cycles_total_gc_cycles_total counter
...(以下略)...

取得できるメトリクスのまとめ

php-fpm_expoterterでは, おおまかに, プロセスの状態を示すメトリクス, リクエストに関するメトリクス, メモリに関するメトリクス, に加えて, CPUに関するメトリクス, スクレイプに関するメトリクスの5種類のメトリクスが取れるようでした.

この他に, php-fpm_expoterterでは, php-fpm_exporterの内部の状態を示すメトリクス( go_から始まるメトリクス や promhttp_metric_から始まるメトリクス, process_から始まるメトリクス )も取得できるようです.

PHP-FPM関連のメトリクスだけを見ても, PHP-FPMからOpenMetrics形式でメトリクスを取得する方法と比べて, 取得できるメトリクスの数は多く, 一部のメトリクスでは, childラベルを使ってプロセスごとのメトリクスも取れるようでした.

メトリクスが多くなっている理由は, php-fpm_exporterが内部でFPMの情報ページをjson形式で取得していることが関係しているようです. OpenMetrics形式の場合はfullパラーメータをつけてもプロセスごとの詳細な情報は取得できませんが, json形式だと取得できます. php-fpm_exporterではFPMの情報ページをjson形式で取得した後, 内部でデータ形式を変換しているため, プロセスごとの詳細な情報もメトリクスとして出力できているようでした.

一方で, 遭遇した落とし穴 その2で説明したlisten queueのメトリクスの問題に関しては, PHP-FPM自体の問題であるため, php-fpm_exporterを使った場合でも同じ問題が発生しました. また, 遭遇した落とし穴 その3で説明したphpfpm_memory_peakのメトリクスに関しては, 現時点(2025/09/21)では最新のphp-fpm_exporter 2.2.0 を使った場合でもPHPのバージョンに関係なく, phpfpm_memory_peakのメトリクスは取れないようでした.

php-fpm_exporterを使って取得できるメトリクスとPHP-FPMからOpenMetrics形式で取得した場合のメトリクスをまとめた表を以下に示します. なお, php-fpm_exporterの内部の状態を示すメトリクスは以下の表からは除いています.

Garoon開発チームがPHP-FPMからOpenMetrics形式でメトリクスを取得する方法を採用した理由

それぞれの方法で取得できるメトリクスの違いを見てきましたが, Garoon開発チームではPHP-FPMから直接, メトリクスを取得する方法を採用しました. その理由は, 2つあります.

1つ目は, サイボウズで定めているOSSセキュリティポリシーの観点から, 外部ツールを導入する障壁がやや大きかったからです, 一方で, PHP-FPMからOpenMetrics形式でメトリクスを取得する方法だと, PHP-FPMとWebサーバーさえあればメトリクスの取得が可能であるため, 導入障壁は小さく, 導入しやすいです.

2つ目は, CPUに関するメトリクスやメモリに関するメトリクスは別手段で既に取得できていたからです. 既に取得できているメトリクスはFPMのプロセス単位で取れているわけでは無いですが, ひとまずPHP-FPMからOpenMetrics形式でメトリクスを取得する方法で始めてみて不足があるようならば, 外部ツールを使う方法を検討することにしました.

最後に

本記事では, 最初にPHP-FPMからOpenMetrics形式でメトリクスを取得する方法と遭遇した落とし穴を紹介しました. その後, php-fpm_exporterを例にデータ形式を変換する外部ツールを用いた方法との違いを比較し, 最後にGaroon開発チームがPHP-FPMからOpenMetrics形式でメトリクスを取得する方法を採用した理由を紹介しました.

PHP-FPMからOpenMetrics形式でメトリクスを取得する方法では, php-fpm_exporterと比べて, まだ取得できるメトリクスが少ない印象がありました. しかし, OSSセキュリティポリシーなどの外部ツールを使うことに障壁がある場合や他で取得しているメトリクスで補える場合などでは, PHP-FPMとWebサーバーさえあればメトリクスの取得できるため, 初めてPHP-FPMのメトリクスを取ってみる場合の選択肢としては良さそうに感じました.

Garoon開発チームでは, まだメトリクスを取得できるように整備したばかりなので, 今後, PHP-FPMのメトリクスを活用して, より効率的な監視や迅速な障害対応を可能にしていきたいです.

以上, 読んでいただきありがとうございました!!