Когда ваш проект растёт, один сервер перестаёт справляться. Даже самый мощный процессор и гигабайты памяти не спасут, если количество запросов растёт лавинообразно. А если этот сервер упадёт — проект умрёт вместе с ним. Решение — балансировка нагрузки и отказоустойчивость.
В этой статье мы подробно разберём:
- Что такое балансировка нагрузки и зачем она нужна.
- Уровни балансировки (L4, L7).
- Алгоритмы распределения трафика.
- Nginx как балансировщик: настройка upstream, health checks, методы распределения.
- HAProxy — более мощный балансировщик.
- Отказоустойчивость: репликация, кластеризация, keepalived, Pacemaker.
- Балансировка баз данных: репликация master-slave, master-master.
- Docker Swarm и Kubernetes для оркестрации.
- Практические примеры и частые ошибки.
1. Что такое балансировка нагрузки
Балансировка нагрузки — это распределение входящего трафика между несколькими серверами, чтобы:
- Увеличить производительность (запросы обрабатываются параллельно).
- Обеспечить отказоустойчивость (если один сервер упал, трафик идёт на другие).
- Упростить масштабирование (можно добавлять серверы по мере роста).
- Проводить обслуживание без простоя (выводить серверы из пула по очереди).
Типы балансировки:
- Аппаратная — специальные устройства (F5, Citrix NetScaler). Дорого, но очень производительно.
- Программная — Nginx, HAProxy, Traefik, Envoy. Гибко, дешево, популярно.
- DNS-балансировка — несколько A-записей для домена. Просто, но нет контроля состояния.
- Облачная — AWS ELB, Yandex Cloud Load Balancer, DigitalOcean Load Balancer.
Мы сосредоточимся на программной балансировке, так как она наиболее доступна и гибка.
2. Уровни балансировки
2.1 L4 (транспортный уровень)
Балансировка на уровне TCP/UDP. Балансировщик видит только IP-адреса и порты, не заглядывая в содержимое пакетов.
Плюсы:
- Очень высокая производительность.
- Не зависит от протокола приложения (HTTP, MySQL, WebSocket).
Минусы:
- Нет возможности маршрутизировать по URL, заголовкам, cookie.
- Нет кеширования, сжатия, SSL-терминации (если не настроено отдельно).
Примеры: HAProxy (в режиме TCP), Nginx (stream), AWS NLB.
2.2 L7 (прикладной уровень)
Балансировка на уровне HTTP/HTTPS. Балансировщик анализирует содержимое запроса: URL, заголовки, cookie.
Плюсы:
- Гибкая маршрутизация (например,
/apiна одни серверы,/staticна другие). - SSL-терминация, кеширование, сжатие.
- Health checks на уровне HTTP.
Минусы:
- Меньше производительность, чем L4.
- Требует больше ресурсов.
Примеры: Nginx, HAProxy (HTTP-режим), Traefik, Envoy, AWS ALB.
Выбор: для веб-приложений обычно используют L7 (Nginx или HAProxy). Для баз данных и других TCP-сервисов — L4.
3. Алгоритмы распределения трафика
| Алгоритм | Описание | Когда использовать |
|---|---|---|
| Round Robin | По очереди: первый запрос — на сервер 1, второй — на сервер 2, и т.д. | Равномерная нагрузка, серверы одинаковой мощности. |
| Weighted Round Robin | То же, но с весами: сервер с весом 3 получает в 3 раза больше запросов. | Серверы разной мощности. |
| Least Connections | Запрос идёт на сервер с наименьшим числом активных соединений. | Долгие соединения, разная нагрузка. |
| Weighted Least Connections | Комбинация весов и наименьших соединений. | Серверы разной мощности + долгие соединения. |
| IP Hash | Сервер выбирается по хешу IP клиента. Один клиент всегда попадает на один сервер. | Нужны «липкие сессии» без cookie. |
| URL Hash | Сервер выбирается по хешу URL. | Кеширование на серверах. |
| Random | Случайный выбор. | Простота, но менее предсказуемо. |
«Липкие сессии» (sticky sessions): когда нужно, чтобы клиент всегда попадал на один и тот же сервер (например, для сессий в памяти). Реализуется через cookie или IP hash.
4. Nginx как балансировщик нагрузки
Nginx — самый популярный инструмент для балансировки. Он может работать как L4 (stream) и L7 (http).
4.1 Базовая настройка upstream
http {
upstream backend {
server backend1.example.com;
server backend2.example.com;
server backend3.example.com;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}4.2 Веса и параметры серверов
upstream backend {
server backend1.example.com weight=3;
server backend2.example.com weight=1;
server backend3.example.com backup; # резервный, включается только если основные недоступны
}Параметры:
weight— вес (по умолчанию 1).max_fails— количество неудачных попыток до пометки сервера как недоступного (по умолчанию 1).fail_timeout— время, на которое сервер исключается (по умолчанию 10 секунд).backup— резервный сервер.down— пометить сервер как выключенный.
4.3 Методы распределения
upstream backend {
least_conn; # Least Connections
# или
ip_hash; # IP Hash
# или
# random; # Random (требует модуль)
server backend1.example.com;
server backend2.example.com;
}4.4 Health checks (активные)
В open-source Nginx активные health checks отсутствуют (только пассивные через max_fails). В Nginx Plus есть активные проверки.
ВАЖНО! В современных сборках (OpenResty, Tengine) есть модуль nginx_upstream_check_module, который как раз и даёт активные проверки. Если нельзя поставить Nginx Plus, можно использовать OpenResty с этим модулем.
Пассивные health checks: если сервер возвращает ошибку (5xx, timeout), он временно исключается.
Активные health checks через сторонние модули:
nginx_upstream_check_module(Tengine, openresty).- Или использовать HAProxy, у которого активные проверки встроены.
4.5 Кеширование и сжатие на балансировщике
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m;
server {
location / {
proxy_cache my_cache;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_pass http://backend;
}
}4.6 SSL-терминация
Nginx может принимать HTTPS и передавать трафик на бэкенды по HTTP (внутри доверенной сети).
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
location / {
proxy_pass http://backend;
proxy_set_header X-Forwarded-Proto https;
}
}4.7 L4-балансировка (stream)
stream {
upstream mysql_backend {
server db1.example.com:3306;
server db2.example.com:3306;
}
server {
listen 3306;
proxy_pass mysql_backend;
}
}5. HAProxy — мощный балансировщик
HAProxy — это специализированный балансировщик, который часто используется для высоконагруженных систем. Он поддерживает L4 и L7, активные health checks, подробную статистику.
5.1 Установка
sudo apt install haproxy5.2 Базовая конфигурация
Файл /etc/haproxy/haproxy.cfg:
global
log /dev/log local0
maxconn 4096
user haproxy
group haproxy
daemon
defaults
log global
mode http
option httplog
option dontlognull
retries 3
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
frontend http_front
bind *:80
default_backend http_back
backend http_back
balance roundrobin
http-check expect status 200
server web1 192.168.1.10:80 check
server web2 192.168.1.11:80 check
server web3 192.168.1.12:80 check backupОбъяснение:
frontend— точка входа (порт 80).backend— пул серверов.balance roundrobin— алгоритм.option httpchk GET /health— активная проверка: HAProxy отправляет GET /health каждые несколько секунд.check— включить проверку.backup— резервный сервер.
5.3 Статистика
HAProxy имеет встроенную страницу статистики.
listen stats
bind *:8404
stats enable
stats uri /stats
stats refresh 10s
stats admin if LOCALHOSTТеперь http://your-server:8404/stats покажет состояние всех серверов, количество запросов, ошибки и т.д.
5.4 HAProxy vs Nginx
| Критерий | Nginx | HAProxy |
|---|---|---|
| Основное назначение | Веб-сервер + балансировщик | Специализированный балансировщик |
| L4/L7 | Да | Да |
| Активные health checks | Нет (в open-source) | Да |
| Статистика | Базовая | Подробная |
| Производительность | Очень высокая | Очень высокая |
| Гибкость настройки | Высокая | Очень высокая |
| Кеширование | Да | Нет (только балансировка) |
Выбор: если нужен только балансировщик с активными проверками — HAProxy. Если совмещать с веб-сервером — Nginx.
6. Отказоустойчивость
Балансировка — это половина дела. Вторая половина — сделать так, чтобы отказ сервера не приводил к падению всего проекта.
6.1 Резервирование балансировщика (keepalived)
Если у вас один балансировщик, он становится единой точкой отказа. Решение — два балансировщика с виртуальным IP (VIP), который переключается при отказе.
keepalived реализует протокол VRRP: один сервер — мастер, второй — бэкup. Если мастер падает, backup забирает VIP.
Установка:
sudo apt install keepalivedКонфигурация на мастере (/etc/keepalived/keepalived.conf):
vrrp_instance VI_1 {
state MASTER
interface eth0
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass secret
}
virtual_ipaddress {
192.168.1.100/24
}
}На бэкенде:
vrrp_instance VI_1 {
state BACKUP
interface eth0
virtual_router_id 51
priority 90
advert_int 1
authentication {
auth_type PASS
auth_pass secret
}
virtual_ipaddress {
192.168.1.100/24
}
}Теперь 192.168.1.100 — это VIP, который всегда указывает на активный балансировщик. Если мастер падает, backup забирает VIP и продолжает работу.
6.2 Кластеризация баз данных
Репликация master-slave:
- Один сервер — мастер (принимает запись и чтение).
- Один или несколько слейвов (только чтение).
- Если мастер падает, слейв можно повысить до мастера.
MySQL репликация:
На мастере в my.cnf:
ini
[mysqld]
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_do_db = mydbСоздание пользователя для репликации:
CREATE USER 'repl'@'%' IDENTIFIED BY 'password';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;На слейве:
CHANGE MASTER TO
MASTER_HOST='master_ip',
MASTER_USER='repl',
MASTER_PASSWORD='password',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=123;
START SLAVE; <---- ВАЖНО! Синтаксис зависит от версииPostgreSQL репликация:
На мастере в postgresql.conf:
text
wal_level = replica
max_wal_senders = 3На слейве:
pg_basebackup -h master_ip -D /var/lib/postgresql/15/main -U replicator -P -vMaster-master репликация: оба сервера принимают запись, но требует разрешения конфликтов. Сложнее и используется реже. ВАЖНО! На реплике ещё нужен recovery.conf (до 12 версии) или primary_conninfo в postgresql.auto.conf (12+).
6.3 Кластеризация приложений
Приложения должны быть stateless (не хранить состояние на сервере). Сессии — в Redis или БД. Файлы — в S3 или общем хранилище. Тогда можно запускать сколько угодно серверов, и любой из них обработает любой запрос.
6.4 Docker Swarm
Docker Swarm — встроенная оркестрация контейнеров.
# Инициализация кластера
docker swarm init
# Добавление рабочих узлов
docker swarm join --token <token> <manager-ip>:2377
# Развёртывание стека
docker stack deploy -c docker-compose.yml myapp
# Масштабирование сервиса
docker service scale myapp_web=5Swarm автоматически распределяет контейнеры по узлам, перезапускает упавшие, балансирует нагрузку.
6.5 Kubernetes
Kubernetes — более мощный оркестратор. Он умеет:
- Автоматически масштабировать поды (Horizontal Pod Autoscaler).
- Самовосстанавливаться (перезапускать упавшие поды).
- Балансировать нагрузку через Service.
- Управлять конфигурациями и секретами.
Пример Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: myapp:latest
ports:
- containerPort: 80Service для балансировки:
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80
type: LoadBalancerKubernetes сам создаст балансировщик и распределит трафик между подами.
7. Практические примеры
7.1 Схема отказоустойчивого веб-проекта
┌─────────────────┐
│ DNS (A-запись)│
└────────┬────────┘
│
┌────────▼────────┐
│ Keepalived VIP │
│ 192.168.1.100 │
└────────┬────────┘
│
┌──────────────┴──────────────┐
│ │
┌────────▼────────┐ ┌────────▼────────┐
│ Nginx (master) │ │ Nginx (backup) │
│ 192.168.1.10 │ │ 192.168.1.11 │
└────────┬────────┘ └────────┬────────┘
│ │
└──────────────┬──────────────┘
│
┌──────────────┴──────────────┐
│ │
┌────────▼────────┐ ┌────────▼────────┐
│ App Server 1 │ │ App Server 2 │
│ 192.168.1.20 │ │ 192.168.1.21 │
└────────┬────────┘ └────────┬────────┘
│ │
└──────────────┬──────────────┘
│
┌────────▼────────┐
│ MySQL Master │
│ 192.168.1.30 │
└────────┬────────┘
│
┌────────▼────────┐
│ MySQL Slave │
│ 192.168.1.31 │
└─────────────────┘ВАЖНО! MHA — это один из вариантов, а ещё бывают Orchestrator, Patroni (для PostgreSQL) и т.п.
Как это работает:
- DNS указывает на VIP (192.168.1.100).
- Keepalived направляет трафик на активный Nginx.
- Nginx балансирует запросы между App Server 1 и 2.
- App Server’ы читают данные с MySQL Slave, пишут в MySQL Master.
- Если Master падает, Slave повышается до Master (вручную или через MHA).
- Если Nginx Master падает, backup забирает VIP.
7.2 Настройка health check для приложения
Приложение должно иметь эндпоинт /health, который возвращает 200 OK, если всё хорошо.
<?php
// health.php
$host = getenv('DB_HOST') ?: 'localhost';
$db = getenv('DB_NAME') ?: 'mydb';
$user = getenv('DB_USER') ?: 'user';
$pass = getenv('DB_PASS') ?: 'REPLACE_WITH_PASSWORD';
try {
$dsn = "mysql:host=$host;dbname=$db;charset=utf8mb4";
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_TIMEOUT => 2,
]);
$pdo->query('SELECT 1');
http_response_code(200);
echo json_encode(['status' => 'ok']);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
Балансировщик будет проверять этот эндпоинт и исключать серверы, которые возвращают ошибку.
7.3 Автоматическое масштабирование в Kubernetes
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70ВАЖНО! Для работы метрик в кластере должен быть установлен Metrics Server.
8. Частые ошибки и лучшие практики
| Ошибка | Последствия | Решение |
|---|---|---|
| Один балансировщик | Единая точка отказа | Используйте keepalived + два балансировщика. |
| Нет health checks | Трафик идёт на упавшие серверы | Настройте активные или пассивные проверки. |
| Сессии в памяти приложения | Пользователь «теряет» сессию | Храните сессии в Redis или БД. |
| Файлы на локальном диске | При переключении сервера файлы недоступны | Используйте S3 или общее хранилище. |
| Нет репликации БД | Потеря данных при отказе | Настройте master-slave репликацию. |
| Игнорирование мониторинга | Проблемы остаются незамеченными | Мониторьте балансировщик, серверы, БД. |
| Нет плана восстановления | Простой затягивается | Документируйте процедуры восстановления. |
Лучшие практики:
- Делайте приложения stateless.
- Используйте несколько зон доступности (если облако).
- Регулярно тестируйте отказоустойчивость (отключайте серверы, смотрите, что происходит).
- Автоматизируйте развёртывание и масштабирование.
- Мониторьте всё: балансировщик, серверы, БД, сеть.
- Документируйте архитектуру и процедуры.
