0Pricing
PHP Academy · レッスン

APIゲートウェイとサービスディスカバリ

サービスを動的にルーティング、集約、検出します。

「APIゲートウェイとサービスディスカバリ」はCoddyKit上の無料PHP Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPHP Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 PHP Academyコースには全4レッスンが含まれています。

ゲートウェイとサービスディスカバリ

サービスが十数個になると、2つの問題が現れます。クライアントが各サービスのアドレスを知ったり、1画面を表示するために5つのサービスを呼び出したりする必要はありません。それがAPIゲートウェイの役割です。また、スケールアップやスケールダウンによってIPアドレスが変わるサービス同士には、お互いを見つける方法が必要です。それがサービスディスカバリです。

このレッスンでは、ゲートウェイの入口部分をPHPで実装しながら、この2つを扱います。

APIゲートウェイの役割

APIゲートウェイは、サービス群の前に置かれる単一の入り口です。一般的な役割は次のとおりです。

  • ルーティング:リクエストを適切なバックエンドへ振り分けます。
  • 横断的関心事:認証、レート制限、CORS、TLS終端を扱います。
  • 集約:複数のバックエンド呼び出しを1つのクライアント向けレスポンスにまとめます。
  • プロトコル変換:外部からRESTを受け取り、内部へgRPCで送ります。

これによりクライアントはシンプルになり、本来なら各サービスで重複するポリシーを一元管理できます。

エッジでのルーティング

ゲートウェイの基本は、受信したパスを上流サービスに対応付けることです。プロダクション環境のゲートウェイ(Kong、Traefik、Nginx、AWS API Gateway)は宣言的にこれを行いますが、そのロジックはPHPで説明できるほどシンプルです。

<?php
$routes = [
    '#^/api/orders#'    => 'http://orders-svc',
    '#^/api/customers#' => 'http://customers-svc',
    '#^/api/catalog#'   => 'http://catalog-svc',
];

function resolveUpstream(string $path, array $routes): ?string {
    foreach ($routes as $pattern => $upstream) {
        if (preg_match($pattern, $path)) {
            return $upstream . $path;
        }
    }
    return null; // 404 at the gateway
}

echo resolveUpstream('/api/orders/42', $routes), "\n";

認証の一元化

ゲートウェイで呼び出し元を一度だけ検証し、信頼できる識別情報を下流へ転送すれば、各サービスが元のトークンを再検証する必要はありません。ゲートウェイはJWTの署名と有効期限を確認し、X-User-Idのようなヘッダーを内部リクエストに追加します(信頼できるネットワーク上で行います)。

<?php
function authenticate(string $authHeader): ?array {
    if (!str_starts_with($authHeader, 'Bearer ')) return null;
    $jwt = substr($authHeader, 7);
    $claims = verifyJwt($jwt);            // signature + exp check
    if ($claims === null) return null;
    // Forward minimal trusted identity to internal services
    return ['X-User-Id' => $claims['sub'], 'X-Scopes' => implode(',', $claims['scopes'])];
}
function verifyJwt(string $j): ?array { return ['sub' => 'u-7', 'scopes' => ['orders:read']]; }
print_r(authenticate('Bearer abc.def.ghi'));

レスポンスの集約

モバイル画面では、注文、顧客、カタログのデータが必要になる場合があります。クライアントから3回呼び出す代わりに、ゲートウェイが各サービスへ並行して呼び出し、結果を待って結合します。これを高速に保つには、上流への呼び出しを順番に行うのではなく、同時実行してください(Guzzleのpromiseやcurl_multiを使います)。

<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Promise\Utils;

$http = new Client(['timeout' => 2.0]);
$promises = [
    'order'    => $http->getAsync('http://orders-svc/orders/42'),
    'customer' => $http->getAsync('http://customers-svc/customers/7'),
    'catalog'  => $http->getAsync('http://catalog-svc/items?order=42'),
];
$results = Utils::settle($promises)->wait(); // run in parallel
// merge the fulfilled bodies into one response for the client

Backend for Frontend

汎用的なゲートウェイ1つで、Webアプリ、モバイルアプリ、パートナーに同じように対応するのは難しいことがあります。それぞれが異なる集約方法やペイロード形式を求めるためです。Backend-for-Frontend(BFF)パターンでは、クライアントの種類ごとに、そのニーズに合わせた薄いゲートウェイを用意し、共有サービスは汎用的なままに保ちます。

これにより、肥大化した万能ゲートウェイを避け、各クライアントチームが独立して開発を進められます。

サービスディスカバリの課題

動的な環境では、インスタンスが増減し、IPアドレスも変わります。http://10.0.3.14:8080のようにハードコードする方法は壊れやすいものです。サービスディスカバリは「現在、サービスXの正常なインスタンスがどれか」という情報を最新の状態でレジストリに保持します。これにより、呼び出し元は呼び出し時に論理名を実際のアドレスへ解決できます。

クライアントサイドとサーバーサイドのディスカバリ

方式は2つあります。

  • クライアントサイド:呼び出し元がレジストリ(Consul、etcd)に問い合わせ、自分でインスタンスを選び、ロードバランシングも行います。
  • サーバーサイド:呼び出し元は安定した仮想アドレス(ロードバランサーやKubernetes Service)にアクセスし、そのアドレスが解決と負荷分散を行います。

Kubernetesでは通常、サーバーサイドディスカバリを標準で利用できます。http://customers-svcを呼び出せば、クラスタのDNSとServiceが後の処理を担います。k8sの外部では、Consulのようなレジストリがよく使われます。

レジストリへの問い合わせ

クライアントサイドディスカバリでは、PHPの呼び出し元がレジストリに正常なインスタンスを問い合わせ、その中から1つを選びます。レジストリはヘルスチェックに合格したインスタンスだけを返すため、停止したノードは自動的に除外されます。

<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;

function discover(Client $http, string $service): string {
    // Consul: only passing-health instances
    $res = $http->get("http://consul:8500/v1/health/service/$service?passing=true");
    $nodes = json_decode((string) $res->getBody(), true);
    if (!$nodes) throw new \RuntimeException("No healthy $service");
    $pick = $nodes[array_rand($nodes)]['Service']; // simple LB
    return "http://{$pick['Address']}:{$pick['Port']}";
}

ヘルスチェックと登録

ディスカバリの品質は、ヘルスデータの品質に左右されます。各サービスは、実際の依存先(DB、キャッシュ)を確認する/healthエンドポイントを公開し、起動時に自分自身を登録します(またはプラットフォームに登録してもらいます)。レジストリはそのエンドポイントを検査し、失敗するインスタンスを除外します。

ヘルスチェックには実態を反映させてください。データベースが停止しているのに200を返すのは、役に立たないどころか有害です。壊れたノードにトラフィックを送り込むことになるためです。

<?php
// GET /health
function health(PDO $db, Redis $cache): array {
    $checks = [
        'db'    => safe(fn() => $db->query('SELECT 1') !== false),
        'cache' => safe(fn() => $cache->ping() === '+PONG'),
    ];
    $ok = !in_array(false, $checks, true);
    http_response_code($ok ? 200 : 503);
    return ['status' => $ok ? 'pass' : 'fail', 'checks' => $checks];
}
function safe(callable $c): bool { try { return (bool) $c(); } catch (\Throwable) { return false; } }

LivenessとReadiness

ヘルスエンドポイントは1つだけでは不十分です。次の2つの問いを区別してください。

  • Liveness:「プロセスは生きているか」。失敗すると、オーケストレーターがコンテナを再起動します。DBの一時的な不調で無意味な再起動が起きないよう、軽量で依存関係のないチェックにしてください。
  • Readiness:「今すぐトラフィックを処理できるか」。失敗すると、トラフィックは送られなくなりますが、プロセスは動き続けます(キャッシュを温めている場合や、DBに一時的に接続できない場合など)。

この2つを混同すると、再起動ループが発生したり、まだ準備できていないノードへルーティングしたりします。

<?php
// GET /livez  - is the process itself healthy? (no external deps)
function livez(): void { http_response_code(200); echo 'alive'; }

// GET /readyz - should we receive traffic? (checks dependencies)
function readyz(PDO $db): void {
    try { $db->query('SELECT 1'); http_response_code(200); echo 'ready'; }
    catch (\Throwable) { http_response_code(503); echo 'not ready'; }
}

クイックチェック

クライアントとサーバー間の往復回数を減らすこと。

まとめ

サービスをルーティングし、見つける方法:

  • APIゲートウェイは、ルーティング、認証、レート制限、TLS、集約を一元管理します。
  • 同時実行で集約し、クライアントごとのニーズが異なる場合はBFFを使います。
  • サービスディスカバリは、論理名を稼働中の正常なインスタンスへ解決します。
  • クライアントサイド(レジストリに問い合わせる方式)とサーバーサイド(安定したLBやk8s Serviceを使う方式)のディスカバリがあります。
  • 意味のあるヘルスチェックによって、壊れたノードにトラフィックが送られないようにします。

次は、避けられない部分障害が起きても、これらの呼び出しをすべて回復力のあるものにする方法を扱います。

よくある質問

「APIゲートウェイとサービスディスカバリ」レッスンは無料ですか?

はい。「APIゲートウェイとサービスディスカバリ」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、PHP Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 PHP Academyコースには全4レッスンが含まれています。

「APIゲートウェイとサービスディスカバリ」で何を学びますか?

サービスを動的にルーティング、集約、検出します。 ブラウザで直接実行するハンズオンコードでPHP Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

PHP Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのPHP Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「APIゲートウェイとサービスディスカバリ」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このPHP Academyレッスンでコードを書いて実行できますか?

はい。すべてのPHP Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. モノリスからマイクロサービスへ
  2. サービス間通信:RESTとgRPC
  3. APIゲートウェイとサービスディスカバリ
  4. レジリエンス:サーキットブレーカーとリトライ
← PHP Academyに戻る