← Назад к вопросам
Какие плюсы и минусы DNS в качестве балансировщика нагрузки?
2.4 Senior🔥 71 комментариев
#DevOps и инфраструктура
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
DNS как балансировщик нагрузки
DNS балансирование — использование Domain Name System для распределения трафика между несколькими серверами.
Как работает DNS балансирование
1. Клиент запрашивает: example.com
2. DNS server возвращает несколько IP адресов:
203.0.113.1
203.0.113.2
203.0.113.3
3. Клиент выбирает один и подключается
Плюсы DNS балансирования
1. Простота и дешевизна
Без DNS балансирования:
┌─────────────────┐
│ Load Balancer │ -- Требует отдельного оборудования
│ (Hardware/SW) │ -- Дорого (или облако решение)
└────────┬────────┘
│
┌────┼────┬──────┐
│ │ │ │
┌───▼──┐ │ ┌──▼──┐ ┌─▼──┐
│Server1│ │ │Server2 │Server3│
└───────┘ │ └──────┘ └──────┘
С DNS балансированием:
┌──────────────────────┐
│ DNS Server │ -- Встроено везде
│ (example.com) │ -- Никаких дополнительных затрат
└──────────────────────┘
│
├──> 203.0.113.1
├──> 203.0.113.2
└──> 203.0.113.3
2. Отсутствие единой точки отказа
# Нет центрального bottleneck
# Если один сервер упадёт, другие остаются доступны
# DNS возвращает все живые записи:
# A record: 203.0.113.1 (Server 1)
# A record: 203.0.113.2 (Server 2)
# A record: 203.0.113.3 (Server 3)
# Клиент выбирает случайно или по порядку
3. Географическое распределение
# GeoDNS — возвращает разные IP на основе геолокации
# Запрос из России:
# answer: 91.108.123.1 (Moscow datacenter)
# Запрос из США:
# answer: 34.117.45.2 (US East datacenter)
# Использование:
# from geoip2 import geolite2
def geo_dns_response(client_ip: str):
response = geolite2.reader().get(client_ip)
location = response['location']
if location['latitude'] > 40: # Northern hemisphere
return '91.108.123.1' # Europe
else:
return '34.117.45.2' # US
4. Поддержка SRV записей
_http._tcp.example.com SRV records:
_http._tcp.example.com 10 60 80 server1.example.com
_http._tcp.example.com 10 40 80 server2.example.com
_http._tcp.example.com 20 60 80 server3.example.com
Priority: 10 (выше) → 20 (ниже)
Weight: распределение среди приоритета
Port: 80
Host: server1.example.com
5. Масштабируемость
Миллионы клиентов одновременно → DNS легко справляется
DNS кэшируется на всех уровнях:
├─ OS кэш
├─ ISP кэш
├─ Router кэш
└─ Application кэш
Не требует центрального load balancer-а
Минусы DNS балансирования
1. Задержка обновления (TTL)
# Если сервер упадёт, клиенты узнают об этом не сразу
# TTL = 300 (5 минут)
Example:
- 12:00:00 → Сервер упадёт
- 12:00:05 → Клиент получает ошибку
- 12:00:10 → Клиент переподключается (но кэш ещё действует!)
- 12:05:00 → TTL истёк, DNS запрашивается заново
- 12:05:05 → Клиент попадает на живой сервер
# Проблема: 5+ минут downtime для некоторых клиентов
import dns.resolver
import time
def check_dns_ttl(domain: str):
answers = dns.resolver.resolve(domain, 'A')
for rdata in answers:
print(f'IP: {rdata}, TTL: {answers.rrset.ttl}')
check_dns_ttl('example.com')
# IP: 203.0.113.1, TTL: 300
2. Отсутствие health checks
# DNS не проверяет живы ли серверы
# Сценарий:
# Server 1: CRASHED ❌
# Server 2: OK ✓
# Server 3: OK ✓
# DNS всё ещё возвращает:
# 203.0.113.1 ← МЁРТВЫЙ СЕРВЕР!
# 203.0.113.2
# 203.0.113.3
# Клиент может попасть на упавший сервер
# и получить Connection refused после 30+ секунд таймаута
# Решение: добавить health checks
def health_check_server(ip: str, port: int = 80, timeout: int = 5):
import socket
try:
sock = socket.create_connection((ip, port), timeout=timeout)
sock.close()
return True
except (socket.timeout, socket.error):
return False
# Но это требует запуска отдельного сервиса!
3. Неравномерное распределение нагрузки
# DNS не учитывает текущую нагрузку на серверах
# Сценарий:
# Server 1: 10k RPS (98% CPU) ← ПЕРЕГРУЖЕН
# Server 2: 100 RPS (1% CPU) ← НЕДОГРУЖЕН
# DNS всё ещё распределяет 33%-33%-33%
# или по round-robin без учёта текущей нагрузки
# Load Balancer может умнее:
from collections import defaultdict
class SmartLB:
def __init__(self, servers):
self.servers = servers
self.current_load = defaultdict(int)
def get_best_server(self):
# Выбирает сервер с минимальной нагрузкой
return min(self.servers, key=lambda s: self.current_load[s])
lb = SmartLB(['server1', 'server2', 'server3'])
# Выберет сервер с наименьшей нагрузкой
4. Различное поведение клиентов
# Разные клиенты обрабатывают DNS по-разному
# Chrome: кэширует DNS 1 минуту
# Firefox: кэширует DNS 60 секунд
# curl: не кэширует (или использует системный DNS)
# Custom app: может кэшировать на часы!
# Результат: очень неравномерное распределение
# Example:
# Клиент A: запросил example.com → получил 203.0.113.1
# кэширует на 1 час → будет использовать 1 час
# Клиент B: запросил example.com → получил 203.0.113.3
# кэширует на 5 минут → будет переопрашивать
# Клиент C: не кэширует → переопрашивает каждый запрос
# может менять IP каждый раз!
5. Проблемы с сессиями
# Если используется session affinity (sticky sessions)
# Сценарий:
# 1. Клиент подключился к Server 1 (получил session cookie)
# 2. Server 1 упал
# 3. DNS возвращает только Server 2 и 3
# 4. Клиент переподключается ко Server 2
# 5. Server 2 не знает о сессии → нужна переаутентификация
# Решение: использовать sticky load balancer с session replication
# Но DNS этого не может!
6. Сложность отладки
Проблема: клиент говорит "ваш сервис медленный"
Вы проверяете: все серверы работают нормально
Причина: у клиента старый кэшированный DNS
который указывает на перегруженный сервер
который был перезагружен 2 часа назад
Отладка:
$ nslookup example.com
$ dig example.com
$ cat /etc/resolv.conf
$ ipconfig /flushdns (Windows)
$ sudo dscacheutil -flushcache (Mac)
7. Отсутствие SSL/TLS termination
# DNS просто возвращает IP адреса
# Криптография должна быть на серверах
# Load Balancer может делать HTTPS offloading:
Client → HTTPS → LB (decrypts) → HTTP → Server
↓
Экономим CPU на серверах!
# DNS не может этого сделать
Когда использовать DNS балансирование
✅ Хорошо для:
- Микросервисов внутри сети (Kubernetes DNS)
- GeoDNS для географического распределения
- Статических сервисов без dynamic scaling
- Дешёвых решений для стартапов
❌ Плохо для:
- Production приложений требующих reliability
- Систем с частыми изменениями (auto-scaling)
- Sessions-зависимых приложений
- Требующих real-time health checks
Гибридный подход
┌────────────────────────────┐
│ GeoDNS │
│ example.com.eu → EU LB │
│ example.com.us → US LB │
└────────────────────────────┘
│
┌────┴────┐
│ │
┌───▼──────┐ ┌─▼─────────┐
│EU LB │ │ US LB │ -- Настоящие load balancers
└──┬─┬─┬──┘ └─┬─┬─┬──────┘
│ │ │ │ │ │
┌──▼▼▼──┐ ┌──▼▼▼──┐
│Servers│ │Servers│ -- Реальные серверы
└───────┘ └───────┘
DNS распределяет между регионами,
Load Balancer внутри региона распределяет между серверами
Практическая реализация
import dns.resolver
import socket
from typing import List
class DNSLoadBalancer:
def __init__(self, domain: str, port: int = 80):
self.domain = domain
self.port = port
self.addresses = []
self.refresh_dns()
def refresh_dns(self):
try:
answers = dns.resolver.resolve(self.domain, 'A')
self.addresses = [str(rdata) for rdata in answers]
except Exception as e:
print(f"DNS error: {e}")
def get_address(self, index: int = None) -> str:
if not self.addresses:
self.refresh_dns()
if index is None:
import random
index = random.randint(0, len(self.addresses) - 1)
return self.addresses[index % len(self.addresses)]
def check_health(self) -> List[str]:
healthy = []
for addr in self.addresses:
try:
sock = socket.create_connection((addr, self.port), timeout=2)
sock.close()
healthy.append(addr)
except (socket.timeout, socket.error):
pass
return healthy
# Использование
lb = DNSLoadBalancer('example.com')
healthy_servers = lb.check_health()
server_to_use = healthy_servers[0]
Вывод: DNS балансирование — бесплатный и простой способ базового распределения нагрузки, но не заменяет полноценный Load Balancer для production систем.