An open API service indexing awesome lists of open source software.

https://github.com/addspin/hllb

High load DNS server for load balancing between balancers or other systems
https://github.com/addspin/hllb

balancing checker dns forward high-load upstream

Last synced: 2 months ago
JSON representation

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

Awesome Lists containing this project

README

          

# 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 │ │
│ └────────────┘ └────────────┘ └────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```

### Сборка

```bash
go build -o hllb .
```

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

При первом запуске автоматически создаются отсутствующие файлы:
- `config.yaml` — конфигурация сервера (порт 53, forward на 8.8.8.8)
- `check.yaml` — список хостов для health check
- `zone/example.com` — тестовая зона

```bash
./hllb
```

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

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

#### Linux

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

# Или запуск от root (не рекомендуется)
sudo ./hllb
```

Если порт 53 уже занят `systemd-resolved`:
```bash
sudo ss -tlnp | grep :53
sudo systemctl disable --now systemd-resolved
```

#### macOS

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

Альтернатива — слушать на высоком порту и пробросить через pfctl:
```bash
# В config.yaml: port: 1053
echo "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:
```powershell
netstat -ano | findstr :53
net stop "DNS Client"
```

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

```bash
dig @127.0.0.1 -p 53 example.com A
```

---

## Возможности

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

### Зоны и записи

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

Поддерживаемые типы записей в зонах:
| Тип | Пример в zone-файле | Описание |
|-----|---------------------|----------|
| `A` | `www IN A 10.13.1.34` | IPv4-адрес |
| `NS` | `@ IN NS ns1.test.ru.` | Сервер имён |

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

| Паттерн | Пример запроса | Описание |
|---------|----------------|----------|
| `*` (корень зоны) | `any.test.ru` | Любой поддомен зоны |
| `*.suffix` | `x.info.test.ru` | Любой поддомен для `*.info.test.ru` |
| `*.sub.domain` | `a.b.msg.admin.test.ru` | Любая вложенность субдоменов |
| `exact.domain` | `sub.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`
```yaml
app:
port: 53 # Порт DNS-сервера
checkZoneInterval: 5 # Интервал проверки изменений зон
checkZoneIntervalType: seconds # Единица: seconds / minutes / hours
activeCheck: true # Включить активную проверку хостов
algorithmCheck: RR # Алгоритм балансировки (RR — Round Robin)
repeatCheckInterval: 3 # Интервал TCP-проверки хостов
repeatCheckIntervalType: seconds
repeatCheckFileInterval: 3 # Интервал проверки изменений check.yaml
repeatCheckFileIntervalType: seconds
forward: true # Включить форвардинг для неизвестных зон
forwardDNS: 8.8.8.8 # Адрес upstream DNS
forwardDNSPort: 53 # Порт upstream DNS
```

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

---

## Benchmark

### Стенд

| | Машина | Роль |
|---|--------|------|
| Клиент | Mac mini M2, 16 GB | Генератор нагрузки (dnsperf / resperf) |
| Сервер | MacBook Air M4, 16 GB | hllb на порту 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%) |
| **Пиковый throughput** | **35 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%) |
| **QPS** | **6 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 # Конвертация единиц времени
```