内部サーバー

OxPHP は、ヘルスチェック、Prometheus メトリクス、ライブ設定の確認のために、専用ポート上で別個の HTTP サーバーを実行します。このサーバーはメインのアプリケーションリスナーから完全に分離されており、TLS もレート制限もリクエストID もイベント処理もありません。

仕組み

INTERNAL_ADDR にリッスンアドレス(例: 127.0.0.1:9090)を設定すると、OxPHP はそのアドレス上で 2 つ目の HTTP リスナーを起動します。内部サーバーは以下の組み込みエンドポイントを公開します。

  • /health — 集約された JSON ステータス
  • /health/liveness/healthz(エイリアス) — liveness プローブ
  • /health/readiness/readyz(エイリアス) — readiness プローブ
  • /health/startup/startupz(エイリアス) — startup プローブ
  • /metrics — Prometheus 形式の出力
  • /config — 有効なサーバー設定の JSON

プラグインは /__<plugin>/ プレフィックスの下に追加のエンドポイントを登録できます。グレースフルシャットダウンの間、内部サーバーはメインサーバーが接続のドレインを完了するまで利用可能な状態を保ちます。

Note

内部サーバーは INTERNAL_ADDR が明示的に設定されている場合にのみ起動します。設定されていない場合、ヘルス、メトリクス、設定の各エンドポイントは HTTP 経由で利用できません。

設定

Variable Default Description
INTERNAL_ADDR (未設定) 内部サーバーのアドレス。未設定の場合は起動しません。ポートのみの値(:9090 または 9090)は 127.0.0.1 にバインドします。ホスト外へ公開するには明示的に 0.0.0.0:9090 を使用してください。例: 127.0.0.1:9090
INTERNAL_ALLOW_IPS (未設定) カンマ区切りの CIDR/IP 許可リスト。リスト外のピアは /metrics/config/__<plugin>/ パスに対して 403 を受け取ります。ヘルスプローブは常に到達可能なままです。未設定または空の場合はすべて許可されます。ループバックは暗黙的に含まれません。localhost からのアクセスを維持するには 127.0.0.1/32 を列挙してください。不正な形式のリストは起動を中断させます

エンドポイント

GET /health

JSON のヘルスステータスを返します。Kubernetes の readiness プローブと liveness プローブにこれを使用してください。

200 OK — すべてのシステムが正常:

json
{ "status": "ok", "uptime_secs": 3612, "total_requests": 48203, "active_connections": 7, "executor_healthy": true, "plugins": { "otel": "ok" } }

503 Service Unavailable — プラグインが失敗を報告した場合:

json
{ "status": "degraded", "uptime_secs": 3612, "total_requests": 48203, "active_connections": 7, "executor_healthy": true, "plugins": { "otel": "failed" } }

plugins オブジェクトは、ロードされたすべてのプラグインをそのヘルスステータス("ok""degraded""failed")とともに列挙します。いずれかのプラグインが "failed" を報告した場合、またはスクリプトエグゼキューターが正常でない(executor_healthy: false)場合、このエンドポイントは 503 を返します。"degraded" のプラグインは JSON ボディには表示されますが、HTTP ステータスは 200 のままです。

GET /metrics

Prometheus 互換のメトリクスをテキスト形式(text/plain; version=0.0.4; charset=utf-8)で返します。常に 200 を返します。

bash
curl http://localhost:9090/metrics
text
# HELP oxphp_requests_total Total HTTP requests # TYPE oxphp_requests_total counter oxphp_requests_total 48203 # HELP oxphp_active_connections Current open connections # TYPE oxphp_active_connections gauge oxphp_active_connections 7 ...

利用可能なメトリクスの完全な一覧については、メトリクスを参照してください。

GET /config

有効なサーバー設定を JSON として返します。常に 200 を返します。

bash
curl -s http://localhost:9090/config | jq .
json
{ "listen_addr": "0.0.0.0:80", "document_root": "/var/www/html/public", "entry_file": "/var/www/html/public/index.php", "log_level": "info", "executor_type": "sapi", "php_workers": "8", "tokio_workers": 4, "queue_capacity": 1024, "max_connections": 10000, "drain_timeout_seconds": 30, "header_timeout_seconds": 5, "rate_limit": 100, "rate_window_seconds": 60, "tls_enabled": true, "tls_min_version": "1.2", "compression_level": 4, "access_log": "all", "max_query_body": 524288, "worker_mode_enabled": false, "worker_max_memory_mib": 0, "static_max_age": 2592000, "static_revalidate": false, "async_workers": 0, "async_queue_capacity": 0, "async_max_fibers": 256, "async_in_flight_cap": 0, "trace_context": false, "superglobals_enabled": true, "trusted_proxies": false, "plugins": {} }

TLS 証明書と鍵ファイルのパスが出力されることは決してなく(公開されるのは tls_enabled のブール値のみ)、internal_addrerror_pages_dir は返されるレスポンスから除去されます。これらはデプロイのトポロジーやファイルシステムのパスであり、攻撃者を助けるものであってスクレイパーには不要だからです。

プラグインエンドポイント

プラグインは /__<plugin_name>/ プレフィックスの下にカスタムエンドポイントを登録できます。たとえば、otel という名前のプラグインは /__otel/status を公開できます。これらのエンドポイントは、対応するプラグインがロードされてハンドラーを登録している場合にのみ利用可能です。

組み込みエンドポイントにもプラグインエンドポイントにも一致しないパスは、404 Not Found を返します。

Kubernetes との統合

readiness プローブ

Kubernetes が Pod にトラフィックをルーティングするかどうかを制御するには、/health を使用します。

yaml
readinessProbe: httpGet: path: /health port: 9090 initialDelaySeconds: 2 periodSeconds: 5

/health が 503 を返すと、Kubernetes は Service のエンドポイント一覧から Pod を除外します。エンドポイントが再び 200 を返すと、トラフィックが再開されます。

liveness プローブ

応答しなくなった Pod を再起動するには、同じエンドポイントを使用します。

yaml
livenessProbe: httpGet: path: /health port: 9090 initialDelaySeconds: 5 periodSeconds: 10 failureThreshold: 3

startup プローブ

起動が遅いアプリケーション(大規模なフレームワーク、重いオートローダーなど)向け:

yaml
startupProbe: httpGet: path: /health port: 9090 initialDelaySeconds: 1 periodSeconds: 2 failureThreshold: 15

セキュリティ

内部サーバーにはベアラートークン認証がありません。アクセスは、どこにバインドするかと INTERNAL_ALLOW_IPS によって制御されます。保護するには次のようにします。

  • localhost にバインドする — ポートのみの INTERNAL_ADDR:9090)はすでに 127.0.0.1 にバインドされます。明示的な 127.0.0.1:9090 は、そのポートをコンテナまたはホスト内からのみ到達可能に保ちます
  • リスナーをホスト外から到達可能にする必要がある場合は**INTERNAL_ALLOW_IPS を設定する** — CIDR/IP の許可リストで、リスト外のピアには /metrics/config、プラグインパスに対して 403 を返しつつ、ヘルスプローブは到達可能なまま保ちます。ループバックは暗黙的に含まれないため、localhost からのアクセスがまだ必要な場合は 127.0.0.1/32 を含めてください。許可リストを設定せずにリスナーがホスト外に公開されている場合、サーバーは起動時に警告します
  • Kubernetes の Service として公開しない — ポートを containerPort として宣言しても、そのための Service は作成しないでください。Kubernetes のプローブはコンテナのポートに直接アクセスします
  • ネットワークポリシーを使用する — ポートを公開しなければならない場合は、ネットワーク層でアクセスを制限してください
Warning

/config エンドポイントは運用上の詳細(ドキュメントルート、レート制限、ワーカー数、タイムアウト値)を明らかにします。TLS のパス、internal_addrerror_pages_dir は除去されますが、残りの情報を Pod の外部からアクセス可能にすべきかどうかを検討してください。

Docker の例

手軽に確認するには内部ポートを公開できます。本番環境では、内部サーバーを localhost にバインドし、Kubernetes のプローブを使用してください。

compose.yaml
services: app: image: ghcr.io/oxphp/oxphp:0.10.0 ports: - "80:80" - "9090:9090" environment: - DOCUMENT_ROOT=/var/www/html/public - INTERNAL_ADDR=0.0.0.0:9090

トラブルシューティング

ヘルス、メトリクス、設定の各エンドポイントが利用できない

INTERNAL_ADDR が設定されていません。

対処: 環境変数を追加します。

bash
INTERNAL_ADDR=0.0.0.0:9090
/health が 503 を返すがアプリケーションは動作している

ロードされたプラグインが PluginHealth::Failed を報告しています。どのプラグインが失敗しているかを特定するには、ヘルスレスポンスの plugins オブジェクトを確認してください。

bash
curl -s http://localhost:9090/health | jq '.plugins'
コンテナの外部から内部サーバーに到達できない

サーバーは 127.0.0.1 にバインドされており、これはコンテナ内部からのみアクセス可能です。

対処: 外部アクセス用に 0.0.0.0 に変更するか、コンテナに直接アクセスする Kubernetes のプローブを使用してください。

メトリクスにワーカーモードや非同期メトリクスが表示されない

ワーカーモードのメトリクスは、ワーカーモードが有効(WORKER_MODE_ENABLED=true)な場合にのみ表示されます。非同期メトリクスは、ASYNC_WORKERS > 0 かつ少なくとも 1 つのタスクがディスパッチまたは拒否された場合にのみ表示されます。

関連項目