Skip to content
Published on

API Gatewayパターン完全ガイド:Rate Limiting、認証/認可、BFFアーキテクチャ設計

Authors
  • Name
    Twitter
API Gateway Pattern

はじめに

マイクロサービスアーキテクチャが普及するにつれ、クライアントが数十、数百のサービスと直接通信することは現実的に不可能になった。API Gatewayはクライアントとバックエンドサービスの間に位置する単一のエントリーポイントとして、ルーティング、認証/認可、Rate Limiting、ロードバランシング、プロトコル変換などの横断的関心事(cross-cutting concerns)を一元的に処理する。

本記事では、API Gatewayパターンの核心概念から始めて、Rate Limitingアルゴリズム(Token Bucket、Sliding Window、Fixed Window、Leaky Bucket)の動作原理と比較、JWT/OAuth2ベースの認証/認可戦略、BFF(Backend for Frontend)アーキテクチャ設計、ロードバランシングとサーキットブレーカー構成、そしてKongとApache APISIXベースのプロダクション実装例を扱う。最後に、プロダクション環境で実際に遭遇しうる障害シナリオと運用チェックリストを整理する。

API Gatewayパターン概要

API Gatewayの役割

API Gatewayは以下の横断的関心事を一元的に処理する。

  • ルーティング:リクエストURL、ヘッダー、メソッドに基づいて適切なバックエンドサービスに転送
  • 認証/認可:JWT検証、OAuth2トークン有効性検査、APIキー管理
  • Rate Limiting:クライアント別、API別リクエスト速度制限
  • ロードバランシング:ラウンドロビン、加重、最小接続などのアルゴリズムでトラフィック分散
  • サーキットブレーカー:障害サービスへのリクエストを自動遮断して連鎖障害防止
  • プロトコル変換:REST to gRPC、HTTP to WebSocketなど
  • キャッシング:レスポンスキャッシングによるパフォーマンス向上
  • モニタリング:メトリクス収集、分散トレーシング、ロギング

API Gatewayソリューション比較

項目KongApache APISIXAWS API GatewayEnvoy
基盤技術NGINX + LuaNGINX + etcdAWSマネージドC++
性能 (QPS)約10,000+約23,000+マネージド(制限あり)約15,000+
プラグイン非常に豊富 (100+)豊富 (80+)限定的豊富(フィルターチェーン)
設定ストアPostgreSQL / CassandraetcdAWS内部xDS API
動的設定変更Admin APIAdmin API + etcd Watchコンソール/CLIxDSホットリロード
サービスメッシュKong Mesh (Kuma)Amesh (Istio連携)App Mesh連携Istioデフォルトプロキシ
KubernetesネイティブKong Ingress ControllerAPISIX Ingress Controllerなし(EKS連携)Gateway APIサポート
ライセンスApache 2.0 / EnterpriseApache 2.0従量課金Apache 2.0
適合環境汎用、エンタープライズ高性能、動的ルーティングAWSネイティブK8s、サービスメッシュ

API Gateway vs サービスメッシュ

API Gatewayとサービスメッシュは補完的な関係である。

区分API Gatewayサービスメッシュ
位置クライアントとサービスの間(南北トラフィック)サービスとサービスの間(東西トラフィック)
主な役割外部リクエストルーティング、認証、Rate Limitingサービス間mTLS、トラフィック管理、オブザーバビリティ
デプロイ方式集中型(ゲートウェイクラスター)分散型(サイドカープロキシ)
プロトコルHTTP、gRPC、WebSocketTCP、HTTP、gRPC
代表ソリューションKong、APISIX、AWS API GWIstio、Linkerd、Consul Connect

Rate Limitingアルゴリズム

Rate LimitingはAPI Gatewayの最も重要な機能の一つである。サービスの過負荷防止、DDoS防御、公平なリソース分配のために不可欠である。

アルゴリズム比較

アルゴリズム原理バースト許容メモリ使用量精度実装複雑度
Fixed Window固定時間ウィンドウ内カウンター境界で2倍可能低い低い非常に低い
Sliding Window Log各リクエストのタイムスタンプ記録なし高い高い中間
Sliding Window Counter前/現在ウィンドウの加重平均最小化低い中間中間
Token Bucket一定速度でトークン補充、リクエスト時消費許容(バケットサイズまで)低い中間低い
Leaky Bucket固定速度でリクエスト処理、超過分キューイングなし(固定速度)低い高い低い

Token Bucketアルゴリズム

Token Bucketはバーストトラフィックを許容しながら平均リクエストレートを制限する最も実用的なアルゴリズムである。

# Kong - Rate Limitingプラグイン設定(Token Bucketベース)
# kong.yml - Declarative Configuration
_format_version: '3.0'

services:
  - name: user-service
    url: http://user-service:8080
    routes:
      - name: user-route
        paths:
          - /api/v1/users
    plugins:
      - name: rate-limiting
        config:
          # 毎分100回、毎時1000回制限
          minute: 100
          hour: 1000
          # ポリシー: local(単一ノード)、cluster(クラスター全体)、redis(Redisベース)
          policy: redis
          redis:
            host: redis-cluster
            port: 6379
            password: null
            database: 0
            timeout: 2000
          # Rate Limitヘッダー返却
          header_name: null
          hide_client_headers: false
          # 制限基準: consumer, credential, ip, header, path, service
          limit_by: consumer
          # Redis障害時リクエスト許可
          fault_tolerant: true

Sliding Windowアルゴリズム

Sliding Window CounterはFixed Windowの境界問題を解決しながらメモリ効率が良い。

-- APISIX カスタムRate Limitingプラグイン(Sliding Window Counter)
-- apisix/plugins/sliding-window-rate-limit.lua

local core = require("apisix.core")
local ngx = ngx
local math = math

local schema = {
    type = "object",
    properties = {
        rate = { type = "integer", minimum = 1 },
        burst = { type = "integer", minimum = 0 },
        window_size = { type = "integer", minimum = 1, default = 60 },
        key_type = {
            type = "string",
            enum = { "remote_addr", "consumer_name", "header" },
            default = "remote_addr"
        },
    },
    required = { "rate" },
}

local _M = {
    version = 0.1,
    priority = 1001,
    name = "sliding-window-rate-limit",
    schema = schema,
}

function _M.access(conf, ctx)
    local key = ctx.var.remote_addr
    if conf.key_type == "consumer_name" then
        key = ctx.consumer_name or ctx.var.remote_addr
    end

    local now = ngx.now()
    local window = conf.window_size
    local current_window = math.floor(now / window) * window
    local previous_window = current_window - window
    local elapsed = now - current_window

    -- 前ウィンドウと現在ウィンドウの加重平均計算
    local prev_count = get_count(key, previous_window) or 0
    local curr_count = get_count(key, current_window) or 0
    local weight = (window - elapsed) / window
    local estimated = prev_count * weight + curr_count

    if estimated >= conf.rate then
        return 429, {
            error = "Rate limit exceeded",
            retry_after = math.ceil(window - elapsed)
        }
    end

    increment_count(key, current_window)
end

return _M

認証/認可戦略

API Gatewayでの認証/認可はバックエンドサービスのセキュリティ負担を大幅に軽減する。

JWT認証設定

# APISIX - JWT認証プラグイン設定
# apisix/conf/config.yaml
routes:
  - uri: /api/v1/orders/*
    upstream:
      type: roundrobin
      nodes:
        'order-service:8080': 1
    plugins:
      jwt-auth:
        # JWT署名検証用の公開鍵
        key: 'user-auth-key'
        # トークン位置設定
        header: 'Authorization'
        query: 'token'
        cookie: 'jwt_token'
      # 追加:権限ベースアクセス制御
      consumer-restriction:
        type: consumer_group_id
        whitelist:
          - 'premium-users'
          - 'admin-group'
        rejected_code: 403
        rejected_msg: 'Access denied: insufficient permissions'

# Consumer設定(APIユーザー定義)
consumers:
  - username: 'mobile-app'
    plugins:
      jwt-auth:
        key: 'mobile-app-key'
        secret: 'mobile-app-secret-256bit-key-here'
        algorithm: 'HS256'
        exp: 86400 # トークン有効期限:24時間
        base64_secret: false
  - username: 'web-frontend'
    plugins:
      jwt-auth:
        key: 'web-frontend-key'
        # RS256使用時の公開鍵パス
        public_key: |
          -----BEGIN PUBLIC KEY-----
          MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
          -----END PUBLIC KEY-----
        algorithm: 'RS256'
        exp: 3600 # トークン有効期限:1時間

OAuth2 + OIDC統合認証フロー

API GatewayでOAuth2/OIDCを統合すると、IdP(Identity Provider)との連携を一元化できる。

# Kong - OpenID Connectプラグイン設定
plugins:
  - name: openid-connect
    config:
      issuer: 'https://auth.example.com/realms/production'
      client_id: 'api-gateway'
      client_secret: 'gateway-secret-value'
      redirect_uri: 'https://api.example.com/callback'
      # サポート認証フロー
      auth_methods:
        - authorization_code # Webアプリケーション
        - client_credentials # サービス間通信
        - password # レガシーサポート(非推奨)
      # トークン検証設定
      token_endpoint_auth_method: client_secret_post
      # スコープベースアクセス制御
      scopes_required:
        - openid
        - profile
        - api:read
      # トークンキャッシング(パフォーマンス最適化)
      cache_ttl: 300
      # トークンイントロスペクション(不透明トークン検証)
      introspection_endpoint: 'https://auth.example.com/realms/production/protocol/openid-connect/token/introspect'
      # アップストリームに転送するヘッダー
      upstream_headers_claims:
        - sub
        - email
        - realm_access.roles
      upstream_headers_names:
        - X-User-ID
        - X-User-Email
        - X-User-Roles

BFF(Backend for Frontend)アーキテクチャ

BFFパターンが必要な理由

単一のAPI Gatewayで全クライアント(Web、モバイル、IoTなど)にサービスを提供すると、以下の問題が発生する。

  • 過剰なデータ転送:モバイルクライアントにWeb用の全データが送信される
  • 複雑なゲートウェイロジック:クライアント別の分岐ロジックがゲートウェイに蓄積される
  • デプロイ結合:一つのクライアントのための変更が他のクライアントに影響

BFFパターンは各フロントエンドに最適化された専用バックエンドを提供してこれらの問題を解決する。

BFFルーティング構成

# APISIX - BFFルーティング設定
# クライアントタイプ別の専用BFFにルーティング
routes:
  # Web BFF - 豊富なデータ、詳細情報含む
  - uri: /api/web/*
    name: web-bff-route
    plugins:
      proxy-rewrite:
        regex_uri:
          - '^/api/web/(.*)'
          - '/$1'
      request-id:
        header_name: X-Request-ID
      jwt-auth: {}
      rate-limiting:
        rate: 200
        burst: 50
        key_type: consumer_name
    upstream:
      type: roundrobin
      nodes:
        'web-bff:3000': 1
      timeout:
        connect: 3
        send: 10
        read: 30

  # モバイルBFF - 軽量データ、ページネーション最適化
  - uri: /api/mobile/*
    name: mobile-bff-route
    plugins:
      proxy-rewrite:
        regex_uri:
          - '^/api/mobile/(.*)'
          - '/$1'
      jwt-auth: {}
      rate-limiting:
        rate: 100
        burst: 20
        key_type: consumer_name
      # モバイル専用:レスポンスサイズ制御
      response-rewrite:
        headers:
          set:
            X-Content-Optimized: 'mobile'
    upstream:
      type: roundrobin
      nodes:
        'mobile-bff:3001': 1
      timeout:
        connect: 3
        send: 5
        read: 15

  # IoT BFF - 最小データ、高頻度
  - uri: /api/iot/*
    name: iot-bff-route
    plugins:
      proxy-rewrite:
        regex_uri:
          - '^/api/iot/(.*)'
          - '/$1'
      key-auth: {} # IoTデバイスはAPIキー認証
      rate-limiting:
        rate: 500
        burst: 100
        key_type: var
        key: remote_addr
    upstream:
      type: roundrobin
      nodes:
        'iot-bff:3002': 1
      timeout:
        connect: 2
        send: 3
        read: 5

BFFアーキテクチャ構造

クライアント層           API Gateway          BFF層            マイクロサービス
+----------+                              +----------+
| Web App  | ----+                   +--> | Web BFF  | --+--> User Service
+----------+     |    +-----------+  |    +----------+   +--> Product Service
                 +--> |           |--+                   +--> Order Service
+----------+     |    | API       |  |    +----------+
|Mobile App| ----+--> | Gateway   |--+--> |Mobile BFF| --+--> User Service
+----------+     |    |           |  |    +----------+   +--> Product Service
                 |    +-----------+  |
+----------+     |                   |    +----------+
|IoT Device| ----+                   +--> | IoT BFF  | --+--> Device Service
+----------+                              +----------+   +--> Telemetry Service

ロードバランシングとサーキットブレーカー

ロードバランシング戦略

API Gatewayは多様なロードバランシングアルゴリズムをサポートする。

# APISIX - ロードバランシング戦略
upstreams:
  # 加重ラウンドロビン
  - id: 1
    type: roundrobin
    nodes:
      'service-a-v1:8080': 8 # 80%トラフィック
      'service-a-v2:8080': 2 # 20%トラフィック(カナリアデプロイ)
    # ヘルスチェック設定
    checks:
      active:
        type: http
        http_path: /health
        healthy:
          interval: 5
          successes: 2
        unhealthy:
          interval: 3
          http_failures: 3
          tcp_failures: 3
      passive:
        healthy:
          http_statuses:
            - 200
            - 201
          successes: 3
        unhealthy:
          http_statuses:
            - 500
            - 502
            - 503
          http_failures: 5
          tcp_failures: 2

  # コンシステントハッシュ(セッションアフィニティ)
  - id: 2
    type: chash
    key: remote_addr
    nodes:
      'session-service-1:8080': 1
      'session-service-2:8080': 1
      'session-service-3:8080': 1

  # 最小接続
  - id: 3
    type: least_conn
    nodes:
      'compute-service-1:8080': 1
      'compute-service-2:8080': 1

サーキットブレーカー設定

# APISIX - api-breakerプラグイン(自動サーキットブレーカー)
routes:
  - uri: /api/v1/payments/*
    plugins:
      api-breaker:
        # サーキットブレーカートリガーステータスコード
        break_response_code: 503
        break_response_body: '{"error":"circuit open","retry_after":30}'
        break_response_headers:
          - key: Content-Type
            value: application/json
          - key: Retry-After
            value: '30'
        # unhealthy判定:連続3回500エラーでサーキットオープン
        unhealthy:
          http_statuses:
            - 500
            - 502
            - 503
          failures: 3
        # healthy判定:連続2回成功でサーキットクローズ
        healthy:
          http_statuses:
            - 200
            - 201
          successes: 2
        # サーキットオープン後の最大待機時間(秒)
        max_breaker_sec: 300
    upstream:
      type: roundrobin
      nodes:
        'payment-service:8080': 1

Kongベースプロダクション実装

Docker ComposeベースKongクラスター構成

# docker-compose.kong.yml
version: '3.8'

services:
  kong-database:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: kong
      POSTGRES_USER: kong
      POSTGRES_PASSWORD: kong_password
    volumes:
      - kong_pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ['CMD', 'pg_isready', '-U', 'kong']
      interval: 10s
      timeout: 5s
      retries: 5

  kong-migration:
    image: kong:3.6
    command: kong migrations bootstrap
    depends_on:
      kong-database:
        condition: service_healthy
    environment:
      KONG_DATABASE: postgres
      KONG_PG_HOST: kong-database
      KONG_PG_USER: kong
      KONG_PG_PASSWORD: kong_password

  kong:
    image: kong:3.6
    depends_on:
      kong-migration:
        condition: service_completed_successfully
    environment:
      KONG_DATABASE: postgres
      KONG_PG_HOST: kong-database
      KONG_PG_USER: kong
      KONG_PG_PASSWORD: kong_password
      KONG_PROXY_ACCESS_LOG: /dev/stdout
      KONG_ADMIN_ACCESS_LOG: /dev/stdout
      KONG_PROXY_ERROR_LOG: /dev/stderr
      KONG_ADMIN_ERROR_LOG: /dev/stderr
      KONG_ADMIN_LISTEN: '0.0.0.0:8001'
      KONG_STATUS_LISTEN: '0.0.0.0:8100'
      # パフォーマンスチューニング
      KONG_NGINX_WORKER_PROCESSES: auto
      KONG_UPSTREAM_KEEPALIVE_POOL_SIZE: 128
      KONG_UPSTREAM_KEEPALIVE_MAX_REQUESTS: 1000
    ports:
      - '8000:8000' # プロキシ(HTTP)
      - '8443:8443' # プロキシ(HTTPS)
      - '8001:8001' # Admin API
    healthcheck:
      test: ['CMD', 'kong', 'health']
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  kong_pgdata:

APISIXベースプロダクション実装

APISIX HelmベースKubernetesデプロイ

# APISIX Kubernetesデプロイ(Helm)
helm repo add apisix https://charts.apiseven.com
helm repo update

# APISIXインストール(etcd含む)
helm install apisix apisix/apisix \
  --namespace apisix \
  --create-namespace \
  --set gateway.type=LoadBalancer \
  --set ingress-controller.enabled=true \
  --set dashboard.enabled=true \
  --set etcd.replicaCount=3 \
  --set etcd.persistence.size=20Gi \
  --set apisix.nginx.workerProcesses=auto \
  --set apisix.nginx.workerConnections=65536

# APISIX状態確認
kubectl -n apisix get pods
kubectl -n apisix get svc

# Admin APIによるルート登録
curl -X PUT http://apisix-admin:9180/apisix/admin/routes/1 \
  -H "X-API-KEY: admin-api-key" \
  -d '{
    "uri": "/api/v1/products/*",
    "upstream": {
      "type": "roundrobin",
      "nodes": {
        "product-service.default.svc:8080": 1
      }
    },
    "plugins": {
      "jwt-auth": {},
      "limit-count": {
        "count": 200,
        "time_window": 60,
        "rejected_code": 429,
        "rejected_msg": "Rate limit exceeded. Please retry later.",
        "policy": "redis",
        "redis_host": "redis.default.svc",
        "redis_port": 6379,
        "key_type": "var",
        "key": "consumer_name"
      },
      "api-breaker": {
        "break_response_code": 503,
        "unhealthy": {
          "http_statuses": [500, 502, 503],
          "failures": 3
        },
        "healthy": {
          "http_statuses": [200],
          "successes": 2
        },
        "max_breaker_sec": 60
      }
    }
  }'

モニタリングと運用

Prometheus + Grafanaメトリクス収集

API Gatewayの核心モニタリングメトリクスは以下の通りである。

  • リクエストレート(Request Rate):毎秒処理リクエスト数
  • エラーレート(Error Rate):4xx/5xxレスポンス比率
  • レイテンシ(Latency):P50、P95、P99レスポンス時間
  • Rate Limitヒット率:制限に到達したリクエスト比率
  • サーキットブレーカー状態:Open/Closed/Half-Open遷移イベント
  • アップストリームヘルス:バックエンドサービス可用性
# APISIX - Prometheusメトリクス収集設定
plugin_attr:
  prometheus:
    export_uri: /apisix/prometheus/metrics
    export_addr:
      ip: '0.0.0.0'
      port: 9091
    default_buckets:
      - 0.005
      - 0.01
      - 0.025
      - 0.05
      - 0.1
      - 0.25
      - 0.5
      - 1
      - 2.5
      - 5
      - 10

# グローバルプラグインで全ルートに適用
global_rules:
  - id: 1
    plugins:
      prometheus:
        prefer_name: true
      # 分散トレーシング(OpenTelemetry)
      opentelemetry:
        sampler:
          name: parent_based_traceidratio
          options:
            fraction: 0.1 # 10%サンプリング
        additional_attributes:
          - 'service.version'

核心アラートルール

# Prometheus Alert Rules
groups:
  - name: api-gateway-alerts
    rules:
      - alert: HighErrorRate
        expr: |
          sum(rate(apisix_http_status{code=~"5.."}[5m]))
          / sum(rate(apisix_http_status[5m])) > 0.05
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: 'API Gateway 5xxエラー率5%超過'

      - alert: HighLatency
        expr: |
          histogram_quantile(0.99,
            sum(rate(apisix_http_latency_bucket[5m])) by (le, route)
          ) > 2000
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: 'API Gateway P99レイテンシ2秒超過'

      - alert: RateLimitExceeded
        expr: |
          sum(rate(apisix_http_status{code="429"}[5m])) > 100
        for: 1m
        labels:
          severity: warning
        annotations:
          summary: 'Rate Limit超過リクエスト毎分100件以上'

障害事例と対応

事例1:Rate Limiter設定ミスによるサービス障害

あるフィンテック企業でRate Limiterをlocalポリシーで設定したままAPI Gatewayを3台にスケールアウトした。各ノードが独立的にRate Limitを適用したため、実際には設定値の3倍のトラフィックがバックエンドに転送され、決済サービスが過負荷でダウンした。

対応:分散環境では必ずredisまたはclusterポリシーを使用する必要がある。Redis ClusterをRate Limitストアとして使用すれば、ノード数に関係なく一貫した制限を適用できる。

事例2:API Gateway単一障害点(Single Point of Failure)

API Gatewayが単一インスタンスで運用されていた中、メモリリークによりOOM(Out of Memory)が発生し、サービス全体が停止した。

対応:API Gatewayは必ずHA(High Availability)構成で運用する必要がある。最低2台以上のインスタンスをActive-Activeでデプロイし、L4ロードバランサー(AWS NLB、MetalLB)を前段に配置する。ヘルスチェックで障害ノードを自動的に除去する。

事例3:認証トークンキャッシングによる権限エスカレーション

JWTトークンをAPI Gatewayで5分間キャッシングするように設定したが、ユーザーの権限が変更されたりアカウントが無効化された後もキャッシュされたトークンでアクセスが可能だった。

対応:トークンキャッシュTTLを短く維持し(30秒~1分)、重要な権限変更時にトークンブラックリストを使用する。Gatewayでexpクレームを必ず検証し、トークンリボケーションエンドポイントを実装する。

事例4:サーキットブレーカー未設定による連鎖障害

外部決済APIのレスポンス遅延が60秒以上に増加したが、サーキットブレーカーが設定されていなかったため、API Gatewayの全ワーカープロセスが決済サービス待機で占有された。その結果、正常な他のAPIもレスポンスできなくなった。

対応:全アップストリームに適切なタイムアウトとサーキットブレーカーを設定する。接続タイムアウトは3秒、読み取りタイムアウトはAPI特性に応じて530秒に制限する。連続35回失敗時にサーキットをオープンし、30~60秒後にHalf-Open状態に遷移して段階的に復旧する。

運用チェックリスト

プロダクション環境でAPI Gatewayを運用する際に確認すべき核心項目である。

デプロイと可用性

  • HA構成(最低2台以上、Active-Active)
  • L4ロードバランサー前段配置(AWS NLB、MetalLBなど)
  • ローリングアップデートまたはブルーグリーンデプロイ戦略
  • 設定ストアバックアップ(PostgreSQL、etcd)

セキュリティ

  • Admin APIアクセス制限(内部ネットワークのみ許可)
  • TLS 1.3適用および証明書自動更新
  • JWTトークン検証有効化およびキャッシュTTL最小化
  • CORS、CSRF保護設定

Rate Limiting

  • 分散ポリシー使用(redisまたはcluster)
  • クライアントタイプ別差等制限設定
  • Rate Limitヘッダー返却(X-RateLimit-Limit、X-RateLimit-Remaining)
  • Redis障害時fault_tolerant設定

モニタリング

  • Prometheusメトリクス収集有効化
  • P99レイテンシ、エラーレート、Rate Limitヒット率ダッシュボード
  • サーキットブレーカー状態変更アラート
  • 分散トレーシング(OpenTelemetry)連携

パフォーマンス

  • ワーカープロセス数最適化(CPUコア数基準)
  • アップストリームKeepalive接続プール設定
  • レスポンスキャッシング戦略適用
  • 不要なプラグイン無効化

参考資料