Перейти до вмісту
EuroVPSEuroVPS

Як зробити перезапуск backend непомітним для ваших відвідувачів

Два вебсервери все одно показують помилки, коли один перезапускається. Налаштування HAProxy, Varnish і keepalived, що роблять перезапуск непомітним, і як це протестувати.

Автор EuroVPS Engineering

5 хв читання
Як зробити перезапуск backend непомітним для ваших відвідувачів

Два вебсервери за балансувальником не означають, що перезапуск непомітний. Вони означають, що перезапуск дає коротку чергу помилок замість збою. Різниця між «коротка» і «жодної» живе у кількох налаштуваннях на edge і в тому, щоб протестувати їх реальним трафіком, перш ніж на них покладатися. Ось як ми закриваємо цей розрив.

Чому саме по собі резервування все одно показує помилки

Коли PHP-FPM або nginx перезапускається на одному вебвузлі, за ту секунду чи дві, що це триває, стаються три речі. Запити, що вже виконуються на цьому вузлі, обриваються. Нові запити продовжують надходити, бо балансувальник ще не помітив, що вузол зник. А коли вузол повертається, йому можуть віддати повну частку трафіку до того, як його кеші та opcode cache прогріються.

Балансувальник з типовими налаштуваннями не впорається елегантно з жодною з них. Його перевірка стану виконується кожні кілька секунд і потребує кількох невдач, перш ніж позначить вузол як недоступний, тож у цьому вікні він свідомо відправляє відвідувачів на сервер, який не відповідає. Кожен з цих запитів стає 502 або 503 на чиємусь екрані.

Повтор і redispatch на edge

Перше виправлення: дозволити HAProxy спробувати знову, деінде, коли з’єднання не вдається:

defaults
    mode http
    retries 3
    option redispatch 1
    retry-on conn-failure empty-response response-timeout
    timeout connect 3s

retries дозволяє HAProxy повторити запит, з’єднання якого не вдалося. option redispatch 1 змушує повтор іти на інший сервер замість того, щоб довбати той, що щойно відмовив. retry-on розширює повтори за межі відхилених з’єднань на порожні відповіді та тайм-аути, тобто саме на те, що часто видає процес посеред перезапуску.

Повтори безпечні для запитів, які не були оброблені. Для запитів POST, які могли дійти до застосунку, HAProxy типово консервативний, і саме цього ви хочете: оформлення замовлення ніколи не має бути надіслане двічі.

Перевірки стану, що бачать застосунок, а не порт

Перевірка TCP проходить, поки nginx слухає, навіть коли кожен worker PHP заклинило. Перевіряйте щось, що задіює стек:

backend be_web
    option httpchk GET /healthz
    http-check expect status 200
    default-server inter 2s fall 3 rise 2 slowstart 20s
    server web1 <web1>:80 check
    server web2 <web2>:80 check

Endpoint /healthz має бути крихітним скриптом PHP, який торкається важливого (відкриває з’єднання з базою даних, читає один ключ кешу) і швидко повертається. fall 3 означає, що одна повільна перевірка під час ротації журналів або reload не виганяє здоровий вузол; rise 2 тримає вузол поза роботою, доки він не відповість двічі. slowstart поступово розганяє сервер, що повертається, тож вузлу з холодними кешами не віддають повну частку трафіку одразу.

Зливайте трафік перед перезапуском

Найчистіший перезапуск той, про який балансувальник знає заздалегідь. Runtime API HAProxy дозволяє вивести вузол з ротації, не обриваючи встановлені запити:

echo "set server be_web/web1 state drain" | socat stdio /run/haproxy/admin.sock
# wait for current sessions to finish
echo "show servers state be_web" | socat stdio /run/haproxy/admin.sock
systemctl restart php-fpm        # on web1
echo "set server be_web/web1 state ready" | socat stdio /run/haproxy/admin.sock

Загорніть це у скрипт, і кожен запланований перезапуск (оновлення безпеки PHP, зміна конфігурації, перезавантаження ядра) стає послідовністю: злити, зачекати, перезапустити, перевірити, повернути, по одному вузлу за раз.

Нехай кеш прикриє прогалину

Повносторінковий кеш робить більше, ніж прискорює анонімний трафік. З налаштованим grace Varnish продовжує віддавати останню добру копію сторінки, коли не може отримати свіжу:

import std;

sub vcl_recv {
    if (std.healthy(req.backend_hint)) {
        set req.grace = 10s;
    }
}

sub vcl_backend_response {
    set beresp.ttl = 5m;
    set beresp.grace = 24h;
}

Поки backend здорові, об’єкти оновлюються за секунди після закінчення терміну. Коли кожен backend нездоровий, Varnish може віддавати об’єкти до доби після їхнього TTL. Читачі продовжують отримувати сторінки під час обслуговування бази даних або заклиненого пулу PHP, і лише шляхи авторизованих користувачів та оформлення замовлення, які за задумом оминають кеш, залежать від того, чи працюють backend.

Сам edge потребує партнера

Два вузли edge з keepalived ділять плаваючі адреси, а скрипт відстеження понижує edge, чий HAProxy зупинився:

vrrp_script chk_haproxy {
    script "/usr/sbin/pidof haproxy"
    interval 2
    fall 2
    rise 2
}

vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 51
    priority 100
    track_script { chk_haproxy }
    virtual_ipaddress { <floating address>/<prefix> }
}

Ми будуємо edge навхрест: кожен вузол є primary для однієї публічної адреси і standby для іншої, а запис DNS несе обидві. Обидва edge несуть трафік увесь час, і відмова переносить одну адресу на вцілілий за секунди.

Доведіть це трафіком, а не відчуттям

Перезапуск непомітний лише якщо ви спостерігали, як він є непомітним. Тест, який ми проводимо перед передачею, простий:

  • Дайте на сайт стабільне навантаження ззовні, із сумішшю кешованих і некешованих URL. Невеликого генератора навантаження або циклу curl достатньо.
  • Рахуйте відповіді за кодом стану на клієнті, з позначками часу.
  • Перезапустіть PHP-FPM на одному вебвузлі. Потім на іншому. Потім відключіть один edge.
  • Порівняйте підрахунок на стороні клієнта з журналами origin.
while true; do
  printf '%s ' "$(date +%T.%N)"
  curl -s -o /dev/null -w '%{http_code} %{time_total}\n' https://www.example.com/
  sleep 0.2
done | tee restart-test.log
grep -vc ' 200 ' restart-test.log

Origin зазвичай запише жменьку помилок під час кожного перезапуску: це і є прогалина, яку має поглинути edge. Важливе число на стороні клієнта. Коли retry, redispatch і grace працюють, лічильник відвідувачів залишається на нулі, тоді як origin показує свою коротку прогалину перезапуску. Якщо якісь помилки все ж доходять до клієнта, позначки часу кажуть, який рівень їх пропустив.

Ваш контрольний список

  • HAProxy повторює з redispatch, а retry-on покриває порожні відповіді й тайм-аути.
  • Перевірка стану HTTP, яка торкається PHP і бази даних, з fall і rise, налаштованими проти хитання.
  • Заплановані перезапуски, які спершу зливають трафік через runtime API.
  • Grace у Varnish достатньо довгий, щоб покрити ваше найдовше обслуговування backend.
  • Два edge з плаваючими адресами і скриптом відстеження, обидва несуть трафік.
  • Тест перезапуску, проведений під навантаженням, із записаним числом помилок на стороні клієнта.

Якщо вам потрібна допомога з налаштуванням стека, доки його перезапуски не зникнуть, ми робимо це щотижня.

cta: Поговоріть з інженером

Related

  • /what-we-do/data-web/high-availability-web-tiers/
  • /what-we-do/data-web/object-caching/
  • /what-we-do/operations/edge-traffic-control/

Опубліковано 27 серпня 2026 р..

Усі нотатки з практики