Операции и Управление → Зависимости сервисов
Зависимости сервисов
1) Зачем это нужно
Любая продакшн-платформа — это граф: пользователи → Edge/API → доменные сервисы → очереди/стримы → БД/кэши → внешние провайдеры (платежи, KYC, провайдеры игр). Ошибка на одном ребре графа часто «гуляет» по всей сети: растут задержки, срабатывают ретраи, забиваются очереди, происходят каскадные отказы. Управление зависимостями снижает «взрывной радиус» и делает релизы предсказуемыми.
Цели:- Видеть полный граф вызовов и понимать, кто от кого зависит.
- Предотвращать каскадные отказы и «шторм ретраев».
- Планировать релизы с учетом совместимости и SLO-пропагации.
- Повышать MTTR: быстрее находить истинный первопричинный узел (root cause).
2) Типы зависимостей
Синхронные (RPC: REST/gRPC/GraphQL): жесткая связность по латентности/доступности. Нужны таймауты, брейкеры, бюджет ретраев.
Асинхронные (Event/Stream: Kafka/Rabbit/Pulsar): более устойчивая связность, но есть lag/backlog и семантика доставки (at-least-once, idempotency).
Хранилища (DB/Cache/Object store): разделяемые ресурсы → контеншн, лимиты коннектов/IOPS, eviction, репликация.
Внешние провайдеры (PSP/KYC/игровые провайдеры): квоты, платные вызовы, окна обслуживания, юридические SLA.
Операционные (релизы, фичефлаги, конфиги): косвенные зависимости через настройки, секреты, schema registry.
3) Каталог сервисов и граф зависимостей
Что фиксируем в каталоге (Backstage/Service Catalog/CMDB):- Владельцы (Squad/чат/On-call rota), репо, среда, артефакты.
- Контракты API (OpenAPI/AsyncAPI), версии, совместимость (back/forward).
- Входящие/исходящие зависимости (upstream/downstream) с типом (sync/async), критичность, ожидания SLO.
- Бюджет таймаутов/ретраев, брейкеры, bulkhead-пулы.
- Данные о квотах и лимитах внешних интеграций.
- `service: payments-api`
- Upstream: `user-profile` (sync), `risk-score` (async).
- Downstream: `PSP-X` (sync, квота 2k RPS), `ledger` (async).
- SLO: p99 ≤ 300 мс, 99.9% uptime.
- Таймауты: 200 мс к `PSP-X`, 150 мс к `user-profile`.
- Ретраи: 2 с экспоненциальной задержкой, джиттер.
- Брейкер: open на 30 с при 5% ошибок/10 с.
4) SLO-пропагация и «бюджет латентности»
При цепочке синхронных вызовов итоговый SLO формируется из суммы задержек и вероятностей отказов.
Принципы:- Бюджет запроса разбивается сверху вниз: фронтовой SLO 500 мс → Edge 50 мс → API 150 мс → доменные сервисы 200 мс → провайдер 100 мс.
- Таймауты «наружу короче, чем внутрь»: у вызывающего таймаут меньше суммарного внутреннего, чтобы обновлялись ресурсы, а не копились зомби-вызовы.
- Ретраи только на безопасные коды/исключения и с джиттером; без ретраев на таймауты узких мест (иначе «шторм»).
5) Контракты и совместимость
Версионирование API: SemVer для контрактов; backward-compatible изменения через поля «optional», расширения схемы; удаление — только через «депрекейт-период».
Consumer-driven contracts (CDC): тесты потребителей (Pact-подобные) запускаются против провайдера в CI; релиз блокируется при несовместимости.
Схема-регистры (Async): версия топиков/событий, эволюция схем (Avro/JSON-Schema), политика «can-read-old / can-write-new».
6) Инженерные паттерны устойчивости
Timeouts: отделяем SLA бизнеса от технических ожиданий; каждое исходящее соединение — явный таймаут.
Retries + backoff + jitter: не более 2–3 попыток, учитывая идемпотентность.
Circuit Breaker: «быстрое падение» при деградации даунстрима; half-open пробы.
Bulkhead (изоляция пулов): для разных даунстримов — отдельные пулы потоков/подов/соединений.
Rate-limit/Leaky-bucket: чтобы не убивать даунстримы при пиках.
Idempotency & дедупликация: ключ идемпотентности на уровне запроса/сообщения; дед-лейтеры и ретрай-очереди.
Кэширование и фоллбеки: локальные/распределенные кэши, статусы «stale-while-revalidate», деградация контента.
outbound:
psp-x:
timeout_ms: 200 retries: 2 retry_on: [5xx, connect_error]
backoff: exponential jitter: true circuit_breaker:
error_rate_threshold: 0. 05 window_s: 10 open_s: 30 pool: dedicated-psp (max_conns: 200)
7) Наблюдаемость зависимостей
Распределенные трассировки (TraceID, Baggage): видеть путь запроса по звеньям; спаны на исходящие вызовы с тегами `peer.service`, `retry`, `timeout`.
Метрики per-dependency: `outbound_latency_p99`, `outbound_error_rate`, `open_circuit`, `retry_count`, `queue_lag`.
- Карта сервисов с цветовой индикацией SLO и ошибочных ребер.
- «Top N проблемных зависимостей» по за последнюю неделю.
- «Blast radius» — список сервисов, которые пострадают при падении X.
- Логи корреляции: включать `trace_id`/`span_id` в журналы.
8) Управление релизами с учетом зависимостей
Dependency-aware пайплайны: релиз провайдера блокируется, если CDC-тесты потребителей красные.
Постепенное включение (фичефлаги): новые поля/эндоинты → для 1% потребителей → 10% → 100%.
Канареечные релизы: проверяем ключевые зависимости и «бюджет латентности» на доле трафика.
Совместимость схем: продюсер пишет `vNew`, консюмеры читают `vOld/vNew`; после перехода — «сбор мусора» старых полей.
9) Инциденты и эскалации по графу
Определяем «истинного виновника»: alert-корреляция — если деградировал `PSP-X`, не пейджим весь «платежный куст», а владельца интеграции.
Автодеградация: фичефлаг «минимальный режим» (менее тяжелые эндпоинты, урезанные бандлы, отключение не-критичных фич).
Гарды от каскадов: ограничиваем параллелизм, выключаем ретраи на горячей ветке, открываем брейкер заранее (pre-open).
- Диагностика: какие дашборды/метрики, как проверить квоты/лимиты.
- Действия: снизить RPS, переключить на резервного провайдера, временно включить кэш-ответы.
- Откат и валидация: вернуть параметры, убедиться в норме p95/p99 и error-rate.
10) Матрица критичности зависимостей
Оцените каждую связь по осям: Правила:- Для «критических» — двойной провайдинг, брейкеры, отдельные пулы, хаос-тесты.
- Для «высоких» — хотя бы деградация и «зеленая кнопка» выключения фичи.
- Для «средних/низких» — лимиты на ретраи и бюджет очередей.
11) Процесс: от инвентаризации до эксплуатации
1. Картирование графа: собрать фактические вызовы (трассировки) + декларативные зависимости из каталога.
2. Назначить владельцев: на каждый сервис и внешнюю интеграцию — ответственный on-call.
3. Определить SLO и бюджеты: латентность/ошибки, таймауты/ретраи/пулы.
4. Формализовать контракты: OpenAPI/AsyncAPI, схемы и CDC.
5. Включить паттерны устойчивости: timeouts/retries/circuit/bulkhead.
6. Настроить дашборды и алерты per-dependency.
7. Поставить релиз-гейты: блок по CDC/совместимости/канарейке.
8. Регулярные game-days: хаос-эксперименты по падению ключевых ребер.
9. Постмортемы с фокусом на связи: что усилило каскад, как сузить радиус.
12) Алерты на зависимости (идеи правил)
Синхронные даунстримы:- `outbound_error_rate{to="X"} > 3% FOR 10m` → warning; `>5% FOR 5m` → critical.
- `outbound_p99_latency{to="X"} > SLO1.3 FOR 10m` → warning.
- `circuit_open{to="X"} == 1 FOR 1m` → page владельцу интеграции.
- `retry_rate{to="X"} > baseline2 FOR 5m` + `outbound_rps > 0` → риск шторма.
- `consumer_lag{topic="Y"} growth > threshold FOR 10m` + `hpa at max` → крит.
- `usage_quota{provider="PSP-X"} > 90% window` → предупреждение, автопереключение маршрутов.
13) Анти-паттерны
«Один общий пул потоков на все даунстримы». Итого: head-of-line blocking. Делите пулы.
Без таймаутов/с бесконечными ретраями. Так рождается шторм.
Слепые ретраи неидемпотентных операций. Дубли списаний/ставок.
Скрытая «общая БД» как точка связности. Сильная конкуренция и блокировки.
Версия API меняется без CDC и депрекейт-плана. Ловите массовые падения.
Наблюдаемость только по сервисам, не по связям. Не видно, где рвется цепь.
14) Дашборды: минимальный набор
Service Map: интерактивная карта сервисов с метриками ребер (latency/error/volume).
Upstream/Downstream Overview: для владельца сервиса — входящие зависимости (кто звонит), исходящие (кому звоним), «топ проблем».
Dependency Drilldown: карточка конкретной связи: p50/p95/p99, ошибки по классам, процент открытого брейкера, ретраи, пул соединений, квоты/кост.
Release Context: аннотации релизов/фичефлагов на графиках зависимостей.
15) Чек-лист внедрения
- Каталог сервисов с владельцами и контрактами (OpenAPI/AsyncAPI).
- Полный граф зависимостей из трассировок (обновлять ежедневно).
- SLO по сервису и «бюджеты латентности» вниз по цепочке.
- Явные таймауты, ретраи с джиттером, брейкеры, bulkhead-изоляция.
- CDC-тесты в CI как релиз-гейт.
- Дашборды per-dependency и карта сервиса.
- Алерты на ребрах + suppression по первопричине.
- Game-days: падение провайдера/кластера/топика и проверка деградаций.
- План деградации: какие фичи отключаем, какие кэши включаем.
- Регулярные постмортемы с действиями по уменьшению связности.
16) KPI качества управления зависимостями
Dependency MTTR: медиана восстановления по ребру.
Blast Radius Index: среднее число затронутых сервисов при падении одного.
Coupling Score: доля sync-зависимостей среди всех; тренд к снижению.
CDC Pass Rate: % релизов без нарушений контрактов.
Retry Storms / месяц: целевое значение → 0.
Cost of External Calls: стоимость внешних вызовов на 1k RPS (видеть эффект кэширования/фоллбеков).
17) Быстрый старт (дефолты)
Таймауты: 70–80% от бюджета звена; верхний таймаут запроса < сумма внутренних.
Ретраи: max 2, только на идемпотентные 5xx/сетевые, с backoff+джиттер.
Брейкер: порог 5% ошибок за 10 с, open=30 с, half-open пробами.
Bulkhead: выделенные пулы/лимиты соединений на каждого даунстрима.
CDC: обязательны для всех публичных API и топиков.
Async-преференс: где можно — переход на события/очереди (развязка по времени).
18) FAQ
Q: Что важнее: ретраи или брейкер?
A: Оба. Ретраи спасают от кратковременных сбоев, брейкер защищает от перманентной деградации и штормов.
Q: Как понять, что связь «слишком хрупкая»?
A: Высокая корреляция ошибок, низкий запас таймаутов, частые ретраи, отсутствие фоллбеков/кэшей, синхронные длинные цепочки.
Q: Зачем CDC, если у нас интеграционные тесты?
A: CDC фиксирует ожидания потребителя и ломает релиз провайдера при несовместимости — раньше, чем код попадет в прод.