Начнем с основ конфигурации timeout и retry. В реальном мире сервисы тормозят, падают и теряют пакеты. Сетевые таймауты и повторные попытки — это наша первая линия обороны. Вот базовая конфигурация VirtualService для управления таймаутами:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: payment-service
spec:
hosts:
- payment-service
http:
- route:
- destination:
host: payment-service
subset: v1
timeout: 3s
retries:
attempts: 3
perTryTimeout: 1s
retryOn: connect-failure,refused-stream,5xx
Этот конфиг говорит: "Если платежный сервис не ответил за 3 секунды или вернул ошибку — делаем еще три попытки, каждая по секунде". Обратите внимание на retryOn — здесь перечислены условия для повторных попыток. В продакшене часто добавляют gateway-error и reset для борьбы с сетевыми глюками.
Но одни таймауты не спасут от каскадных отказов. Тут в бой вступает circuit breaker. Его задача — вовремя понять, что сервис болеет, и перестать его мучить запросами. Настраивается через DestinationRule:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: payment-circuit-breaker
spec:
host: payment-service
trafficPolicy:
outlierDetection:
consecutive5xxErrors: 5
interval: 10s
baseEjectionTime: 30s
maxEjectionPercent: 50
connectionPool:
tcp:
maxConnections: 100
http:
http1MaxPendingRequests: 1
maxRequestsPerConnection: 10
Тут мы говорим: "После 5 ошибок подряд в течение 10 секунд выключаем проблемный под на 30 секунд". MaxEjectionPercent защищает от ситуации, когда все поды вырублены — минимум 50% останутся в строю. А connectionPool не дает завалить сервис лавиной запросов.
Теперь самое интересное — rate limiting. В продакшене часто бывает, что один сервис-сосед начинает долбить твой сервис миллионом запросов в секунду. Rate limiting помогает зарубить такие атаки на корню:
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: payment-ratelimit
spec:
workloadSelector:
labels:
app: payment-service
configPatches:
- applyTo: HTTP_FILTER
match:
context: SIDECAR_INBOUND
listener:
filterChain:
filter:
name: "envoy.filters.network.http_connection_manager"
patch:
operation: INSERT_BEFORE
value:
name: envoy.filters.http.local_ratelimit
typedConfig:
"@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
statPrefix: http_local_rate_limiter
tokenBucket:
maxTokens: 1000
tokensPerFill: 100
fillInterval: 1s
filterEnabled:
runtime_key: local_rate_limit_enabled
default_value:
numerator: 100
denominator: HUNDRED
Этот монстр ограничивает входящий трафик до 100 запросов в секунду с возможностью буста до 1000 запросов. Важный момент — rate limiting лучше делать на уровне отдельных эндпоинтов, а не всего сервиса. Какие-то API могут быть тяжелыми и требовать жестких ограничений, а какие-то — легкими и способными переварить больше трафика.
Для мониторинга всей этой красоты нужны правильные метрики. Базовый набор:
-
istio_request_duration_milliseconds — латентность запросов-
istio_requests_total — количество запросов с разбивкой по кодам ответа-
istio_request_bytes — размер запросов-
envoy_cluster_upstream_rq_pending_overflow — сработавшие circuit breaker-
envoy_cluster_upstream_rq_retry — количество повторных попыток-
envoy_http_ratelimit_total_requests_denied — отклоненные rate limiter запросы🏴☠️ @happy_devops
