Skip to content

Repository files navigation

HLLB - High load DNS сервер с проверкой хостов для балансировки нагрузки

Быстрый старт

Для балансировки нагрузки:

  • Добавьте несколько одинаковых "A" записей в зоне с разными ip адресами.
  • В файле check.yaml добавьте список этих ip для проверки.
  • Запустите сервер.
  • curl на запись к которой вы закрепили ip ареса в зоне будет чередовать выдачу ip адреса из списка check.yaml.

Некторые варианты работы

┌──────────────────────────────────────────────────────────────────┐
│ ИНТЕРНЕТ │
│ │
│ Пользователь ──────▶ DNS Провайдер │
│ │ │ │
│ │ │ NS: ns1.your-domain.com. │
│ │ │ → 203.0.113.1 (hllb) │
│ │ ▼ │
│ │ ┌─────────────────┐ │
│ │ │ Ваш hllb │ │
│ │ │ 203.0.113.1 │ │
│ │ │ │ │
│ │ │ check.yaml: │ │
│ │ │ - 198.51.100.1 │ ← внешний IP │
│ │ │ - 198.51.100.2 │ ← внешний IP │
│ │ │ - 198.51.100.3 │ ← внешний IP │
│ │ │ │ │
│ │ │ zone/your-domain.com: │
│ │ │ lb A 198.51.100.1 │
│ │ │ lb A 198.51.100.2 │
│ │ │ lb A 198.51.100.3 │
│ │ └─────────────────┘ │
│ │ │ │
│ │ │ Health Check (TCP) │
│ │ │ по внешним IP │
│ ▼ ▼ │
│ ┌──────────────────────────────────┐ │
│ │ Firewall / NAT Gateway │ │
│ │ (прозрачно для hllb) │ │
│ │ 198.51.100.1:443 → 10.0.1.1:80 │ │
│ │ 198.51.100.2:443 → 10.0.1.2:80 │ │
│ │ 198.51.100.3:443 → 10.0.1.3:80 │ │
│ └──────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Закрытый контур (VPN) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Клиент #1 │ │ Клиент #2 │ │ Клиент #3 │ │
│ │ 10.100.1.10 │ │ 10.100.1.11 │ │ 10.100.1.12 │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └──────────────────┼──────────────────┘ │
│ │ │
│ │ DNS запрос (внутри VPN) │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Ваш hllb │ │
│ │ 10.100.1.1 │ │
│ │ :53 (DNS) │ │
│ │ │ │
│ │ check.yaml: │ │
│ │ - 10.200.1.1 │ │
│ │ - 10.200.1.2 │ │
│ │ - 10.200.1.3 │ │
│ │ port: 80 │ │
│ │ │ │
│ │ zone/internal: │ │
│ │ lb A 10.200.1.1 │
│ │ lb A 10.200.1.2 │
│ │ lb A 10.200.1.3 │
│ └────────┬────────┘ │
│ │ │
│ │ Health Check (TCP) │
│ │ по внутренним IP │
│ ▼ │
│ ┌─────────────────┼─────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │Balancer #1 │ │Balancer #2 │ │Balancer #3 │ │
│ │10.200.1.1 │ │10.200.1.2 │ │10.200.1.3 │ │
│ │ :80 │ │ :80 │ │ :80 │ │
│ └────────────┘ └────────────┘ └────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘

Сборка

go build -o hllb .

Первый запуск

При первом запуске автоматически создаются отсутствующие файлы:

  • config.yaml — конфигурация сервера (порт 53, forward на 8.8.8.8)
  • check.yaml — список хостов для health check
  • zone/example.com — тестовая зона
./hllb

Запуск на порту 53

Порт 53 — привилегированный (< 1024), для работы на нём требуются дополнительные права.

Linux

# Рекомендуемый способ — дать бинарнику право на привилегированные порты
sudo setcap 'cap_net_bind_service=+ep' ./hllb
./hllb
# Или запуск от root (не рекомендуется)
sudo ./hllb

Если порт 53 уже занят systemd-resolved:

sudo ss -tlnp | grep :53
sudo systemctl disable --now systemd-resolved

macOS

# Запуск от root
sudo ./hllb

Альтернатива — слушать на высоком порту и пробросить через pfctl:

# В config.yaml: port: 1053echo"rdr pass on lo0 inet proto {tcp, udp} from any to 127.0.0.1 port 53 -> 127.0.0.1 port 1053"| sudo pfctl -ef -

Windows

Порты ниже 1024 в Windows не привилегированные — дополнительных прав не требуется. Убедитесь, что порт 53 не занят службой DNS Client:

netstat -ano | findstr :53
net stop "DNS Client"

Проверка работы

dig @127.0.0.1 -p 53 example.com A

Возможности

DNS-сервер

  • Слушает входящие запросы одновременно по UDP и TCP на настраиваемом порту
  • Обрабатывает типы запросов A и NS
  • Флаг Authoritative устанавливается только для ответов из собственных зон
  • Форвардинг — запросы, не найденные в зонах, пересылаются на внешний DNS (настраивается в config.yaml)

Зоны и записи

Файлы зон хранятся в директории ./zone/. Имя файла — имя зоны (например test.ru), оно же становится ORIGIN — корневым доменом, подставляемым к относительным записям внутри файла.

Поддерживаемые типы записей в зонах:

ТипПример в zone-файлеОписание
Awww IN A 10.13.1.34IPv4-адрес
NS@ IN NS ns1.test.ru.Сервер имён

Wildcard-матчинг (три уровня)

ПаттернПример запросаОписание
* (корень зоны)any.test.ruЛюбой поддомен зоны
*.suffixx.info.test.ruЛюбой поддомен для *.info.test.ru
*.sub.domaina.b.msg.admin.test.ruЛюбая вложенность субдоменов
exact.domainsub.admin.test.ruТочное совпадение записи

Порядок приоритета обработки запроса:

  1. Точное совпадение
  2. Wildcard-записи зоны (*.suffix)
  3. Wildcard-фолбэк (*.zone)
  4. Форвардинг на внешний DNS (если forward: true)
  5. NXDOMAIN

Горячая перезагрузка зон

  • Каждый zone-файл отслеживается в отдельной горутине
  • Изменение файла обнаруживается через SHA-256 хеш
  • При изменении — зона атомарно перезагружается без перезапуска сервера
  • Интервал проверки настраивается: checkZoneInterval + checkZoneIntervalType

Активная проверка хостов (Health Check)

Включается параметром activeCheck: true в config.yaml.

  • Выполняет TCP-проверку порта для списка хостов из check.yaml
  • При изменении - список хостов для провери и порт перезагружаются check.yaml (также через SHA-256)
  • Алгоритм балансировки Round Robin применяется только к записям, чьи IP находятся в пуле check.yaml
  • Записи, не относящиеся к пулу, отдаются напрямую из зоны
  • Интервалы настраиваются отдельно: repeatCheckInterval и repeatCheckFileInterval

Форвардинг

Включается параметром forward: true в config.yaml.

  • Запросы, не найденные ни в одной зоне, пересылаются на указанный внешний DNS-сервер
  • Настраивается адрес (forwardDNS) и порт (forwardDNSPort)

Конфигурация

config.yaml

app:
port: 53# Порт DNS-сервераcheckZoneInterval: 5# Интервал проверки изменений зонcheckZoneIntervalType: seconds # Единица: seconds / minutes / hoursactiveCheck: true # Включить активную проверку хостовalgorithmCheck: RR # Алгоритм балансировки (RR — Round Robin)repeatCheckInterval: 3# Интервал TCP-проверки хостовrepeatCheckIntervalType: secondsrepeatCheckFileInterval: 3# Интервал проверки изменений check.yamlrepeatCheckFileIntervalType: secondsforward: true # Включить форвардинг для неизвестных зонforwardDNS: 8.8.8.8 # Адрес upstream DNSforwardDNSPort: 53# Порт upstream DNS

check.yaml

hostCheck:
- 10.13.1.36 # IP хоста (должен совпадать с A-записью в зоне)
- 176.125.254.184portCheck: 22# TCP-порт для проверки

Benchmark

Стенд

МашинаРоль
КлиентMac mini M2, 16 GBГенератор нагрузки (dnsperf / resperf)
СерверMacBook Air M4, 16 GBhllb на порту 1053
СетьWi-FiБез оптимизации сетевого стека

resperf (поиск точки насыщения)

resperf -P 20260325-1333.gnuplot -s 10.13.1.18 -p 1053 -d test.info -R -C 10
МетрикаЗначение
Запросов отправлено673 250
Запросов завершено608 827 (90.4%)
Потеряно64 423 (9.6%)
Пиковый throughput35 790 qps
Потери на пике16.61%

dnsperf (стабильная нагрузка)

dnsperf -s 10.13.1.18 -m udp -p 1053 -d test.info -c 10 -l 30
МетрикаЗначение
Запросов отправлено202 632
Запросов завершено202 632 (100%)
Потеряно0 (0%)
QPS6 752
Средняя латенция14.1 ms
Мин / Макс латенция3.1 ms / 145 ms

Выводы

  • При стабильной нагрузке (10 клиентов) — 0% потерь, ~6 750 qps
  • Пиковый throughput по resperf — ~35 800 qps (с потерями 16%)
  • Предположительно Узкое место — UDP syscall ядра macOS (если опираться на pprof)

Структура проекта

.
├── main.go # Точка входа, запуск DNS-сервера
├── config.yaml # Основная конфигурация
├── check.yaml # Список хостов для health check
├── zone/ # DNS zone-файлы (стандартный формат RFC 1035)
│ └── test.ru
├── algorithm/
│ └── rr.go # Round Robin балансировка
├── checks/
│ └── tcpCheck.go # TCP health check
├── handles/
│ ├── handleDNS.go # Обработчик DNS-запросов
│ └── forwardDNS.go # Форвардинг на upstream DNS
└── utils/
├── initFiles.go # Создание файлов/папок при первом запуске
├── zoneParser.go # Парсинг zone-файлов
├── watchZoneFile.go # Слежение за изменениями zone-файлов
├── watchCheckFile.go # Слежение за изменениями check.yaml
├── hashFile.go # SHA-256 хеширование файлов
├── readConfig.go # Чтение и кэширование config.yaml
├── readCheck.go # Чтение check.yaml
└── selectTime.go # Конвертация единиц времени

About

High load DNS server for load balancing between balancers or other systems

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages