レート制限
レート制限は、サービスを過負荷から保護し、公平なリソース使用を確保し、コストを管理するためにリクエストレートを制限する機能です。
目次
概要
レート制限は、次のような状況で必要です。
レート制限の目的
- サービス保護: 過負荷を防止
- 公平性: すべてのクライアントへの公平なリソース配分
- コスト管理: 外部 API 呼び出しのコストを管理
- セキュリティ: DDoS 攻撃への防御
レート制限の種類
1. ローカルレート制限
特徴:
- 各 Envoy proxy が独立して制限
- 高速な応答(追加のネットワーク呼び出しなし)
- 分散環境では、合計制限がインスタンスごとに適用される
yaml
# 100 req/s limit per pod
# With 3 pods, up to 300 req/s total allowed2. グローバルレート制限
特徴:
- 集中型の Rate Limit server を使用
- 正確な合計制限(すべてのインスタンスで共有)
- わずかなレイテンシー(外部サービス呼び出し)
yaml
# 100 req/s total limit
# Only 100 req/s allowed regardless of pod count比較
| 特性 | ローカルレート制限 | グローバルレート制限 |
|---|---|---|
| 正確性 | 低い(インスタンスごと) | 高い(合計) |
| パフォーマンス | 非常に高速 | やや低速 |
| 複雑性 | 低い | 高い(外部サービスが必要) |
| ユースケース | 一般的な保護 | 正確な制限が必要な場合 |
ローカルレート制限
Token Bucket アルゴリズム
基本設定
yaml
apiVersion: networking.istio.io/v1
kind: EnvoyFilter
metadata:
name: local-ratelimit
namespace: default
spec:
workloadSelector:
labels:
app: myapp
configPatches:
- applyTo: HTTP_FILTER
match:
context: SIDECAR_INBOUND
listener:
filterChain:
filter:
name: "envoy.filters.network.http_connection_manager"
subFilter:
name: "envoy.filters.http.router"
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.local_ratelimit
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
stat_prefix: http_local_rate_limiter
token_bucket:
max_tokens: 100 # Maximum token count
tokens_per_fill: 10 # Tokens to add
fill_interval: 1s # Fill interval
filter_enabled:
runtime_key: local_rate_limit_enabled
default_value:
numerator: 100
denominator: HUNDRED
filter_enforced:
runtime_key: local_rate_limit_enforced
default_value:
numerator: 100
denominator: HUNDRED
response_headers_to_add:
- append: false
header:
key: x-local-rate-limit
value: 'true'主要パラメータ:
max_tokens: バケットが保持できる最大トークン数(バーストを許可)tokens_per_fill: fill_interval ごとに追加するトークン数fill_interval: トークンの追加間隔
例:
yaml
# 10 requests per second, 100 burst allowed
token_bucket:
max_tokens: 100
tokens_per_fill: 10
fill_interval: 1s
# Result:
# - Average: 10 req/s
# - Burst: 100 req/s (for short periods)パスベースのレート制限
yaml
apiVersion: networking.istio.io/v1
kind: EnvoyFilter
metadata:
name: path-based-ratelimit
namespace: default
spec:
workloadSelector:
labels:
app: api-service
configPatches:
- applyTo: HTTP_FILTER
match:
context: SIDECAR_INBOUND
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.local_ratelimit
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
stat_prefix: http_local_rate_limiter
# Different limits per path
descriptors:
# /api/v1/users - High limit
- entries:
- key: header_match
value: "/api/v1/users"
token_bucket:
max_tokens: 1000
tokens_per_fill: 100
fill_interval: 1s
# /api/v1/admin - Low limit
- entries:
- key: header_match
value: "/api/v1/admin"
token_bucket:
max_tokens: 100
tokens_per_fill: 10
fill_interval: 1sヘッダーベースのレート制限
yaml
apiVersion: networking.istio.io/v1
kind: EnvoyFilter
metadata:
name: user-based-ratelimit
namespace: default
spec:
workloadSelector:
labels:
app: api-service
configPatches:
- applyTo: HTTP_FILTER
match:
context: SIDECAR_INBOUND
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.local_ratelimit
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
stat_prefix: http_local_rate_limiter
# Limits by user tier
descriptors:
# Premium users
- entries:
- key: header_match
value: "x-user-tier:premium"
token_bucket:
max_tokens: 1000
tokens_per_fill: 100
fill_interval: 1s
# Free users
- entries:
- key: header_match
value: "x-user-tier:free"
token_bucket:
max_tokens: 100
tokens_per_fill: 10
fill_interval: 1sグローバルレート制限
グローバルレート制限は、集中型の Rate Limit service を使用して、クラスター全体にわたり正確なレート制限を適用します。
アーキテクチャ
設定方法
グローバルレート制限では、外部 Rate Limit service をデプロイし、EnvoyFilter と統合する必要があります。
1. Rate Limit Service のデプロイ
注記: Istio は外部依存関係として envoyproxy/ratelimit service を使用します。
yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: ratelimit-config
namespace: istio-system
data:
config.yaml: |
domain: production-ratelimit
descriptors:
# Global limit: 100 per second
- key: generic_key
value: "global"
rate_limit:
unit: second
requests_per_unit: 100
# Per-path limit
- key: header_match
value: "/api/v1/*"
rate_limit:
unit: second
requests_per_unit: 50
# Per-user limit (per minute)
- key: remote_address
rate_limit:
unit: minute
requests_per_unit: 1000
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ratelimit
namespace: istio-system
spec:
replicas: 1
selector:
matchLabels:
app: ratelimit
template:
metadata:
labels:
app: ratelimit
spec:
containers:
- name: ratelimit
image: envoyproxy/ratelimit:19f2079f # Use stable version
ports:
- containerPort: 8080
name: http
- containerPort: 8081
name: grpc
env:
- name: LOG_LEVEL
value: debug
- name: RUNTIME_ROOT
value: /data
- name: RUNTIME_SUBDIRECTORY
value: ratelimit
- name: RUNTIME_IGNOREDOTFILES
value: "true"
- name: USE_STATSD
value: "false"
volumeMounts:
- name: config-volume
mountPath: /data/ratelimit/config
readOnly: true
command: ["/bin/ratelimit"]
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
volumes:
- name: config-volume
configMap:
name: ratelimit-config
---
apiVersion: v1
kind: Service
metadata:
name: ratelimit
namespace: istio-system
spec:
ports:
- port: 8080
name: http
targetPort: 8080
- port: 8081
name: grpc
targetPort: 8081
selector:
app: ratelimit2. EnvoyFilter でグローバルレート制限を設定
yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: filter-ratelimit
namespace: istio-system
spec:
workloadSelector:
labels:
istio: ingressgateway
configPatches:
# Add HTTP filter
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: "envoy.filters.network.http_connection_manager"
subFilter:
name: "envoy.filters.http.router"
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.ratelimit
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit
domain: production-ratelimit
failure_mode_deny: true
timeout: 5s
rate_limit_service:
grpc_service:
envoy_grpc:
cluster_name: rate_limit_cluster
transport_api_version: V3
# Add Rate Limit cluster
- applyTo: CLUSTER
match:
context: GATEWAY
patch:
operation: ADD
value:
name: rate_limit_cluster
type: STRICT_DNS
connect_timeout: 5s
lb_policy: ROUND_ROBIN
http2_protocol_options: {}
load_assignment:
cluster_name: rate_limit_cluster
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: ratelimit.istio-system.svc.cluster.local
port_value: 8081
---
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: filter-ratelimit-svc
namespace: istio-system
spec:
workloadSelector:
labels:
istio: ingressgateway
configPatches:
# Add rate limit action to VirtualHost
- applyTo: VIRTUAL_HOST
match:
context: GATEWAY
patch:
operation: MERGE
value:
rate_limits:
# Global limit
- actions:
- generic_key:
descriptor_value: "global"3. VirtualService に Rate Limit アクションを追加
yaml
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: filter-ratelimit-actions
namespace: default
spec:
workloadSelector:
labels:
istio: ingressgateway
configPatches:
- applyTo: VIRTUAL_HOST
match:
context: GATEWAY
routeConfiguration:
vhost:
name: "*:80"
patch:
operation: MERGE
value:
rate_limits:
# Path-based Rate Limiting
- actions:
- header_value_match:
descriptor_value: "/api/v1/*"
headers:
- name: ":path"
string_match:
prefix: "/api/v1/"
# IP-based Rate Limiting
- actions:
- remote_address: {}主要パラメータの説明
| パラメータ | 説明 |
|---|---|
domain | Rate Limit Service の設定ドメイン(ConfigMap と一致している必要があります) |
failure_mode_deny | Rate Limit Service が失敗した場合にリクエストを拒否するかどうか |
timeout | Rate Limit Service の応答待機時間 |
rate_limit_service | 外部 Rate Limit Service の gRPC エンドポイント |
グローバルレート制限とローカルレート制限の選択基準
ローカルレート制限を使用する場合:
- シンプルな設定
- 高速な応答速度
- 外部依存関係なし
- Pod ごとの制限(合計制限は不正確)
グローバルレート制限を使用する場合:
- 正確な合計制限
- 複雑なルール(ユーザーごと、IP ごと、パスごと)
- 集中管理
- 外部サービスが必要(複雑性が増加)
- わずかなレイテンシー(gRPC 呼び出し)
推奨事項:
- 本番 API Gateway: グローバルレート制限(正確な制御が必要)
- マイクロサービス保護: ローカルレート制限(高速な応答)
- ハイブリッド: Gateway ではグローバル、内部サービスではローカル
実践例
例 1: API Gateway のレート制限
yaml
apiVersion: networking.istio.io/v1
kind: EnvoyFilter
metadata:
name: api-gateway-ratelimit
namespace: istio-system
spec:
workloadSelector:
labels:
istio: ingressgateway
configPatches:
- applyTo: HTTP_FILTER
match:
context: GATEWAY
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.local_ratelimit
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
stat_prefix: http_local_rate_limiter
# Public API: Low limit
descriptors:
- entries:
- key: header_match
value: "/api/v1/public/*"
token_bucket:
max_tokens: 100
tokens_per_fill: 10
fill_interval: 1s
# Authenticated API: High limit
- entries:
- key: header_match
value: "/api/v1/protected/*"
token_bucket:
max_tokens: 1000
tokens_per_fill: 100
fill_interval: 1s
# GraphQL: Medium limit
- entries:
- key: header_match
value: "/graphql"
token_bucket:
max_tokens: 500
tokens_per_fill: 50
fill_interval: 1s例 2: ユーザーごとの階層型レート制限
yaml
apiVersion: networking.istio.io/v1
kind: EnvoyFilter
metadata:
name: tiered-ratelimit
namespace: default
spec:
workloadSelector:
labels:
app: api-service
configPatches:
- applyTo: HTTP_FILTER
match:
context: SIDECAR_INBOUND
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.local_ratelimit
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
stat_prefix: http_local_rate_limiter
descriptors:
# Enterprise: 1000 req/s
- entries:
- key: header_match
value: "x-api-tier:enterprise"
token_bucket:
max_tokens: 10000
tokens_per_fill: 1000
fill_interval: 1s
# Premium: 100 req/s
- entries:
- key: header_match
value: "x-api-tier:premium"
token_bucket:
max_tokens: 1000
tokens_per_fill: 100
fill_interval: 1s
# Free: 10 req/s
- entries:
- key: header_match
value: "x-api-tier:free"
token_bucket:
max_tokens: 100
tokens_per_fill: 10
fill_interval: 1s例 3: 外部 API の保護
yaml
# External API call limiting (Egress)
apiVersion: networking.istio.io/v1
kind: EnvoyFilter
metadata:
name: external-api-ratelimit
namespace: default
spec:
workloadSelector:
labels:
app: myapp
configPatches:
- applyTo: HTTP_FILTER
match:
context: SIDECAR_OUTBOUND
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.local_ratelimit
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
stat_prefix: egress_rate_limiter
# External API limit (cost savings)
token_bucket:
max_tokens: 1000 # Allow burst
tokens_per_fill: 10 # 10 per second
fill_interval: 1s
# Log when limit exceeded
response_headers_to_add:
- header:
key: x-rate-limit-exceeded
value: "true"モニタリング
Prometheus メトリクス
yaml
# Rate Limiting metrics
# 1. Limited request count
rate(envoy_http_local_rate_limit_rate_limited[5m])
# 2. Allowed request count
rate(envoy_http_local_rate_limit_ok[5m])
# 3. Rate Limit application rate
(rate(envoy_http_local_rate_limit_rate_limited[5m])
/
(rate(envoy_http_local_rate_limit_rate_limited[5m]) + rate(envoy_http_local_rate_limit_ok[5m]))) * 100
# 4. Global Rate Limit calls
rate(envoy_cluster_ratelimit_over_limit[5m])Grafana ダッシュボード
json
{
"dashboard": {
"title": "Istio Rate Limiting",
"panels": [
{
"title": "Rate Limited Requests",
"targets": [
{
"expr": "rate(envoy_http_local_rate_limit_rate_limited[5m])",
"legendFormat": "{{pod_name}}"
}
]
},
{
"title": "Rate Limit Hit Rate",
"targets": [
{
"expr": "(rate(envoy_http_local_rate_limit_rate_limited[5m]) / (rate(envoy_http_local_rate_limit_rate_limited[5m]) + rate(envoy_http_local_rate_limit_ok[5m]))) * 100",
"legendFormat": "Hit Rate %"
}
]
}
]
}
}トラブルシューティング
レート制限が機能しない
bash
# 1. Check EnvoyFilter
kubectl get envoyfilter -A
# 2. Check Envoy configuration
istioctl proxy-config listeners <pod-name> -n <namespace> -o json | \
jq '.[] | select(.name | contains("0.0.0.0")) | .filterChains[].filters[] | select(.name == "envoy.filters.network.http_connection_manager") | .typedConfig.httpFilters[] | select(.name == "envoy.filters.http.local_ratelimit")'
# 3. Check logs
kubectl logs -n <namespace> <pod-name> -c istio-proxy | grep ratelimitグローバルレート制限の接続失敗
bash
# Check Rate Limit Service
kubectl get pods -n istio-system -l app=ratelimit
kubectl logs -n istio-system -l app=ratelimit
# Check Redis connection
kubectl exec -n istio-system -it deploy/ratelimit -- redis-cli -h redis-ratelimit ping