С чего начать Первые шаги трек · 15 ступеней
Linux Labs

Балансировка нагрузки — копии за одним адресом и проверка здоровья

9 мин Ubuntu 24.04 · nginx 1.24 · keepalived 2.2

Балансировщик принимает запросы на одном адресе, раздаёт их между копиями сервиса и убирает больные из ротации.

Шпаргалка

7 команд Версия для печати
  • sudo nginx -T | grep -A4 '^upstream' копии и их параметры
  • tail -n 5 /var/log/nginx/access.log $upstream_addr: кто ответил
  • sudo tail -n 3 /var/log/nginx/error.log почему копия выпала
  • curl -s -o /dev/null -w '%{http_code}\n' 127.0.0.1:8082 копия сама
  • curl -s -o /dev/null -w '%{http_code} %header{x-upstream}\n' localhost
  • sudo nginx -t && sudo systemctl reload nginx применить без простоя
  • ip -br a VIP — второй адрес интерфейса

Обзор

Один сервер — единая точка отказа: упал, и сайт лежит. Балансировщик нагрузки (load balancer) принимает запросы на одном адресе и раздаёт их между несколькими одинаковыми копиями сервиса — бэкендами (upstream). Упала одна копия — запросы идут на остальные, и пользователи этого не видят. На Linux балансировщиком обычно служит nginx или haproxy.

Копии и круговой порядок

nginx
upstream app {
    server 127.0.0.1:8081;
    server 127.0.0.1:8082;
}
server {
    listen 80;
    location / {
        proxy_pass http://app;
        add_header X-Upstream $upstream_addr always;
    }
}

upstream — группа копий под одним именем, proxy_pass шлёт запросы в группу. По умолчанию nginx перебирает копии по кругу (round robin): 8081, 8082, 8081… Другие способы и параметры строки server — в разделе nginx:

Параметр Что делает
least_conn; запрос — копии с меньшим числом соединений
ip_hash; один клиент — всегда одна копия
weight=3 копии достаётся втрое больше запросов
backup копия получает запросы, только когда остальные недоступны
down копия выключена руками

Кто ответил, видно в журнале по $upstream_addr и $upstream_status и в заголовке X-Upstream. Если nginx повторил запрос на другой копии, поля — списки через запятую:

text
… 200 upstream=127.0.0.1:8082, 127.0.0.1:8081 ustatus=502, 200

Первая копия не ответила (502 — отказ в соединении), вторая ответила 200. Пользователь получил 200.

Пассивная проверка здоровья

Балансировщик должен знать, какая копия больна. nginx судит по настоящим запросам — это пассивная проверка здоровья. Неудача запроса к копии — плюс один к счётчику. max_fails неудач за fail_timeout — копия выключена на время fail_timeout. Потом nginx пробует её снова одним запросом.

nginx
server 127.0.0.1:8082 max_fails=2 fail_timeout=10s;

По умолчанию max_fails=1, fail_timeout=10s: после первой неудачи копия отдыхает 10 секунд. max_fails=0 выключает учёт — копия считается живой всегда. Копия, которую починили в эти 10 секунд, получает запросы только после них. Какая неудача считается неудачей, задаёт proxy_next_upstream (ниже).

У пассивной проверки два минуса: за неё платит запрос пользователя, и больную копию она замечает только по трафику.

Повтор на другой копии

proxy_next_upstream — какие ответы считать неудачей и повторять на следующей копии. По умолчанию error timeout: копия не приняла соединение или не ответила вовремя.

Копия Что видит пользователь по умолчанию
не слушает порт (Connection refused) 200: nginx повторил запрос на другой копии
отвечает 500 500 через раз: ответ копии — не неудача
висит без ответа ответ через 60 с: ждёт proxy_read_timeout

Ответы копии с кодами ошибок добавляют в список явно:

nginx
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;

Запрос, который мог что-то изменить (POST), nginx повторно не отправляет, если уже передал его копии: два списания денег хуже одной ошибки. Сколько повторов и сколько секунд на них — proxy_next_upstream_tries и proxy_next_upstream_timeout.

Тайм-ауты

Директива Сколько ждёт По умолчанию
proxy_connect_timeout соединения с копией 60 с
proxy_read_timeout ответа копии между двумя чтениями 60 с
proxy_send_timeout пока копия примет запрос 60 с

Копия, недоступная по сети (пакеты пропадают), и зависшая копия стоят каждому запросу минуту. Короткие тайм-ауты (2–3 с) вместе с timeout в proxy_next_upstream делают из минуты пару секунд и повтор на живой копии. Слишком короткий proxy_read_timeout рвёт медленные, но здоровые ответы: тяжёлые отчёты, выгрузки.

Активная проверка

При активной проверке балансировщик сам опрашивает каждую копию раз в несколько секунд — например, запросом GET /health. Копию выключают после нескольких неудач подряд и включают после нескольких успехов. Больная копия выпадает до того, как к ней придёт пользователь. Так проверяет haproxy (параметр check у строки server); в бесплатном nginx активной проверки нет, только пассивная.

Плавающий адрес

Балансировщик — тоже единая точка отказа. Его делают из двух машин с плавающим адресом (VIP, virtual IP): клиенты ходят на VIP, а он живёт на одной машине — MASTER. keepalived на обеих машинах обменивается объявлениями VRRP. Объявления MASTER пропали — адрес забирает запасная машина (BACKUP), примерно за 3 секунды при advert_int 1. При systemctl stop keepalived MASTER сам объявляет уход, и адрес переезжает сразу.

text
$ ip -br a show ens2
ens2   UP   10.55.90.11/24 10.55.90.100/24 fe80::5054:ff:fe8c:5836/64

Второй адрес на интерфейсе — VIP: эта машина сейчас MASTER. Если машины не слышат объявлений друг друга, адрес оказывается на обеих — раздвоение (split brain), разбор в keepalived.

Рецепты

3

Убрать копию из ротации на время работ

nginx
server 127.0.0.1:8082 down;

sudo nginx -t && sudo systemctl reload nginx — копия больше не получает новых запросов, текущие доходят. Вернуть — убрать down и снова reload. reload не рвёт соединения, restart рвёт (nginx).

Сколько запросов досталось каждой копии

bash
grep -o 'upstream=[^ ]*' /var/log/nginx/lb-access.log | sort | uniq -c

Поле upstream= — подпись $upstream_addr в формате журнала; в своём формате ищи то же поле. Перекос — weight, ip_hash, копия выключена после неудач или помечена backup.

Найти больную копию

bash
for p in 8081 8082; do
  curl -s -o /dev/null -w "$p %{http_code} %{time_total}\n" \
    http://127.0.0.1:$p/
done

Запрос к каждой копии напрямую, мимо балансировщика: код и время ответа. Копия на другой машине — её адрес вместо 127.0.0.1.

Неполадки

5

Каждый второй запрос — ошибка

Одна копия из двух отвечает 500 или 503 сама. Для nginx такой ответ — успешный: копия остаётся в ротации, и через раз пользователь получает ошибку. Почини копию, убери её (down) или добавь коды в proxy_next_upstream (load-balancing). Какая копия отвечает ошибкой — $upstream_addr у строк с 500 в журнале.

Запрос висит минуту, потом ответ

Копия недоступна по сети или зависла, и nginx ждёт proxy_connect_timeout или proxy_read_timeout — по 60 с. В error.log — upstream timed out (110: Connection timed out). Поставь 2–3 секунды (load-balancing).

no live upstreams

text
[error] … no live upstreams while connecting to upstream,
  … upstream: "http://app/"

Все копии группы выключены после неудач — клиент получает 502, в $upstream_addr вместо адреса имя группы (app). Почини хотя бы одну копию: через fail_timeout nginx попробует их снова.

Копию починили, а запросов на неё нет

  • не прошёл fail_timeout после последней неудачи;
  • у строки осталась пометка down или backup;
  • копия указана именем, а адрес сменился: nginx разрешает имена в upstream при загрузке конфига — нужен reload.

В error.log нет строки о выключении копии

upstream server temporarily disabled nginx пишет с уровнем warn, а error_log в Ubuntu ведёт уровень error и выше. Выключение копии видно по Connection refused и upstream timed out в error.log и по спискам в $upstream_addr.

Опасно

2

max_fails=0

Учёт неудач выключен: мёртвая копия остаётся в ротации навсегда. При отказе в соединении это лишний повтор на каждом запросе, при зависшей копии — тайм-аут на каждом её запросе.

Повтор запросов, которые меняют данные

non_idempotent в proxy_next_upstream разрешает повторять POST на другой копии. Если первая копия успела выполнить запрос и упала на ответе, заказ или платёж пройдёт дважды.

Термины

5
балансировщик нагрузки (load balancer)
Принимает запросы на одном адресе и раздаёт их между копиями сервиса, а больные копии убирает из ротации. На Linux это обычно nginx с блоком upstream или HAProxy.
круговой порядок (round robin)
Способ раздачи запросов по очереди: первая копия, вторая, снова первая. Порядок nginx upstream по умолчанию. Ещё встречается в: DNS и разрешение имён, nginx
пассивная проверка здоровья passive health check
Балансировщик судит копию по настоящим запросам: max_fails неудач за fail_timeout — копия выключена на fail_timeout. В nginx по умолчанию 1 неудача и 10 с.
активная проверка здоровья active health check
Балансировщик сам опрашивает каждую копию раз в несколько секунд и выключает её после нескольких неудач подряд — до запросов пользователей. Так проверяет HAProxy (check). Ещё встречается в: HAProxy
единая точка отказа single point of failure
Часть системы, без которой не работает всё: один сервер, один балансировщик, один диск. Её убирают копиями и переключением.

См. также

  • nginx — директивы upstream, server, повторы, тайм-ауты.
  • haproxy — балансировщик с активной проверкой копий.
  • keepalived — плавающий адрес по VRRP.
  • http — коды ответа 5xx.
  • curl — запрос к копии напрямую, -w с кодом и временем.
  • sli-slo — сколько ошибок допускает цель.
  • incident-response — убрать сервер из балансировки как способ восстановления.
Загрузка…