- Published on
API Gateway完全ガイド2025:Kong、Envoy、AWS API Gateway、認証/レートリミット/モニタリング
- Authors

- Name
- Youngju Kim
- @fjvbn20031
目次
1. なぜAPI Gatewayが必要(ひつよう)か
1.1 マイクロサービスの横断的関心事(おうだんてきかんしんじ)
マイクロサービスアーキテクチャでは、数十から数百のサービスが独立して運用されます。各サービスで認証、ロギング、レートリミットを個別に実装すると、重複と不整合が発生します。
API Gatewayのない世界:
[クライアント]
│ │ │
│ │ └──── [サービスA] (独自認証、独自ロギング、独自レートリミット)
│ └─────── [サービスB] (独自認証、独自ロギング、独自レートリミット)
└────────── [サービスC] (独自認証、独自ロギング、独自レートリミット)
↑
重複!不整合!管理不能!
API Gatewayのある世界:
[クライアント]
│
┌────┴─────────────────────────┐
│ API Gateway │
│ ┌─────────────────────────┐ │
│ │ 認証 / 認可 │ │
│ │ レートリミット │ │
│ │ リクエスト変換 │ │
│ │ キャッシング │ │
│ │ ロギング / モニタリング │ │
│ │ サーキットブレーカー │ │
│ │ ロードバランシング │ │
│ └─────────────────────────┘ │
└──────┬───────┬───────┬───────┘
│ │ │
[SvcA] [SvcB] [SvcC]
(ビジネス (ビジネス (ビジネス
ロジック) ロジック) ロジック)
1.2 API Gatewayパターン
3つのコアGatewayパターン:
1. Routingパターン(ルーティング)
クライアントリクエストを正しいバックエンドサービスに転送
/api/users → User Service
/api/orders → Order Service
/api/products → Product Service
2. Aggregationパターン(集約)
複数サービスのレスポンスを1つにまとめて返却
GET /api/dashboard →
User Service (プロフィール) +
Order Service (最近の注文) +
Notification Service (通知)
→ 単一JSONレスポンス
3. Offloadingパターン(オフロード)
共通機能をサービスからGatewayに移管
認証、SSL終了、圧縮、キャッシング、CORS等
→ サービスはビジネスロジックに専念
1.3 API Gatewayが処理(しょり)する機能
| 機能 | 説明 |
|---|---|
| ルーティング | URLパターン/ヘッダーに基づいてリクエストを正しいサービスに転送 |
| 認証/認可 | OAuth2、JWT、API Key検証 |
| レートリミット | API乱用防止。ユーザー/IP/エンドポイント別制限 |
| リクエスト/レスポンス変換 | ヘッダー追加/削除、ボディ変換、プロトコル変換 |
| キャッシング | レスポンスキャッシュでバックエンド負荷軽減 |
| ロードバランシング | トラフィックを複数インスタンスに分散 |
| サーキットブレーカー | 障害サービスの隔離。連鎖障害防止 |
| ロギング/モニタリング | アクセスログ、メトリクス収集、分散トレーシング |
| SSL/TLS終了 | HTTPSをGatewayで処理 |
| CORS | Cross-Originリクエストポリシー管理 |
2. Kong Deep Dive
2.1 Kongアーキテクチャ
Kong Architecture:
[クライアント]
│
┌────┴────────────────────────────────┐
│ Kong Gateway │
│ │
│ ┌──────────────────────────────┐ │
│ │ Kong Core (OpenResty) │ │
│ │ Nginx + LuaJIT │ │
│ └──────────┬───────────────────┘ │
│ │ │
│ ┌──────────┴───────────────────┐ │
│ │ Plugin Layer │ │
│ │ │ │
│ │ Auth │ Rate │ Logging │ ... │ │
│ │ │ Limit │ │ │ │
│ └──────────────────────────────┘ │
│ │ │
│ ┌──────────┴───────────────────┐ │
│ │ Data Store │ │
│ │ PostgreSQL │ Cassandra │ │
│ │ (またはDB-lessモード) │ │
│ └──────────────────────────────┘ │
└──────┬──────────┬──────────┬────────┘
│ │ │
[Service A] [Service B] [Service C]
2.2 Kong DB-lessモード(宣言的(せんげんてき)設定)
# kong.yml - DB-less宣言的設定
_format_version: "3.0"
_transform: true
services:
- name: user-service
url: http://user-service:8080
routes:
- name: user-routes
paths:
- /api/v1/users
methods:
- GET
- POST
- PUT
- DELETE
strip_path: false
plugins:
- name: rate-limiting
config:
minute: 100
hour: 5000
policy: local
- name: jwt
config:
uri_param_names:
- token
claims_to_verify:
- exp
- name: order-service
url: http://order-service:8080
routes:
- name: order-routes
paths:
- /api/v1/orders
strip_path: false
plugins:
- name: rate-limiting
config:
minute: 50
hour: 2000
- name: request-transformer
config:
add:
headers:
- "X-Request-Source:api-gateway"
- "X-Forwarded-Service:order-service"
- name: product-service
url: http://product-service:8080
routes:
- name: product-routes
paths:
- /api/v1/products
strip_path: false
plugins:
- name: proxy-cache
config:
response_code:
- 200
request_method:
- GET
content_type:
- "application/json"
cache_ttl: 300
strategy: memory
# グローバルプラグイン
plugins:
- name: correlation-id
config:
header_name: X-Request-ID
generator: uuid
echo_downstream: true
- name: prometheus
config:
per_consumer: true
2.3 Kong主要(しゅよう)プラグイン
Kongプラグインカテゴリ:
Authentication:
├── jwt - JWTトークン検証
├── oauth2 - 内蔵OAuth2サーバー
├── key-auth - API Key認証
├── basic-auth - Basic認証
├── hmac-auth - HMAC署名認証
├── ldap-auth - LDAP/AD認証
└── openid-connect - OIDC (Enterprise)
Security:
├── cors - Cross-Originポリシー
├── ip-restriction - IPホワイト/ブラックリスト
├── bot-detection - ボット検知
├── acl - Access Control Lists
└── mtls-auth - mTLS認証
Traffic Control:
├── rate-limiting - レートリミット
├── request-size-limiting - リクエストサイズ制限
├── request-termination - リクエストブロック/メンテナンスモード
└── proxy-cache - レスポンスキャッシング
Observability:
├── prometheus - Prometheusメトリクス
├── datadog - Datadog APM統合
├── zipkin - 分散トレーシング
├── file-log - ファイルロギング
└── opentelemetry - OTel統合
2.4 Kongカスタムプラグイン(Lua)
-- custom-auth-plugin/handler.lua
local BasePlugin = require "kong.plugins.base_plugin"
local CustomAuthHandler = BasePlugin:extend()
CustomAuthHandler.VERSION = "1.0.0"
CustomAuthHandler.PRIORITY = 1000 -- 実行順序
function CustomAuthHandler:new()
CustomAuthHandler.super.new(self, "custom-auth")
end
function CustomAuthHandler:access(conf)
CustomAuthHandler.super.access(self)
-- 1. API Key抽出
local api_key = kong.request.get_header("X-API-Key")
if not api_key then
return kong.response.exit(401, {
message = "Missing API Key"
})
end
-- 2. 外部認証サービス呼び出し
local httpc = require("resty.http").new()
local res, err = httpc:request_uri(conf.auth_service_url, {
method = "POST",
headers = {
["Content-Type"] = "application/json",
},
body = '{"api_key":"' .. api_key .. '"}',
timeout = conf.timeout or 5000,
})
if not res then
kong.log.err("Auth service error: ", err)
return kong.response.exit(503, {
message = "Auth service unavailable"
})
end
if res.status ~= 200 then
return kong.response.exit(403, {
message = "Invalid API Key"
})
end
-- 3. 認証情報をアップストリームヘッダーに追加
local cjson = require "cjson.safe"
local body = cjson.decode(res.body)
if body then
kong.service.request.set_header("X-Consumer-ID", body.consumer_id or "")
kong.service.request.set_header("X-Consumer-Plan", body.plan or "free")
end
end
return CustomAuthHandler
3. Envoy Proxy Deep Dive
3.1 Envoyアーキテクチャ
Envoy Core Architecture:
┌──────────────────────────────────────────────┐
│ Envoy Proxy │
│ │
│ Listener(ポートバインディング) │
│ │ │
│ ▼ │
│ Filter Chain(フィルターチェーン) │
│ ├── Network Filters (L3/L4) │
│ │ ├── TCP Proxy │
│ │ ├── TLS Inspector │
│ │ └── HTTP Connection Manager │
│ │ │
│ └── HTTP Filters (L7) │
│ ├── Router │
│ ├── CORS │
│ ├── JWT Auth │
│ ├── Rate Limit │
│ ├── WASM Filter(カスタム) │
│ └── ... │
│ │
│ Route Configuration │
│ │ │
│ ▼ │
│ Cluster(アップストリームサービスグループ) │
│ ├── Endpoint Discovery │
│ ├── Load Balancing │
│ ├── Health Checking │
│ ├── Circuit Breaking │
│ └── Outlier Detection │
└──────────────────────────────────────────────┘
3.2 Envoy静的(せいてき)設定
# envoy.yaml - 静的設定
static_resources:
listeners:
- name: main_listener
address:
socket_address:
address: 0.0.0.0
port_value: 8080
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
codec_type: AUTO
route_config:
name: local_route
virtual_hosts:
- name: api_service
domains: ["*"]
routes:
- match:
prefix: "/api/v1/users"
route:
cluster: user_service
timeout: 10s
retry_policy:
retry_on: "5xx,connect-failure"
num_retries: 3
per_try_timeout: 3s
- match:
prefix: "/api/v1/orders"
route:
cluster: order_service
timeout: 15s
- match:
prefix: "/api/v1/products"
route:
weighted_clusters:
clusters:
- name: product_service_v1
weight: 90
- name: product_service_v2
weight: 10
http_filters:
- name: envoy.filters.http.jwt_authn
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication
providers:
auth0:
issuer: "https://company.auth0.com/"
audiences:
- "https://api.company.com"
remote_jwks:
http_uri:
uri: "https://company.auth0.com/.well-known/jwks.json"
cluster: auth0_jwks
timeout: 5s
cache_duration: 600s
rules:
- match:
prefix: "/api/"
requires:
provider_name: "auth0"
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: user_service
connect_timeout: 5s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
health_checks:
- timeout: 3s
interval: 10s
unhealthy_threshold: 3
healthy_threshold: 2
http_health_check:
path: "/health"
circuit_breakers:
thresholds:
- priority: DEFAULT
max_connections: 1024
max_pending_requests: 1024
max_requests: 1024
max_retries: 3
load_assignment:
cluster_name: user_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: user-service
port_value: 8080
3.3 xDS API(動的設定(どうてきせってい))
xDS APIの種類:
┌──────────────────────────────────────┐
│ Control Plane │
│ (Istio Pilot / カスタムxDSサーバー) │
└──────────┬───────────────────────────┘
│
┌────────┴──────────────────┐
│ │
▼ ▼
[Envoy A] [Envoy B]
xDS種類:
├── LDS (Listener Discovery Service)
│ → リスナー設定の動的変更
├── RDS (Route Discovery Service)
│ → ルーティングルールの動的変更
├── CDS (Cluster Discovery Service)
│ → クラスター(アップストリーム)の動的変更
├── EDS (Endpoint Discovery Service)
│ → エンドポイント(IP:Port)の動的変更
├── SDS (Secret Discovery Service)
│ → TLS証明書の動的変更
└── ECDS (Extension Config Discovery)
→ フィルター設定の動的変更
4. AWS API Gateway
4.1 AWS API Gatewayタイプ比較(ひかく)
3種類のAWS API Gateway:
┌──────────────┬───────────────┬───────────────┬───────────────┐
│ │ REST API │ HTTP API │ WebSocket │
├──────────────┼───────────────┼───────────────┼───────────────┤
│ プロトコル │ REST │ REST │ WebSocket │
│ レイテンシ │ 標準 │ 低い(~35%減) │ 標準 │
│ 価格 │ 高い │ 低い(~70%減) │ メッセージ単位│
│ キャッシュ │ O │ X │ X │
│ Usage Plans │ O │ X │ X │
│ API Keys │ O │ X │ X │
│ WAF │ O │ X │ X │
│ Lambda統合 │ O │ O │ O │
│ VPC Link │ O │ O │ X │
│ 推奨用途 │ フル機能必要 │ シンプルプロキシ│ リアルタイム │
└──────────────┴───────────────┴───────────────┴───────────────┘
4.2 AWS REST API Gateway + Lambda
# SAMテンプレート - REST API Gateway
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Api:
Cors:
AllowMethods: "'GET,POST,PUT,DELETE,OPTIONS'"
AllowHeaders: "'Content-Type,Authorization,X-Api-Key'"
AllowOrigin: "'https://app.company.com'"
Resources:
ApiGateway:
Type: AWS::Serverless::Api
Properties:
StageName: prod
Auth:
DefaultAuthorizer: CognitoAuth
Authorizers:
CognitoAuth:
UserPoolArn: !GetAtt UserPool.Arn
ApiKeyRequired: true
UsagePlan:
CreateUsagePlan: PER_API
UsagePlanName: "StandardPlan"
Throttle:
BurstLimit: 100
RateLimit: 50
Quota:
Limit: 10000
Period: DAY
UserFunction:
Type: AWS::Serverless::Function
Properties:
Handler: handlers/user.handler
Runtime: nodejs20.x
MemorySize: 256
Timeout: 10
Events:
GetUsers:
Type: Api
Properties:
RestApiId: !Ref ApiGateway
Path: /api/v1/users
Method: GET
UserPool:
Type: AWS::Cognito::UserPool
Properties:
UserPoolName: api-user-pool
AutoVerifiedAttributes:
- email
Policies:
PasswordPolicy:
MinimumLength: 12
RequireUppercase: true
RequireLowercase: true
RequireNumbers: true
RequireSymbols: true
5. Traefik
5.1 Traefikアーキテクチャ
Traefik Architecture:
[クライアント]
│
┌────┴────────────────────────────────┐
│ Traefik Proxy │
│ │
│ EntryPoints(ポート) │
│ ├── :80 (web) │
│ └── :443 (websecure) │
│ │ │
│ Routers(ルーティングルール) │
│ ├── Host / Path / Header マッチ │
│ ├── TLS設定 │
│ └── Middlewareチェーン │
│ │ │
│ Middlewares(中間処理) │
│ ├── RateLimit │
│ ├── BasicAuth / ForwardAuth │
│ ├── Headers │
│ ├── Retry │
│ ├── CircuitBreaker │
│ └── StripPrefix │
│ │ │
│ Services(バックエンド) │
│ ├── LoadBalancer │
│ ├── Weighted │
│ └── Mirroring │
│ │
│ Providers(自動検出) │
│ ├── Docker │
│ ├── Kubernetes │
│ ├── File │
│ └── Consul / etcd │
└──────────────────────────────────────┘
5.2 Docker + Traefik自動検出(じどうけんしゅつ)
# docker-compose.yml with Traefik
version: "3.8"
services:
traefik:
image: traefik:v3.0
command:
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedByDefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
- "--certificatesresolvers.letsencrypt.acme.email=admin@company.com"
- "--metrics.prometheus=true"
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- letsencrypt:/letsencrypt
user-service:
image: user-service:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.users.rule=Host(`api.company.com`) && PathPrefix(`/api/v1/users`)"
- "traefik.http.routers.users.entrypoints=websecure"
- "traefik.http.routers.users.tls.certresolver=letsencrypt"
- "traefik.http.services.users.loadbalancer.server.port=8080"
volumes:
letsencrypt:
5.3 Kubernetes IngressRoute (CRD)
# Traefik IngressRoute CRD
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: api-routes
namespace: production
spec:
entryPoints:
- websecure
routes:
- match: Host(`api.company.com`) && PathPrefix(`/api/v1/users`)
kind: Rule
services:
- name: user-service
port: 8080
middlewares:
- name: rate-limit
- name: jwt-auth
- match: Host(`api.company.com`) && PathPrefix(`/api/v1/products`)
kind: Rule
services:
- name: product-service-v1
port: 8080
weight: 90
- name: product-service-v2
port: 8080
weight: 10
tls:
certResolver: letsencrypt
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: rate-limit
namespace: production
spec:
rateLimit:
average: 100
burst: 50
period: 1m
6. API Gateway比較表(15+項目(こうもく))
| 項目 | Kong | Envoy | AWS API GW | Traefik |
|---|---|---|---|---|
| 基盤 | Nginx/OpenResty | C++(自社製) | AWSマネージド | Go(自社製) |
| ライセンス | Apache 2.0 / Enterprise | Apache 2.0 | 従量課金 | MIT |
| デプロイ | セルフホスト / Cloud | セルフホスト(サイドカー) | サーバーレス | セルフホスト |
| パフォーマンス | 高い(Nginx) | 非常に高い | 高い(マネージド) | 高い |
| 設定方式 | Admin API / 宣言的 | YAML / xDS API | コンソール / CFn | ラベル / YAML / CRD |
| プラグイン | Lua / Go | C++ / WASM | Lambda Authorizer | 内蔵Middleware |
| サービス検出 | DNS / Consul | EDS (xDS) | CloudMap / VPC Link | Docker / K8s / Consul |
| gRPC | O | O | O (HTTP/2) | O |
| WebSocket | O | O | O(別API) | O |
| mTLS | O (Enterprise) | O | O (VPC Link) | O |
| レートリミット | 内蔵プラグイン | 外部サービス / WASM | 内蔵(Usage Plans) | 内蔵Middleware |
| 学習コスト | 中 | 高 | 低 | 低 |
| 適合対象 | 汎用API GW | Service Mesh | AWSサーバーレス | Docker/K8s環境 |
7. 認証(にんしょう)
7.1 JWT検証(けんしょう)実装
# JWT検証ミドルウェア(Python/FastAPI例)
from fastapi import Request, HTTPException
from jose import jwt, JWTError, ExpiredSignatureError
import httpx
class JWTAuthMiddleware:
def __init__(self, jwks_url: str, issuer: str, audience: str):
self.jwks_url = jwks_url
self.issuer = issuer
self.audience = audience
self._jwks_cache = None
async def get_jwks(self):
"""JWKSの取得とキャッシュ"""
if self._jwks_cache is None:
async with httpx.AsyncClient() as client:
response = await client.get(self.jwks_url)
self._jwks_cache = response.json()
return self._jwks_cache
async def verify_token(self, request: Request) -> dict:
"""リクエストからJWTを抽出して検証"""
auth_header = request.headers.get("Authorization")
if not auth_header or not auth_header.startswith("Bearer "):
raise HTTPException(
status_code=401,
detail="Missing or invalid Authorization header"
)
token = auth_header.split(" ")[1]
try:
unverified_header = jwt.get_unverified_header(token)
kid = unverified_header.get("kid")
jwks = await self.get_jwks()
key = None
for jwk in jwks.get("keys", []):
if jwk["kid"] == kid:
key = jwk
break
if key is None:
self._jwks_cache = None
jwks = await self.get_jwks()
for jwk in jwks.get("keys", []):
if jwk["kid"] == kid:
key = jwk
break
if key is None:
raise HTTPException(status_code=401, detail="Unknown signing key")
payload = jwt.decode(
token, key,
algorithms=["RS256"],
audience=self.audience,
issuer=self.issuer,
)
return payload
except ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Token expired")
except JWTError as e:
raise HTTPException(status_code=401, detail=f"Invalid token: {str(e)}")
7.2 OAuth2フロー
OAuth2 Authorization Code Flow + PKCE:
[ブラウザ/アプリ] [API Gateway] [Auth Server] [Backend]
│ │ │ │
│ 1. ログイン要求 │ │ │
│─────────────────────────▶│ │ │
│ │ │ │
│ 2. Auth Serverへリダイレクト │ │
│◀─────────────────────────│ │ │
│ │ │ │
│ 3. 認証(ID/PW or SSO) │ │ │
│──────────────────────────────────────────────▶│ │
│ │ │ │
│ 4. Authorization Code │ │ │
│◀──────────────────────────────────────────────│ │
│ │ │ │
│ 5. Code + PKCE verifier │ │ │
│─────────────────────────▶│ 6. Code -> Token │ │
│ │───────────────────▶│ │
│ │ 7. Access Token │ │
│ │◀───────────────────│ │
│ 8. Access Token │ │ │
│◀─────────────────────────│ │ │
│ │ │ │
│ 9. API要求 + Bearer │ 10. Token検証 │ │
│─────────────────────────▶│───────────────────▶│ │
│ │ 11. 検証結果 │ │
│ │◀───────────────────│ │
│ │ 12. 要求転送 │ │
│ │──────────────────────────────────▶│
│ 14. APIレスポンス │◀─────────────────────────────────│
│◀─────────────────────────│ │ │
8. レートリミットアルゴリズム
8.1 Token Bucket
import time
import threading
class TokenBucket:
"""Token Bucketレートリミッター
特徴:
- 一定速度でトークンがバケットに追加される
- リクエスト時にトークンを1つ消費
- トークンがなければリクエスト拒否
- バースト許可(バケットが満杯まで)
"""
def __init__(self, rate: float, capacity: int):
self.rate = rate # 秒あたりのトークン生成速度
self.capacity = capacity # バケット最大サイズ(バースト許容量)
self.tokens = capacity
self.last_refill = time.monotonic()
self.lock = threading.Lock()
def allow_request(self) -> bool:
with self.lock:
now = time.monotonic()
elapsed = now - self.last_refill
# トークン補充
self.tokens = min(
self.capacity,
self.tokens + elapsed * self.rate
)
self.last_refill = now
if self.tokens >= 1:
self.tokens -= 1
return True
return False
# 使用例
limiter = TokenBucket(rate=10, capacity=20)
# rate=10: 秒あたり10トークン生成
# capacity=20: 最大20トークン保存(バースト20)
8.2 Sliding Window Log
import time
from collections import deque
import threading
class SlidingWindowLog:
"""Sliding Window Logレートリミッター
特徴:
- 正確なウィンドウベースの制限
- メモリ使用量がリクエスト数に比例
- 境界問題なし(Fixed Windowの欠点を解消)
"""
def __init__(self, max_requests: int, window_seconds: int):
self.max_requests = max_requests
self.window_seconds = window_seconds
self.requests = deque()
self.lock = threading.Lock()
def allow_request(self) -> bool:
with self.lock:
now = time.monotonic()
window_start = now - self.window_seconds
while self.requests and self.requests[0] < window_start:
self.requests.popleft()
if len(self.requests) < self.max_requests:
self.requests.append(now)
return True
return False
# 使用例
limiter = SlidingWindowLog(max_requests=100, window_seconds=60)
8.3 分散(ぶんさん)レートリミット(Redis)
import redis
import time
class DistributedRateLimiter:
"""Redisベースの分散レートリミッター"""
def __init__(self, redis_client: redis.Redis, prefix: str = "rl"):
self.redis = redis_client
self.prefix = prefix
def is_allowed(
self,
key: str,
max_requests: int,
window_seconds: int
) -> dict:
now = time.time()
window_start = now - window_seconds
redis_key = f"{self.prefix}:{key}"
pipe = self.redis.pipeline()
pipe.zremrangebyscore(redis_key, 0, window_start)
pipe.zcard(redis_key)
pipe.zadd(redis_key, {f"{now}:{id(object())}": now})
pipe.expire(redis_key, window_seconds + 1)
results = pipe.execute()
current_count = results[1]
if current_count < max_requests:
return {
"allowed": True,
"remaining": max(0, max_requests - current_count - 1),
"limit": max_requests,
}
else:
self.redis.zrem(redis_key, f"{now}:{id(object())}")
return {
"allowed": False,
"remaining": 0,
"limit": max_requests,
"retry_after": window_seconds,
}
9. サーキットブレーカーとカナリアデプロイ
9.1 サーキットブレーカーパターン
Circuit Breaker状態:
┌──────────┐ 失敗率超過 ┌──────────┐
│ CLOSED │──────────────────▶│ OPEN │
│ (正常) │ │ (遮断) │
└──────────┘ └────┬─────┘
▲ │
│ タイムアウト後│
│ 成功 ┌───────────┴──┐
└───────────────────│ HALF-OPEN │
│ (テスト) │
└──────────────┘
│
失敗時 → 再びOPENへ
# Envoy Circuit Breaker設定
clusters:
- name: order_service
circuit_breakers:
thresholds:
- priority: DEFAULT
max_connections: 1024
max_pending_requests: 512
max_requests: 2048
max_retries: 3
outlier_detection:
consecutive_5xx: 5
interval: 10s
base_ejection_time: 30s
max_ejection_percent: 50
9.2 カナリア/A-Bルーティング
# Envoy Weighted Routing(カナリアデプロイ)
route_config:
virtual_hosts:
- name: api
domains: ["api.company.com"]
routes:
- match:
prefix: "/api/v1/products"
route:
weighted_clusters:
clusters:
- name: product_v1
weight: 90
- name: product_v2
weight: 10
# ヘッダーベースA/Bルーティング
- match:
prefix: "/api/v1/checkout"
headers:
- name: "X-Feature-Flag"
exact_match: "new-checkout"
route:
cluster: checkout_v2
- match:
prefix: "/api/v1/checkout"
route:
cluster: checkout_v1
10. GraphQL Gateway
10.1 Apollo Router / Federation
GraphQL Federation Architecture:
[クライアント]
│
┌────┴────────────────────────┐
│ Apollo Router │
│ (Supergraph Gateway) │
│ │
│ ┌───────────────────────┐ │
│ │ Query Planner │ │
│ │ → どのサブグラフに │ │
│ │ どのクエリを送るか │ │
│ │ 最適計画を策定 │ │
│ └───────────────────────┘ │
└──────┬──────┬──────┬────────┘
│ │ │
┌────┴──┐ ┌┴────┐ ┌┴────────┐
│ User │ │Order│ │ Product │
│Sub- │ │Sub- │ │Subgraph │
│graph │ │graph│ │ │
└───────┘ └─────┘ └──────────┘
# Apollo Router設定
supergraph:
listen: 0.0.0.0:4000
traffic_shaping:
all:
timeout: 30s
subgraphs:
users:
timeout: 10s
orders:
timeout: 15s
limits:
max_depth: 15
max_height: 200
max_aliases: 30
telemetry:
exporters:
metrics:
prometheus:
enabled: true
listen: 0.0.0.0:9090
tracing:
otlp:
enabled: true
endpoint: http://otel-collector:4317
ratelimit:
global:
capacity: 1000
interval: 1m
11. APIバージョニング戦略(せんりゃく)
11.1 バージョニング方式(ほうしき)比較
3つのAPIバージョニング戦略:
1. URL Pathバージョニング
GET /api/v1/users
GET /api/v2/users
→ 最も直感的、広く使用
→ キャッシュフレンドリー
→ URLが変わる(Breaking)
2. Headerバージョニング
GET /api/users
Accept: application/vnd.company.v2+json
→ URLがクリーン
→ ブラウザでのテストが困難
3. Query Parameterバージョニング
GET /api/users?version=2
→ シンプル
→ キャッシング複雑
11.2 Gatewayレベルバージョニング実装
# Kong Routeベースのバージョニング
services:
- name: user-service-v1
url: http://user-service-v1:8080
routes:
- name: users-v1
paths:
- /api/v1/users
- name: user-service-v2
url: http://user-service-v2:8080
routes:
- name: users-v2
paths:
- /api/v2/users
# ヘッダーベースバージョニング
- name: user-service-v2-header
url: http://user-service-v2:8080
routes:
- name: users-v2-header
paths:
- /api/users
headers:
X-API-Version:
- "2"
12. モニタリングとオブザーバビリティ
12.1 Prometheusメトリクス
# API Gatewayコアメトリクス - 4 Golden Signals
golden_signals:
latency:
- histogram: api_request_duration_seconds
labels: [method, route, status_code]
buckets: [0.01, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
traffic:
- counter: api_requests_total
labels: [method, route, status_code, consumer]
errors:
- counter: api_errors_total
labels: [method, route, error_type]
saturation:
- gauge: api_active_connections
- gauge: api_rate_limit_remaining
# Grafanaダッシュボードクエリ例
panels:
- title: "Request Rate (RPS)"
query: "sum(rate(api_requests_total[5m])) by (route)"
- title: "P99 Latency"
query: |
histogram_quantile(0.99,
sum(rate(api_request_duration_seconds_bucket[5m])) by (le, route)
)
- title: "Error Rate"
query: |
sum(rate(api_requests_total{status_code=~"5.."}[5m]))
/ sum(rate(api_requests_total[5m])) * 100
12.2 分散トレーシング
API Gateway分散トレーシングフロー:
Request ID: abc-123-def
[Client] ─── [API Gateway] ──── [User Service] ──── [DB]
│ │ │ │
│ Span: req │ Span: gateway │ Span: user-svc│ Span: db-query
│ trace: abc │ trace: abc │ trace: abc │ trace: abc
│ dur: 250ms │ dur: 200ms │ dur: 150ms │ dur: 50ms
│ │ │ │
│ Tags: │ Tags: │ Tags: │
│ GET /usr │ auth: jwt │ db: postgres │
│ 200 │ cache: miss │ SELECT │
13. クイズ
Q1. API Gatewayの3つのコアパターンは何ですか?
回答:
-
Routingパターン: クライアントリクエストをURLパス、ヘッダーなどに基づいて正しいバックエンドサービスに転送します。
-
Aggregationパターン: 複数のバックエンドサービスのレスポンスを1つにまとめてクライアントに単一レスポンスとして返します。BFF(Backend for Frontend)パターンと関連します。
-
Offloadingパターン: 認証、SSL終了、キャッシング、レートリミットなどの共通関心事を個別サービスからGatewayに移管し、サービスがビジネスロジックに集中できるようにします。
Q2. Kong、Envoy、AWS API Gateway、Traefikはそれぞれどのような状況に適していますか?
回答:
-
Kong: 汎用API Gatewayが必要な場合。豊富なプラグインエコシステム、DB-lessモードサポート。認証/レートリミット/変換がプラグインですぐ使えます。
-
Envoy: Service Meshのデータプレーンやサイドカープロキシとして使用する場合。xDS APIによる動的設定、WASMフィルターでカスタム拡張。Istioとの統合時に最適です。
-
AWS API Gateway: AWS Lambdaベースのサーバーレスアーキテクチャ。インフラ管理不要、Usage PlansとAPI Key管理が内蔵されています。
-
Traefik: Docker/Kubernetes環境での自動サービス検出が必要な場合。ラベル/アノテーションだけでルーティング設定可能、Let's Encrypt自動証明書管理をサポートします。
Q3. Token BucketとSliding Windowレートリミットの違いは?
回答:
-
Token Bucket: 一定速度でトークンが補充され、リクエストごとにトークンを消費します。バケットが満杯の時は一時的なバーストが許可されます。メモリ効率が良く、実装がシンプルです。
-
Sliding Window Log: 各リクエストのタイムスタンプを記録し、現在時点からウィンドウサイズ分遡ってリクエストをカウントします。Fixed Windowの境界問題(ウィンドウの始まり/終わりでの2倍バースト)がなく正確ですが、メモリ使用量がリクエスト数に比例します。
実務ではRedis Sorted Setを使用したSliding Window Counterが、正確性とメモリ効率のバランスが良く広く使われています。
Q4. GraphQL Federationの利点とApollo Routerの役割は?
回答:
GraphQL Federationは、複数のサービスがそれぞれのGraphQLスキーマ(サブグラフ)を定義し、それを1つの統合スキーマ(スーパーグラフ)に合成するアーキテクチャです。
利点:
- サービスの自律性:各チームが独立してドメインスキーマを管理
- 単一エンドポイント:クライアントは1つのGraphQLエンドポイントのみ使用
- 型拡張:サービス間で型を拡張して接続
Apollo Routerの役割:
- クライアントクエリを分析し、どのサブグラフにどのクエリを送るかQuery Planを策定
- 各サブグラフのレスポンスを自動的にマージしてクライアントに返却
- レートリミット、認証、トレーシング、キャッシングなどゲートウェイ機能を提供
Q5. API Gatewayでモニタリングすべき4つのGolden Signalは?
回答: Google SREが提唱した4つのGolden Signalです:
-
Latency(レイテンシ): リクエスト処理にかかる時間。P50、P95、P99パーセンタイルの追跡が重要です。
-
Traffic(トラフィック): 秒あたりリクエスト数(RPS)。エンドポイント別、コンシューマー別のトラフィックパターンを把握します。
-
Errors(エラー率): 全リクエストに対するエラーの比率。5xxサーバーエラーと4xxクライアントエラーを区別します。
-
Saturation(飽和度): システムリソース使用率。同時接続数、レートリミット残量、サーキットブレーカー状態などです。
14. 参考資料(さんこうしりょう)
- Kong Documentation
- Envoy Proxy Documentation
- AWS API Gateway Developer Guide
- Traefik Documentation
- Apollo Router Documentation
- GraphQL Federation Specification
- NGINX Rate Limiting
- Token Bucket Algorithm (Wikipedia)
- Envoy xDS Protocol
- Google SRE Book - Monitoring Distributed Systems
- OpenTelemetry Documentation
- Istio Service Mesh
- RFC 6585 - Additional HTTP Status Codes (429)
- Microsoft API Design Best Practices