Service Discovery и DNS
Service Discovery и DNS
1) Зачем это нужно
В распределенных системах узлы появляются и исчезают, а клиенты должны находить рабочие экземпляры сервиса быстро и надежно. DNS — универсальный слой имен; service discovery — стратегия сопоставления имени сервиса реальным эндпойнтам с учетом здоровья, веса и политики маршрутизации.
Ключевые цели:- стабильные имена вместо эфемерных адресов,
- точный, но не шумный апдейт (баланс между свежестью и TTL),
- деградация без полного падения (failover/health-check),
- минимум «догадок» на клиенте: таймауты, ретраи, кэш-политики.
2) Модели service discovery
2.1 Клиентская (client-side)
Клиент сам разрешает имя в набор эндпойнтов и балансирует (round-robin, EWMA, хеш по ключу). Источник — DNS (A/AAAA/SRV), сервис-реестр (Consul/Eureka), статический список.
Плюсы: меньше центральных SPOF, гибкие алгоритмы.
Минусы: неоднородность клиентов, сложнее обновлять логику.
2.2 Серверная (server-side)
Клиент идет на front/LB (L4/L7, gateway/ingress). Балансировка и health-checking — на стороне прокси/балансировщика.
Плюсы: единое место политики, наблюдаемость.
Минусы: нужен высокодоступный периметр (N+1, multi-AZ).
2.3 Гибрид
DNS дает набор точек входа (региональные LB), дальше — балансировка на L7/mesh.
3) DNS: основы, записи и TTL
3.1 Базовые типы
A/AAAA — IPv4/IPv6 адреса.
CNAME — алиас на другое имя (не на apex).
SRV — `_service._proto.name` → хост/порт/вес/приоритет (для gRPC/LDAP/SIP и пр.).
TXT/HTTP/HTTPS — метаданные/указатели (в т.ч. для HTTP-discovery).
NS/SOA — делегирование и атрибуты зоны.
3.2 TTL и кэш-каскад
Кэш есть у: резолвера ОС, локального stub-резолвера, узлов (NodeLocal DNS/CoreDNS), провайдера, промежуточных рекурсоров и у клиента библиотеки. Фактическая свежесть = min(TTL, политика клиента). Негативный кэш (NXDOMAIN) тоже кэшируется по `SOA.MINIMUM`/`TTL`.
Рекомендации:- Прод — TTL 30–120s для динамичных записей, 300–600s для стабильных.
- Для переключений (фейловер) готовьте пониженный TTL заранее, а не «во время пожара».
- Учитывайте sticky-кэш библиотек (Java/Go/Node) — при необходимости настраивайте TTL резолвера внутри рантайма.
4) Политики DNS-балансировки и отказоустойчивости
Weighted RR — веса на A/AAAA/SRV.
Failover — первичный/вторичный наборы (health-check снаружи).
Geo/Latency — ответ на «ближайшую» POP/регион.
Anycast — один IP в разных POP (BGP); устойчиво к региональным сбоям.
Split-horizon — разные ответы внутри VPC/он-прем и в интернете.
GSLB — глобальный балансировщик с health-проверками и политиками (latency, гео, capacity).
5) Health-checks и свежесть
DNS сам по себе «тупой»: он не знает здоровья бэкендов. Поэтому:- Либо внешний health-checker управляет записями/весами (GSLB, Route53/Traffic-policy, external-dns + пробы).
- Либо клиент/mesh делает активный outlier-ejection и retry из множества эндпойнтов.
6) Kubernetes: discovery из коробки
Сервисные имена: `svc.namespace.svc.cluster.local`.
ClusterIP: стабильный виртуальный IP + kube-proxy/ebpf.
Headless Service (`clusterIP: None`): отдает A-записи на pod’ы (или их поддомен), SRV для портов.
EndpointSlice: масштабируемый список эндпойнтов (замена Endpoints).
CoreDNS: DNS-резолвер кластера; плагины rewrite/template/forward/cache; `kube-dns` зону.
NodeLocal DNSCache: локальный кэш на узле → меньше latency и перехват проблем апстрим-резолвера.
Пример: Headless + SRV
yaml apiVersion: v1 kind: Service metadata: { name: payments, namespace: prod }
spec:
clusterIP: None selector: { app: payments }
ports:
- name: grpc port: 50051 targetPort: 50051
Клиент может резолвить `_grpc._tcp.payments.prod.svc.cluster.local` (SRV) и получить хост/порт/веса.
CoreDNS (фрагмент ConfigMap)
yaml apiVersion: v1 kind: ConfigMap metadata: { name: coredns, namespace: kube-system }
data:
Corefile:
.:53 {
errors health ready cache 30 loop forward. /etc/resolv. conf prometheus:9153 reload
}
NodeLocal DNS (идеи):
- DaemonSet с локальным резолвером на `169.254.20.10`; kubelet указывает эту точку.
- Снижает p99 name-resolution и защищает от «флапа» апстрим-DNS.
7) Service discovery вне K8s
Consul: агент, health-checks, сервис-каталог, DNS-интерфейс (`.consul`), KV для конфигов.
Eureka/ZooKeeper/etcd: реестры для JVM/legacy; часто в связке с sidecar/шлюзом.
Envoy/Istio: EDS/xDS (Endpoint Discovery) и SDS (секреты); сервисы объявляются через control-plane.
8) Безопасность DNS
DNSSEC: защита целостности записей (подпись зон). Критично для публичных доменов.
DoT/DoH: шифрование канала к рекурсору (внутренние политики, совместимость).
ACL и split-horizon: приватная зона — только из VPC/VPN.
Защита от кеш-отравления: рандомизация портов/ID, короткие TTL для динамики.
Политики на egress: разрешайте DNS только на доверенные резолверы, журналируйте.
9) Поведение клиентов и ретраи
Уважайте TTL: не кэшируйте бесконечно, не «беспределите» частыми резолвами (шторм к рекурсору).
Happy Eyeballs (IPv4/IPv6), параллельные коннекты к нескольким A/AAAA уменьшают tail.
Ретраи только при идемпотентных запросах; джиттер, ограничение budget ретраев.
- Java: `networkaddress.cache.ttl`, `networkaddress.cache.negative.ttl`.
- Go: `GODEBUG=netdns=go`/`cgo`, `Resolver.PreferGo`, `DialTimeout`.
- Node: `dns.setDefaultResultOrder('ipv4first')`, `lookup` с `all:true`.
10) GSLB/DNS-переключения: практика
Снизьте TTL с 300→60 за 24–48 часов до планового переключения.
Держите канареечный набор эндпойнтов с малым весом для валидации.
Применяйте weighted + health-check вместо ручного массового апдейта A-записей.
Для статики/edge — Anycast; для API — Geo/Latency + быстрый L7-фейловер.
11) Наблюдаемость и SLO для имени
Метрики:- Rate/latency DNS-запросов, cache hit-ratio, ошибки по типам (SERVFAIL/NXDOMAIN).
- Доля запросов с stale-ответами (если используете stale-cache).
- Успех пользовательских операций при сменах записей (бизнес-SLI).
- p95/p99 resolve-time в приложениях.
- Расслойте путь: клиент → локальный кэш → нодовый кэш → кластерный резолвер → рекурсор провайдера.
- Отслеживайте всплески NXDOMAIN (ошибки имен/опечатки) и SERVFAIL (проблемы рекурсора/ресурс-лимиты).
12) Примеры конфигураций
CoreDNS: rewrite и stub-зона
yaml
.:53 {
log errors cache 60 rewrite name suffix. svc. cluster. local. svc. cluster. local forward. 10. 0. 0. 2 10. 0. 0. 3
}
example. internal:53 {
file /zones/example. internal. signed dnssec
}
systemd-resolved (форс локального резолвера)
ini
[Resolve]
DNS=169. 254. 20. 10
FallbackDNS=1. 1. 1. 1 8. 8. 8. 8
Domains=~cluster. local ~internal
DNSSEC=yes
Envoy: динамический DNS-refresh
yaml dns_refresh_rate: 5s dns_failure_refresh_rate:
base_interval: 2s max_interval: 30s respect_dns_ttl: true
external-dns (поддержка публичной зоны)
yaml args:
- --source=service
- --source=ingress
- --domain-filter=example. com
- --policy=upsert-only
- --txt-owner-id=cluster-prod
13) Чек-лист внедрения (0–30 дней)
0–7 дней
Каталог имен сервисов, выбор модели (client-/server-side/гибрид).
Базовые TTL, включить NodeLocal DNSCache, дашборды DNS-метрик.
Запрет «жестких IP» в конфиге/коде.
8–20 дней
Headless-сервисы + SRV для gRPC; EndpointSlice включен.
GSLB/weighted для внешних; health-checks и канарейка.
Настроены таймауты/ретраи клиентов и бюджет ретраев.
21–30 дней
Split-horizon и приватные зоны; DoT/DoH по политике.
Тест переключений (по TTL) и фейловера; пост-анализ.
Политики mesh/EDS, outlier-ejection включены.
14) Анти-паттерны
TTL = 0 в проде → шторм к рекурсорам, непредсказуемые задержки.
Хардкод IP/портов, отсутствие CNAME/алиасов для уровней.
Смена записей «вручную» без health-checks и канареек.
Один глобальный резолвер без кэша на узлах (узкое место).
Игнорирование негативного кэша (всплески NXDOMAIN).
Попытки «лечить» отказ БД через DNS вместо уровня данных/фейловера.
15) Метрики зрелости
100% сервисов используют имена; нулевые случаи hard-IP.
CoreDNS/NodeLocal в проде, cache hit-ratio > 90% на узлах.
GSLB с health-checks, документированные TTL и runbook переключений.
SRV/EndpointSlice для stateful/gRPC, p99 resolve-time в приложениях ≤ 20–30 мс.
Алерты по SERVFAIL/NXDOMAIN и деградации cache hit-ratio.
Проверки в CI: запрет `:latest` и hard-IP в чартах/конфигах.
16) Заключение
Service discovery — это договор о стабильном имени и дисциплина кэша. Стройте гибридную модель: DNS дает быстрый и простой вход, L7/mesh — здоровье и умные политики. Поддерживайте разумные TTL, кэш на узлах, headless-сервисы и SRV там, где нужно, используйте GSLB/Anycast для границ регионов, следите за NXDOMAIN/SERVFAIL и p99 resolve-time. Тогда ваше имя будет таким же надежным активом, как и сам сервис.