Когда сервисы живут в нескольких средах — часть на VDS, часть в Kubernetes — ручное управление списками хостов в Nginx быстро превращается в боль. Consul даёт DNS-интерфейс над каталогом сервисов и их состоянием, а Nginx умеет динамически резолвить имена и обновлять адреса. В паре «Consul DNS + Nginx» мы получаем чистый и предсказуемый service discovery: единое имя сервиса, балансировку по живым инстансам и failover без ручных правок конфигов и перезапусков.
Зачем это нужно на практике
Consul решает две задачи сразу: хранит каталог сервисов (service discovery) и проверяет их здоровье (health checks). DNS-интерфейс Consul возвращает A/AAAA записи только для «здоровых» инстансов. Nginx, опираясь на DNS, делает балансировку и переживает сбои инстансов без вмешательства администратора. Это особенно полезно в гибридных топологиях, где часть бэкендов крутится в Kubernetes, а часть — на обычных VDS.
Ключевые преимущества связки:
- простое подключение через DNS без шаблонизаторов;
- автоматический failover — нездоровые инстансы исчезают из ответов DNS;
- согласованность — одно имя сервиса для VDS и Kubernetes;
- минимум перезагрузок Nginx — обновления адресов происходят «на лету»;
- отказоустойчивость с помощью prepared queries и политик Consul.
Архитектура: Nginx + Consul DNS
Базовая схема выглядит так:
- Nginx на периметре (часто на VDS) — reverse proxy;
- локальный DNS-резолвер на этом же узле — кеширует и форвардит зону
.consulв Consul DNS; - Consul-клиенты на узлах с бэкендами — регистрируют сервисы и выставляют health checks;
- по запросу к имени вида
api.service.consulConsul отдаёт A/AAAA только живых инстансов; - Nginx резолвит имя с заданным
resolverи направляет трафик на актуальные IP.
Важно: Nginx (open source) не умеет SRV-балансировку с портами. Он работает по A/AAAA. Поэтому либо используйте единый порт для всех инстансов, либо генерируйте upstream-список через шаблонизатор (например, consul-template) — ниже разберём оба подхода.

Быстрый старт на VDS: агент Consul, DNS и health checks
На узлах с приложениями ставим агент Consul в режиме клиента и описываем сервисы. Пример конфигурации агента (HCL/JSON — на ваш вкус). Ниже — JSON с локальным DNS на 127.0.0.1 и коротким TTL для сервисов:
{
"datacenter": "dc1",
"node_name": "edge-proxy-1",
"data_dir": "/var/lib/consul",
"bind_addr": "0.0.0.0",
"client_addr": "127.0.0.1",
"retry_join": ["10.0.0.10", "10.0.0.11"],
"ports": {
"dns": 8600,
"http": 8500
},
"addresses": {
"dns": "127.0.0.1"
},
"dns_config": {
"service_ttl": {"*": "5s"},
"enable_truncate": true
}
}
Пример регистрации сервиса с HTTP-проверкой:
{
"service": {
"name": "api",
"port": 8080,
"tags": ["v1", "blue"],
"checks": [
{
"http": "http://127.0.0.1:8080/health",
"interval": "5s",
"timeout": "2s"
}
]
}
}
Проверки могут быть HTTP, TCP, gRPC, командные (скрипт). Консенсус прост: Consul должен видеть инстанс «passing», иначе адрес не попадёт в DNS-ответ.
Локальный DNS-резолвер для зоны .consul
Nginx нуждается в быстрых и стабильных ответах DNS. Надёжная практика — поднять на узле кеширующий резолвер (dnsmasq или unbound), который отправляет зону .consul в Consul, а прочие домены — к системным рекурсорам. Это снижает нагрузку на Consul и защищает от временных сетевых проблем.
dnsmasq (фрагмент):
no-resolv
cache-size=10000
server=/consul/127.0.0.1#8600
unbound (фрагмент):
server:
cache-min-ttl: 5
cache-max-ttl: 60
qname-minimisation: yes
forward-zone:
name: "consul."
forward-addr: 127.0.0.1@8600
После настройки убедитесь, что на узле корректно резолвятся имена сервисов:
dig @127.0.0.1 -p 53 api.service.consul A +short
Nginx: динамический upstream через DNS
Есть два рабочих паттерна.
1) Upstream с именем хоста и флагом resolve
Подходит, когда у всех инстансов одинаковый порт. Nginx сам будет обновлять пул IP по DNS и балансировать между ними.
resolver 127.0.0.1 valid=5s ipv6=off;
resolver_timeout 2s;
upstream api_dynamic {
zone api_dynamic 64k;
least_conn;
server api.service.consul:8080 resolve;
keepalive 64;
}
server {
listen 80;
server_name example.local;
location /api/ {
proxy_pass http://api_dynamic;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_connect_timeout 2s;
proxy_read_timeout 30s;
proxy_next_upstream error timeout http_502 http_503 http_504;
}
}
Ключевые моменты: resolver указывает локальный кеш, valid=5s ограничивает минимальный TTL, resolve включает динамическое обновление адресов, zone делает пул общим для воркеров и позволяет корректно применять обновления.
2) Переменная в proxy_pass (перезапрос DNS на каждый запрос)
Если нужен максимально свежий DNS на каждый запрос (и нагрузка позволяет), используйте переменную. Nginx будет обращаться к резолверу чаще — закладывайтесь на кеш.
resolver 127.0.0.1 valid=5s ipv6=off;
resolver_timeout 2s;
server {
listen 80;
server_name example.local;
location /api/ {
set $api_endpoint "api.service.consul:8080";
proxy_pass http://$api_endpoint;
proxy_connect_timeout 2s;
proxy_read_timeout 30s;
proxy_next_upstream error timeout http_502 http_503 http_504;
}
}
Без
resolveили переменных Nginx резолвит имя только при старте/перезагрузке. Это частая причина «залипших» адресов в конфиге.
Балансировка и failover
Nginx распределяет трафик по адресу из ответа DNS. Если адресов несколько — он перебирает их и исключает неотвечающие узлы (пасcивное наблюдение). Сочетая это с тем, что Consul DNS отдаёт только «passing» инстансы, получаем простой и эффективный failover.
Рекомендации:
- включайте
least_connилиround_robinпо задаче; - тонко настраивайте таймауты и
proxy_next_upstreamдля быстрых переключений; - при большом числе запросов используйте upstream с
resolveиzone, а не переменные, чтобы снизить давление на DNS.
TTL, кэш и отказ Consul
valid=5s в resolver — компромисс между свежестью и нагрузкой. В Consul можно задать service_ttl — это TTL, который увидит резолвер. При кратковременном отказе Consul Nginx продолжит использовать кеш до истечения valid. Если Consul недоступен дольше, а кеш истёк — возможны ошибки резолвинга. Защитные меры:
- поднимайте локальный кеширующий резолвер;
- используйте
valid=30sили больше на критичных путях; - настройте мониторинг доступности DNS и Consul;
- предусмотрите аварийный стейт — статический upstream с ручным переключением на время инцидента.
Kubernetes + Consul: гибридный каталог сервисов
Если бэкенды живут в Kubernetes и на VDS одновременно, есть два распространённых подхода:
- Синхронизировать сервисы Kubernetes в каталог Consul (оператор/агент синхронизации). Тогда все бэкенды доступны через единообразные имена
*.service.consul. - Регистрировать в Consul только те сервисы из Kubernetes, которые должны быть доступны извне (например, через Service типа LoadBalancer или NodePort), и использовать их имена в Nginx.
Плюс синхронизации — единая точка правды, минус — ещё один компонент. Минимально жизнеспособный вариант — регистрировать в Consul только «edge»-сервисы, которые реально нужны периметру.
Prepared queries и междатацентровый failover
Consul Prepared Queries позволяют реализовать политику выбора ближайшего инстанса и отработку отказа в другой зоне/ДЦ, сохраняя одно имя. Пример подготовленного запроса в JSON:
{
"Name": "web",
"Service": {
"Service": "web",
"OnlyPassing": true,
"Failover": { "NearestN": 1 }
}
}
Такой запрос будет доступен по имени вида web.query.consul. В Nginx достаточно заменить имя сервиса — политика выбора инстанса сместится на сторону Consul. Если вы строите отказоустойчивую СУБД, посмотрите также руководство по HA PostgreSQL с Patroni и DCS.
Health checks: что, где и как проверять
В связке «Consul DNS + Nginx» активные проверки делает Consul, Nginx — пассивные. Это нормально: Consul точнее знает, какие порты/эндпоинты проверять, а Nginx реагирует на сетевые ошибки и коды 502/504.
Какие проверки использовать:
- HTTP/HTTPS с короткими таймаутами и простым ответом (
/healthили/ready); - TCP для неболтливых протоколов;
- gRPC health (если сервис на gRPC);
- скриптовые — для комплексных кейсов.
Пример TCP-проверки в определении сервиса:
{
"service": {
"name": "tcp-app",
"port": 7000,
"checks": [
{ "tcp": "127.0.0.1:7000", "interval": "5s", "timeout": "1s" }
]
}
}
Безопасность: ACL, сети и firewall
Несколько практических правил:
- ограничьте DNS Consul адресом 127.0.0.1 на узле с Nginx, не публикуйте его наружу;
- закройте межузловые порты Consul на границе, оставьте только то, что требуется кластеру;
- включите ACL Consul и используйте токены с минимальными правами для сервис-регистрации;
- в Nginx отключите IPv6 в
resolver, если его нет в инфраструктуре (ipv6=off), чтобы не ждать таймаутов; - версионируйте конфиги и храните шаблоны регистраций сервисов в репозитории;
- для внешних точек входа не забывайте про TLS — оформляйте и обновляйте SSL-сертификаты; для wildcard-доменов пригодится автоматизация DNS-01.
Наблюдаемость и отладка
Быстрый чек-лист диагностики:
dig @127.0.0.1 api.service.consul A +short— адреса есть?dig @127.0.0.1 -p 8600 api.service.consul SRV +short— порты и веса с точки зрения Consul (информативно);nginx -T— убедиться, чтоresolverиresolveне потерялись;- метрики Consul: число passing/critical чеков по сервису;
- логи Nginx на 502/504 и время резолвинга (error_log на info/debug при необходимости).

Паттерны и анти-паттерны
- Паттерн: upstream с
resolveи локальным кеширующим DNS. Минимум запросов, быстрая реакция на изменения. - Паттерн:
prepared queriesдля межДЦ-failover без переписывания конфигов Nginx. - Анти-паттерн: ожидать, что Nginx прочитает SRV-порты. Открытая версия не умеет. Если у инстансов разные порты — используйте консул-шаблоны для генерации списка
server ip:portили унифицируйте порт. - Анти-паттерн: указывать в
resolverвнешние DNS вместо локального кеша. Это повышает латентность и риски отказа. - Анти-паттерн: слишком короткий
validпри большом RPS — получите бурю DNS-запросов. Держите 5–30 секунд и мониторьте. - Анти-паттерн: «забыть»
resolveили переменную вproxy_pass— адреса устареют до следующего reload.
Чек-лист внедрения
- Установите Consul-агенты на узлах с бэкендами. Включите
service_ttlи проверьте статус health checks. - Поднимите на узле с Nginx локальный кеширующий резолвер и форвардинг зоны
.consulв Consul DNS. - Проверьте
digдля имён вида<svc>.service.consul. Убедитесь, что нездоровые инстансы не попадают в A-ответы. - Настройте Nginx:
resolver,upstream ... resolveили переменные вproxy_pass. Включите разумные таймауты иproxy_next_upstream. - Опционально: внедрите
prepared queriesдля кросс-ДЦ failover. - Покройте мониторингом Consul DNS и ошибки 5xx на роутерах Nginx, добавьте алерты на рост timeout и upstream failures.
Итоги
Связка Consul DNS и Nginx даёт управляемый и прозрачный service discovery: Nginx не хранит список инстансов, а только имя; Consul заботится о здоровье и политике выбора. Для гибридных инфраструктур VDS + Kubernetes это простой путь к отказоустойчивости: меньше ручных переключений, быстрее реакции на сбои, единая точка учёта сервисов. Начните с локального кеширующего резолвера, настройте upstream ... resolve, задайте адекватные TTL — и у вас появится гибкий контур балансировки и failover без усложнения инфраструктуры.


