Отказоустойчивое веб-приложение можно построить из двух одинаковых application-серверов, размещенных в одной приватной сети, и Load Balancer, который принимает внешние запросы и распределяет их между доступными backend.
В этом руководстве создадим две виртуальные машины с одинаковым приложением, добавим их в backend pool балансировщика, настроим health checks и назначим Load Balancer внешний IP. После этого проверим, что запросы действительно распределяются между обеими VM.
Затем принудительно остановим один application-сервер и убедимся, что health check помечает его как недоступный, а Load Balancer продолжает отправлять запросы на вторую VM. В результате приложение остается доступным даже при отказе одного сервера.
Отдельно разберем, где должны храниться пользовательские сессии и данные. Сессии нельзя привязывать только к памяти конкретной VM, иначе пользователь потеряет состояние при переключении между backend. Для этого лучше использовать Redis, базу данных или другой общий внешний storage. Пользовательские файлы и постоянные данные также должны храниться вне локального диска application-сервера — например, в общей базе данных или Object Storage.
В итоге получим базовую отказоустойчивую архитектуру, в которой выход из строя одной VM не приводит к полной недоступности приложения. Рекомендуется размещать VM в разных зонах доступности, сессионные БД использовать в отказоустойчивом варианте - это защитит не только от сбоя конкретной VM (и уберёт даунтайм при плановых работах), но и от сбоев на площадках облачного провайдера.
Как устроено отказоустойчивое веб-приложение
Один application-сервер остается единой точкой отказа. Если виртуальная машина выключится, зависнет или приложение перестанет отвечать, пользователи потеряют доступ ко всему сервису.
Базовый способ повысить отказоустойчивость — использовать несколько одинаковых backend-серверов и поставить перед ними Load Balancer. Пользователь обращается только к одному публичному адресу балансировщика, а тот сам выбирает доступную VM для обработки запроса. При этом если это облачный балансировщик, он выполнен как правило уже в отказоустойчивом исполнении, и единой точкой отказа не становится.
Зачем использовать несколько application-серверов
Если приложение работает только на одной VM, любой сбой этой машины приводит к полной недоступности сервиса.
Причиной может быть:
- Аварийное завершение приложения;
- Нехватка памяти;
- Проблема с ОС;
- Плановые работы, например перезагрузка или обновление;
- Сбой виртуальной машины;
- Сетевой инцидент.
При использовании двух одинаковых application-серверов запрос может быть обработан второй VM, если первая временно недоступна.
Упрощенно схема выглядит так:

Обе VM должны запускать одну и ту же версию приложения и иметь одинаковую конфигурацию.
При этом важно отделить само приложение от постоянных данных. Если один сервер хранит уникальные пользовательские данные только на своем локальном диске, наличие второй VM уже не обеспечивает полноценную отказоустойчивость.
Как работает Load Balancer
Load Balancer принимает запросы клиентов и перенаправляет их на backend-серверы из своего pool.
Например:

Для клиента обе VM скрыты за одним публичным адресом.
Балансировщик может распределять запросы разными способами. Один из простых вариантов — Round Robin, при котором backend выбираются по очереди:

В реальной работе распределение зависит от алгоритма балансировки, состояния backend и текущих соединений.
Главное преимущество схемы в том, что клиенту не нужно знать адрес каждой VM. Он всегда обращается к одному endpoint.
Что происходит при отказе одной VM
Самого Load Balancer недостаточно. Он должен понимать, какие backend действительно способны принимать запросы.
Для этого используются health checks.
Балансировщик регулярно обращается к application-серверам, например по адресу http://PRIVATE_IP/health или просто к /
Если VM отвечает успешно, backend считается здоровым: Healthy
Если несколько проверок подряд завершаются ошибкой, backend переводится в состояние: Unhealthy
После этого Load Balancer прекращает отправлять ему пользовательские запросы.
Например:

Пользователь продолжает обращаться к тому же публичному адресу, но все новые запросы направляются на вторую VM.
Когда первый сервер снова начинает успешно проходить health checks, балансировщик может автоматически вернуть его в pool.
Какие компоненты создадим в этом руководстве
Для практического примера понадобится следующая инфраструктура:

Создадим:
- Приватную сеть;
- Подсеть;
- Две одинаковые VM;
- Security Group;
- Load Balancer;
- listener;
- Backend pool;
- Health monitor;
- Внешний IP балансировщика.
На каждой VM будет работать небольшая тестовая веб-страница.
Чтобы видеть, какой сервер обработал запрос, ответы немного различаются: Application Server 1 и Application Server 2.
Это позволит проверить как обычную балансировку, так и работу схемы после остановки одной виртуальной машины.
Подготовка облачной инфраструктуры
Сначала подготовим сеть, правила доступа и две VM. Обе машины должны находиться в одной приватной сети и быть доступны Load Balancer по внутренним IP.
Создание приватной сети и подсети
В панели облака создайте отдельную приватную сеть, например: ha-private-network
Для нее создайте подсеть: ha-private-subnet
В качестве CIDR можно использовать: 192.168.60.0/24
В результате обе application VM получат внутренние адреса из одного диапазона, например:
Application Server 1 → 192.168.60.10
Application Server 2 → 192.168.60.11
Конкретные адреса могут быть назначены автоматически.
Приватная сеть нужна для внутреннего взаимодействия между Load Balancer и backend. Публиковать каждую VM отдельно в интернет для работы приложения не требуется.
Подготовка Security Group
Создадим отдельную Security Group, например: ha-app-sg
Для application-серверов понадобится разрешить HTTP-трафик от балансировщика: TCP 80
Для первоначальной настройки также понадобится SSH: TCP 22
Если провайдер позволяет ограничить правило источником, доступ к порту 80 лучше разрешать только из приватной сети или адресов Load Balancer.
Например:
TCP 80
Source: 192.168.60.0/24
SSH также безопаснее ограничить административным IP, если это возможно.
Не следует без необходимости открывать базы данных или внутренние сервисы:
3306
5432
6379
Они не нужны Load Balancer для обычной HTTP-проверки приложения.
Создание двух одинаковых виртуальных машин

Создадим две VM с одинаковыми параметрами.
Например:
ha-app-01
ha-app-02
Для обеих выбираем:
- Одинаковый Ubuntu image;
- Одинаковый flavor;
- Одну Security Group;
- Одну приватную сеть;
- Одинаковый SSH keypair.
Главная задача — сделать backend максимально одинаковыми.
Если одна VM имеет другую версию приложения или системных пакетов, поведение сервиса может зависеть от того, на какой backend попал конкретный запрос.
После запуска убедитесь, что обе машины находятся в состоянии Active и подключены к ha-private-network
Какие порты должны быть доступны между компонентами
Для нашего простого HTTP-приложения достаточно следующей схемы:
| Направление | Порт | Назначение |
| Администратор → VM | 22 | первоначальная настройка по SSH |
| Load Balancer → VM | 80 | пользовательские запросы |
| Health Check → VM | 80 | проверка состояния backend |
| Интернет → Load Balancer | 80 или 443 | публичный доступ к приложению |
Если приложение позже будет работать только по HTTPS, TLS можно завершать непосредственно на Load Balancer или передавать HTTPS до backend — это зависит от архитектуры и возможностей облачной платформы.
Важно, чтобы VM не были открыты всему интернету только ради работы балансировщика. Пользовательский трафик должен приходить через единый внешний endpoint.
Подготовка первого application-сервера
Теперь настроим первую VM. Для теста достаточно Nginx со статической страницей, которая явно показывает идентификатор backend.
Подключение к первой VM
Для первоначальной настройки VM нужен SSH-доступ.
Если машине временно назначен Floating IP, подключитесь: ssh -i ~/.ssh/ha-key ubuntu@203.0.113.21
Здесь 203.0.113.21 используется только как пример.
Если доступ к приватной VM организован через bastion host, VPN или консоль облачной панели, используйте соответствующий способ.
После входа обновим индекс пакетов: sudo apt update
Установка Nginx или тестового веб-приложения
Установим Nginx: sudo apt install -y nginx
Проверим состояние: sudo systemctl status nginx --no-pager
Сервис должен находиться в: active (running)
Включим автоматический запуск: sudo systemctl enable nginx
Проверим локальный ответ: curl http://127.0.0.1
Если Nginx установлен корректно, сервер вернет стандартную HTML-страницу.
Создание страницы с идентификатором сервера
Заменим стандартную страницу: sudo nano /var/www/html/index.html
Добавим простой HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Application Server 1</title>
</head>
<body>
<h1>Application Server 1</h1>
<p>Status: OK</p>
</body>
</html>
Теперь запрос curl http://127.0.0.1 должен вернуть содержимое с Application Server 1.
Для health check можно использовать эту же страницу, но удобнее создать отдельный endpoint.
Например: echo 'OK' | sudo tee /var/www/html/health
Проверим: curl http://127.0.0.1/health
Ответ: OK
Позже Load Balancer сможет использовать /health для определения доступности backend.
Проверка ответа первого application-сервера

Сначала получим приватный IP hostname -I или ip addr
Затем проверим Nginx с другой VM или из доступной части приватной сети: curl http://192.168.60.10
В ответ должна появиться строка: Application Server 1
Health endpoint: curl http://192.168.60.10/health должен возвращать: OK
На этом первый backend готов. Далее такую же конфигурацию установим на вторую VM, изменив только идентификатор страницы на Application Server 2.
Подготовка второго application-сервера
Вторая виртуальная машина должна быть максимально похожа на первую: та же операционная система, тот же веб-сервер, одинаковая структура приложения и одинаковые health endpoints. Различаться будет только тестовый идентификатор страницы, чтобы мы могли увидеть, какой backend обработал конкретный запрос.
Установка такого же приложения на вторую VM
Подключитесь ко второй виртуальной машине: ssh -i ~/.ssh/ha-key ubuntu@203.0.113.22
Здесь IP приведен как пример.
Обновим индекс пакетов: sudo apt update
Установим Nginx: sudo apt install -y nginx
Включим автоматический запуск: sudo systemctl enable nginx
Проверим состояние: sudo systemctl status nginx --no-pager
Сервис должен находиться в состоянии: active (running)
Создание страницы с идентификатором второго сервера

Теперь заменим стандартную страницу: sudo nano /var/www/html/index.html
Добавим:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Application Server 2</title>
</head>
<body>
<h1>Application Server 2</h1>
<p>Status: OK</p>
</body>
</html>
Создадим такой же health endpoint: echo 'OK' | sudo tee /var/www/html/health
Проверим локально: curl http://127.0.0.1
В ответ должно присутствовать: Application Server 2
Health check: curl http://127.0.0.1/health
Ответ: OK
Проверка одинаковой конфигурации серверов
Перед добавлением VM в Load Balancer стоит убедиться, что оба backend работают одинаково.
На первой VM: nginx -v
На второй: nginx -v
Версии желательно использовать одинаковые.
Также проверьте health endpoint обеих машин из приватной сети:
curl http://192.168.60.10/health
curl http://192.168.60.11/health
Оба запроса должны вернуть: OK
Основная страница при этом специально отличается:
Application Server 1
Application Server 2
Эта разница нужна только для демонстрации распределения запросов. В реальном production-приложении обе VM должны отдавать одну и ту же версию приложения.
Создание Load Balancer
После подготовки двух backend можно создать Load Balancer. Он получит интерфейс в приватной сети и станет единой точкой входа для пользовательских запросов.
Названия элементов интерфейса могут немного отличаться в зависимости от OpenStack-панели, но логика остается одинаковой: Load Balancer → Listener → Pool → Members.
Создание балансировщика в приватной сети
В разделе Load Balancers создайте новый балансировщик.
Задайте имя, например: ha-web-lb
В качестве сети или subnet выберите: ha-private-subnet
Load Balancer получит внутренний адрес в той же сетевой инфраструктуре, где находятся application-серверы.
После создания дождитесь рабочего состояния. В OpenStack оно может отображаться как ACTIVE или аналогичным статусом.
Не добавляйте публичный адрес к каждой application VM только ради пользовательского трафика. Внешний IP позже будет назначен самому Load Balancer.
Добавление listener для HTTP или HTTPS
Listener определяет, на каком протоколе и порту Load Balancer принимает запросы.
Для тестовой схемы используем HTTP:
Protocol: HTTP
Port: 80
Название: http-listener
В результате Load Balancer будет принимать соединения на 80 и передавать их в backend pool.
Для production-сервиса вместо HTTP обычно используют HTTPS. TLS может завершаться непосредственно на балансировщике, если облачная платформа поддерживает загрузку сертификатов.
Создание backend pool
Теперь создайте pool, в котором будут находиться две application VM.
Например:
Name: web-backend-pool
Protocol: HTTP
В качестве алгоритма выберите: ROUND_ROBIN
Pool связывается с созданным ранее listener.
Схема становится такой:

После этого в pool можно добавить реальные backend-серверы.
Добавление двух VM в pool
Добавим первую VM как member:
Address: 192.168.60.10
Protocol Port: 80
И вторую:
Address: 192.168.60.11
Protocol Port: 80
Если интерфейс позволяет выбирать instances из списка, вместо ручного IP достаточно выбрать нужную VM и ее приватный интерфейс.
После добавления структура будет выглядеть так:

Load Balancer теперь знает, куда можно перенаправлять пользовательские запросы.
Однако прежде чем использовать его как отказоустойчивую точку входа, необходимо настроить health checks.
Настройка алгоритма распределения запросов
Для простой схемы из двух одинаковых application-серверов подходит Round Robin.
Пример:

Также OpenStack Load Balancer может поддерживать другие алгоритмы, например Least Connections или Source IP.
Least Connections выбирает backend с меньшим количеством текущих соединений. Такой вариант может быть полезен, если запросы отличаются по продолжительности.
Для демонстрации оставим: ROUND_ROBIN
Он позволит наглядно увидеть ответы от обеих VM при последовательных запросах.
Настройка Health Checks
Без health checks балансировщик может продолжать отправлять запросы на VM, которая уже перестала обслуживать приложение.
Поэтому следующий шаг — создать Health Monitor для backend pool.
Зачем балансировщику проверять backend-серверы
Состояние виртуальной машины в OpenStack и состояние самого приложения — не одно и то же.
VM может быть ACTIVE но Nginx или приложение внутри нее может уже не работать.
Поэтому Load Balancer должен выполнять собственные проверки на уровне приложения.
Например: GET /health
Если сервер отвечает HTTP 200 backend считается работоспособным.
Если запрос несколько раз подряд завершается ошибкой или timeout, сервер временно исключается из распределения запросов.
Создание HTTP health check
Для pool web-backend-pool создайте Health Monitor.
Тип: HTTP
URL path: /health
Ожидаемый HTTP status: 200
Если интерфейс поддерживает настройку HTTP method, оставьте: GET
Балансировщик будет регулярно выполнять запросы:
http://192.168.60.10/health
http://192.168.60.11/health
и оценивать состояние обоих members.
Настройка интервала, timeout и количества попыток
Для тестовой схемы можно использовать относительно короткие значения:
Delay / Interval: 5 seconds
Timeout: 3 seconds
Max Retries: 3
Это означает, что проверка выполняется примерно каждые 5 секунд, а backend не будет считаться недоступным после одной случайной ошибки.
Параметры нужно подбирать так, чтобы балансировщик достаточно быстро обнаруживал реальные сбои, но не исключал server из pool из-за единичного кратковременного timeout.
Например, слишком агрессивная схема:
Interval: 1s
Retries: 1
может давать ложные переключения при коротких сетевых задержках.
Слишком медленная:
Interval: 60s
Retries: 5
наоборот, слишком долго будет отправлять запросы на уже недоступный backend.
Проверка состояния обоих backend

После сохранения Health Monitor дождитесь нескольких циклов проверки.
Обе VM должны отображаться как работоспособные. В зависимости от интерфейса статус может называться:
ONLINE
Healthy
UP
или аналогично.
В итоге pool должен выглядеть примерно так:
Application Server 1 192.168.60.10:80 ONLINE
Application Server 2 192.168.60.11:80 ONLINE
Если один из серверов остается ERROR или OFFLINE, сначала проверьте доступность health endpoint с другого узла приватной сети:
curl http://192.168.60.10/health
curl http://192.168.60.11/health
Также нужно проверить Security Group. Load Balancer должен иметь возможность подключаться к TCP-порту 80 обеих VM.
После того как оба member стабильно проходят health checks, можно назначать Load Balancer внешний IP и проверять реальное распределение пользовательских запросов.
Подключение внешнего IP к Load Balancer
После настройки backend pool и health checks Load Balancer уже умеет распределять трафик внутри приватной сети. Чтобы приложение стало доступно из интернета, назначим балансировщику внешний IP.
Назначение Floating IP
Откройте созданный Load Balancer: ha-web-lb
и привяжите к нему Floating IP из внешней сети.
Например: 203.0.113.30
Здесь адрес приведен только как пример.
В результате схема будет выглядеть так:

Именно этот публичный адрес теперь используется пользователями для обращения к приложению.
Application-серверам при этом собственные Floating IP для постоянной работы не нужны.
Проверка публичного адреса балансировщика
С локального компьютера или другой внешней машины выполните: curl http://203.0.113.30
В ответ должна прийти HTML-страница одного из backend.
Например:
Application Server 1
Status: OK
или:
Application Server 2
Status: OK
Также адрес можно открыть в браузере: http://203.0.113.30
Если приложение не открывается, проверьте:
- Статус Load Balancer;
- Состояние listener;
- Состояние backend pool;
- Health checks;
- Security Group;
- Привязку Floating IP;
- Фаервол на VM;
- Доступность TCP-порта 80.
Далее переходим к проверке.
Проверка распределения запросов между двумя VM
Один успешный запрос подтверждает только доступность самого Load Balancer.
Чтобы убедиться, что в pool действительно участвуют обе VM, нужно выполнить несколько запросов подряд.
При алгоритме Round Robin ответы должны распределяться между двумя backend.
Упрощенно:

Точный порядок может отличаться из-за особенностей балансировщика, существующих соединений или настроек HTTP keep-alive, поэтому важен не строгий порядок, а появление ответов от обеих VM.
Проверка балансировки запросов
Теперь отдельно проверим, что оба application-сервера реально участвуют в обработке пользовательского трафика.
Как убедиться, что работают обе VM
Для этого мы заранее указали разные идентификаторы на тестовых страницах Application Server 1 и Application Server 2.
При этом все остальные параметры серверов должны быть одинаковыми.
Если при обращении к одному и тому же Floating IP появляются ответы от обоих серверов, значит Load Balancer действительно распределяет запросы между двумя members.
Повторные HTTP-запросы к балансировщику
Выполним несколько последовательных запросов:
for i in {1..10}; do
curl -s http://203.0.113.30 | grep "Application Server"
done
Пример результата:
<h1>Application Server 1</h1>
<h1>Application Server 2</h1>
<h1>Application Server 1</h1>
<h1>Application Server 2</h1>
<h1>Application Server 1</h1>
<h1>Application Server 2</h1>
Если shell не поддерживает такой цикл, запросы можно выполнить вручную: curl http://203.0.113.30. Несколько раз подряд.
Также можно вывести только идентификатор сервера:
for i in {1..10}; do
curl -s http://203.0.113.30 | grep -o "Application Server [12]"
done
Проверка ответов от Application Server 1 и Application Server 2
Если в выводе встречаются оба значения:
Application Server 1
Application Server 2
значит обе VM:
- Доступны по приватной сети;
- Находятся в backend pool;
- Успешно проходят health checks;
- Получают пользовательские запросы через Load Balancer.
Если всегда отвечает только одна VM, проверьте состояние второго member.
Например, он может находиться в статусе:
OFFLINE
ERROR
Также причиной может быть неправильно указанный приватный IP, порт backend или Security Group.
Имитация отказа одной виртуальной машины
Главная проверка отказоустойчивой схемы — искусственный отказ одного backend.
Мы остановим первую VM и проверим, продолжит ли приложение работать через тот же публичный IP.
Принудительная остановка первой VM
В панели управления откройте ha-app-01 и остановите виртуальную машину.
Для теста важно именно сделать backend недоступным, а не просто удалить его из pool вручную. Так мы увидим, как Load Balancer реагирует на настоящий отказ.
После остановки состояние VM изменится, например, на SHUTOFF или аналогичный статус (название статуса может отличаться в зависимости от интерфейса облачной платформы).
На этом этапе не меняйте настройки Load Balancer и не удаляйте member из backend pool.
Обнаружение сбоя через Health Check
После остановки VM health monitor продолжит проверять: 192.168.60.10:80/health
Запросы начнут завершаться timeout или ошибкой соединения.
С учетом настроек:
Interval: 5s
Timeout: 3s
Max Retries: 3
переход в состояние недоступности произойдет не мгновенно.
Через несколько циклов первый backend должен получить статус OFFLINE или Unhealthy.
Второй сервер при этом должен оставаться: ONLINE
Исключение недоступного backend из балансировки
После того как health check признал первый member недоступным, Load Balancer перестает направлять на него новые запросы.
Backend при этом может оставаться в pool — удалять его вручную не требуется.
Балансировщик просто учитывает его состояние:
Server 1 → Unhealthy → не получает новые запросы
Server 2 → Healthy → получает весь трафик
Это одно из ключевых преимуществ health checks.
Без них Load Balancer мог бы продолжать отправлять часть запросов на неработающий сервер, что приводило бы к периодическим ошибкам у пользователей.
Проверка работы приложения после отказа
Теперь снова обращаемся к тому же Floating IP: curl http://203.0.113.30
Приложение должно продолжить отвечать:
Application Server 2
Status: OK
Проверим несколько раз:
for i in {1..10}; do
curl -s http://203.0.113.30 | grep -o "Application Server [12]"
done
После исключения первого backend ожидаемый результат:
Application Server 2
Application Server 2
Application Server 2
Application Server 2
Application Server 2
При этом внешний адрес не меняется: 203.0.113.30
Пользователь продолжает обращаться к тому же endpoint и не должен вручную переключаться на другую VM.
Такой тест подтверждает базовую отказоустойчивость application-уровня: отказ одного backend не приводит к полной недоступности сервиса, пока второй сервер и сам Load Balancer остаются работоспособными.
Возврат сервера после сбоя
После проверки отказоустойчивости можно снова запустить остановленную VM и убедиться, что Load Balancer автоматически возвращает ее в работу.
Запуск остановленной VM
В панели управления откройте ha-app-01 и запустите виртуальную машину.
После загрузки ОС проверьте, что Nginx снова работает: sudo systemctl status nginx --no-pager
Ожидаемое состояние: active (running)
Если сервис не запустился автоматически, выполните: sudo systemctl start nginx
Проверим локально: curl http://127.0.0.1/health
Ответ должен быть: OK
Повторное прохождение Health Check
После запуска VM Load Balancer снова начнет получать успешные ответы от: http://192.168.60.10/health
Переход обратно в рабочее состояние также происходит не мгновенно. Балансировщик должен получить необходимое количество успешных проверок.
Через несколько циклов статус первого backend должен измениться с OFFLINE на ONLINE или аналогичное состояние.
Второй сервер при этом продолжает работать без изменений.
Возвращение VM в backend pool
Если health monitor настроен правильно, вручную повторно добавлять сервер в pool не требуется.
Member уже остается частью: web-backend-pool
Load Balancer только временно исключает его из распределения трафика на основании результатов health checks.
После восстановления схема снова выглядит так:

Проверить это можно повторными запросами:
for i in {1..10}; do
curl -s http://203.0.113.30 | grep -o "Application Server [12]"
done
После возврата первой VM в работу в выводе снова должны появляться ответы от обоих backend.
Где хранить пользовательские сессии
Две VM и Load Balancer решают проблему отказа application-сервера, но сами по себе не делают приложение полностью отказоустойчивым.
Особенно важно правильно организовать хранение пользовательских сессий.
Почему нельзя хранить сессии только в памяти application-сервера
Представим, что пользователь авторизовался через первый backend: User → Load Balancer → Server 1
Server 1 сохранил его сессию только в оперативной памяти.
Следующий запрос Load Balancer отправил на второй backend: User → Load Balancer → Server 2
Server 2 ничего не знает о сессии, созданной на первой VM.
В результате пользователь может неожиданно потерять авторизацию или состояние приложения.
Проблема станет еще заметнее при отказе Server 1: все сессии, находившиеся только в его памяти, исчезнут вместе с ним.
Поэтому состояние пользователя не должно зависеть от конкретного application-сервера.
Хранение сессий в Redis или базе данных
Один из распространенных вариантов — вынести сессии в отдельное централизованное хранилище.
Например:

Обе VM обращаются к одному Redis и видят одинаковое состояние пользовательских сессий.
Вместо Redis можно использовать базу данных, если производительности и модели приложения достаточно для такого сценария.
Например:

Главное правило — сессия должна быть доступна независимо от того, какой backend получил запрос.
Для production-систем само внешнее хранилище также должно быть отказоустойчивым. Если все application-серверы зависят от одного Redis или одной базы данных без резервирования, этот компонент становится новой единой точкой отказа.
Когда можно использовать sticky sessions
Некоторые Load Balancer поддерживают sticky sessions, или session persistence.
В этом режиме запросы одного пользователя стараются направлять на один и тот же backend.
Например:
User A → Server 1
User A → Server 1
User A → Server 1
User B → Server 2
User B → Server 2
Это может быть полезно для старых приложений, которые сложно быстро переделать под внешнее хранение состояния.
Однако sticky sessions не решают проблему полностью.
Если Server 1 выйдет из строя, User A все равно будет перенаправлен на другой backend, который может не знать о его локальной сессии.
Поэтому sticky sessions лучше рассматривать как дополнительный механизм, а не замену общему хранилищу состояния.
Почему stateless-приложения проще масштабировать
Наиболее удобная модель для балансировки — stateless application server.
Такой backend не хранит уникальное состояние пользователя локально.
Например:

Любой запрос можно отправить на любую доступную VM.
Это упрощает:
- Горизонтальное масштабирование;
- Замену application-серверов;
- Rolling updates;
- Восстановление после сбоев;
- Автоматическое добавление новых VM;
- Работу Load Balancer без привязки пользователя к конкретному backend.
Если нужно увеличить производительность, можно добавить третий или четвертый application-сервер без переноса пользовательских данных между ними.
Где хранить пользовательские данные и файлы
Постоянные данные приложения также не должны зависеть от локального диска одной конкретной VM.
Иначе отказоустойчивая схема будет защищать только веб-слой, но не сами данные.
Почему локальный диск VM не подходит для общих данных
Представим, что пользователь загрузил файл через Server 1: User → Load Balancer → Server 1
Приложение сохранило его в /var/www/app/uploads/ на локальном диске первой VM.
Следующий запрос попал на Server 2, где такого файла нет.
В результате часть запросов будет работать, а часть — возвращать ошибку.
Еще хуже ситуация станет после удаления или необратимого повреждения первой VM: локально сохраненные данные могут быть потеряны полностью.
Поэтому локальный filesystem application-сервера лучше использовать только для:
- Кода приложения;
- Временных файлов;
- Кеша, который можно восстановить;
- Логов, если они дополнительно собираются централизованно.
Далее переходим к использованию общей базы данных.
Использование общей базы данных
Структурированные данные пользователей обычно хранятся в общей базе данных:

Обе VM подключаются к одному логическому источнику данных.
Например, там могут храниться:
- Учетные записи;
- Заказы;
- Настройки;
- Комментарии;
- Записи приложения;
- Права доступа;
- Metadata.
Для действительно отказоустойчивой архитектуры база данных также должна иметь собственное резервирование или использовать managed-сервис с высокой доступностью.
Если база существует только на одной VM, ее отказ приведет к остановке приложения даже при двух исправных application-серверах.
Object Storage для пользовательских файлов
Для изображений, документов, архивов и других бинарных файлов удобнее использовать Object Storage.
Схема:

Приложение сохраняет файл не на локальный диск VM, а в общий bucket.
Обе VM обращаются к одному объекту по одинаковому ключу.
Например: uploads/users/42/avatar.jpg
Такой подход особенно удобен при горизонтальном масштабировании: новые application-серверы не требуют синхронизации каталогов с пользовательскими файлами.
Что произойдет с данными при отказе одной VM
Если архитектура построена правильно, отказ одного application-сервера не должен затрагивать постоянные данные.
Например:
Server 1 — OFFLINE
Server 2 — ONLINE
Redis — ONLINE
Database — ONLINE
Object Storage — ONLINE
Load Balancer перестает отправлять запросы на Server 1, а Server 2 продолжает использовать те же:
- Пользовательские сессии;
- Записи базы данных;
- Загруженные файлы.
Пользователь может заметить кратковременную задержку во время обнаружения сбоя, но его данные не должны исчезнуть вместе с application VM.
При этом важно понимать, что два backend делают отказоустойчивым только application-слой. Для полноценной high availability необходимо отдельно продумать надежность базы данных, session storage, Object Storage, Load Balancer и других критичных компонентов.
Защита отказоустойчивой схемы
Высокая доступность не должна достигаться за счет лишнего открытия инфраструктуры в интернет. В типичной схеме публичной точкой входа остается Load Balancer, а application-серверы работают внутри приватной сети.
Почему application-серверы не должны иметь публичные IP
Если обе VM имеют собственные Floating IP и доступны из интернета напрямую, пользователь потенциально может обойти Load Balancer.
Например:

Во втором случае запрос уже не проходит через:
- Балансировку;
- Health checks;
- Eдиные правила доступа;
- TLS termination на Load Balancer.
Кроме того, каждый дополнительный публичный endpoint увеличивает поверхность атаки.
После завершения первоначальной настройки application-серверам желательно оставить только приватные адреса:
Application Server 1 → 192.168.60.10
Application Server 2 → 192.168.60.11
Для административного доступа можно использовать bastion host, VPN, консоль облачного провайдера или другой защищенный канал.
Ограничение доступа Security Group
Security Group должна разрешать только действительно необходимый трафик.
Для backend-серверов обычно требуется:
| Порт | Источник | Назначение |
| 80 | Load Balancer или приватная сеть | HTTP-запросы и health checks |
| 22 | административный адрес или bastion | SSH |
| 443 | Load Balancer | если backend принимает HTTPS |
Не следует без необходимости создавать правило:
TCP 80
Source: 0.0.0.0/0
для каждой application VM.
Если облачная платформа позволяет указать Security Group самого Load Balancer как источник, это предпочтительнее широкого CIDR.
Также не стоит открывать наружу внутренние сервисы приложения, например:
PostgreSQL :5432
Redis :6379
Доступ к ним должен предоставляться только тем компонентам приватной сети, которым он действительно необходим.
Какие порты должны быть доступны Load Balancer
С внешней стороны Load Balancer принимает пользовательский трафик.
Для HTTP: Internet → Load Balancer :80
Для HTTPS: Internet → Load Balancer :443
Со стороны backend необходимо разрешить порт приложения:
Load Balancer → VM 1 :80
Load Balancer → VM 2 :80
Health monitor также должен иметь возможность обращаться к выбранному endpoint: GET /health
Если приложение использует отдельный health-порт, его нужно разрешить отдельно.
Например: Load Balancer → VM :8080
При этом такой порт не обязательно должен быть доступен пользователям из интернета.
HTTPS и TLS termination на балансировщике
В production-среде публичное приложение обычно публикуют по HTTPS.
Один из вариантов — завершать TLS непосредственно на Load Balancer:

Балансировщик хранит сертификат, устанавливает защищенное соединение с клиентом и после расшифровки передает HTTP-запрос backend-серверу.
Такой подход упрощает управление сертификатами: их не нужно отдельно устанавливать на каждую application VM.
Если требования безопасности предполагают шифрование трафика и внутри приватной сети, можно использовать схему:

В этом случае сертификаты или подходящая PKI-конфигурация потребуются и на backend.
Конкретный способ зависит от возможностей Load Balancer и требований проекта.
Обслуживание двух application-серверов
Несколько backend позволяют не только переживать аварии, но и выполнять часть плановых работ без полной остановки приложения.
Вместо одновременного обновления двух VM серверы можно обслуживать по очереди.
Как обновлять приложение без полной остановки
Предположим, сейчас работают оба backend:
Server 1 → ONLINE
Server 2 → ONLINE
Сначала обновляется Server 1, в то время как Server 2 продолжает принимать пользовательские запросы.
После проверки первой VM ее возвращают в балансировку, а затем аналогично обновляют Server 2.
Схема выглядит так:
1. Server 1 → обслуживание
Server 2 → обслуживает трафик
2. Server 1 → ONLINE
Server 2 → обслуживание
3. Server 1 → ONLINE
Server 2 → ONLINE
Такой подход значительно уменьшает время полной недоступности сервиса.
При этом новая версия приложения должна оставаться совместимой с общими внешними компонентами — например, с базой данных и форматом пользовательских сессий.
Поочередное исключение VM из балансировки
Перед обновлением backend желательно временно исключить из распределения новых запросов.
В зависимости от Load Balancer для этого можно:
- Отключить member;
- Перевести его в административное состояние Disabled;
- Использовать механизм drain connections, если он поддерживается;
- Временно удалить сервер из pool.
После этого убедитесь, что трафик продолжает обслуживать второй backend.
Затем на исключенной VM можно обновить приложение: sudo systemctl stop myapp
выполнить необходимые действия и снова запустить сервис: sudo systemctl start myapp
Для Nginx вместо полной остановки при изменении конфигурации обычно достаточно:
sudo nginx -t
sudo systemctl reload nginx
После проверки сервер можно вернуть в pool.
Проверка Health Checks после обновления
Не следует возвращать VM в обработку пользовательского трафика только потому, что процесс приложения запустился.
Сначала проверьте endpoint: curl http://127.0.0.1/health
Ожидаемый ответ: OK
Затем дождитесь, пока Load Balancer снова определит backend как: ONLINE
После этого стоит проверить публичный endpoint: curl http://203.0.113.30
и несколько запросов подряд:
for i in {1..10}; do
curl -s http://203.0.113.30 | grep -o "Application Server [12]"
done
Если ответы снова приходят от обеих VM, первый этап обновления завершен и можно переходить ко второму серверу.
Что делать, если оба backend становятся недоступны
Схема из двух VM выдерживает отказ одного application-сервера, но не одновременный отказ обоих.
Если health monitor пометил оба member как недоступные:
Server 1 → OFFLINE
Server 2 → OFFLINE
Load Balancer больше не имеет исправного backend для обработки запроса.
Пользователь начнет получать ошибку или timeout, несмотря на то что сам публичный адрес балансировщика остается доступным.
В такой ситуации необходимо проверить:
- Состояние обеих VM;
- Работоспособность приложения;
- Результаты health checks;
- Security Group;
- Приватную сеть;
- Доступность зависимых сервисов;
- Последние изменения конфигурации;
- Состояние общей базы данных или Redis.
Если одновременно перестали отвечать сразу обе VM, стоит искать общий для них компонент. Причиной может оказаться не два независимых сбоя, а, например, отказ базы данных, проблема приватной сети или неудачное обновление приложения.
Для более критичных систем можно дополнительно использовать больше двух backend, несколько зон доступности и резервирование зависимых сервисов.
Заключение

Load Balancer и несколько application-серверов позволяют устранить одну из главных проблем простой архитектуры — зависимость всего приложения от единственной виртуальной машины.
В рассмотренной схеме две одинаковые VM находятся в приватной сети, а внешний трафик поступает через Load Balancer. Health checks позволяют автоматически определить недоступный backend и исключить его из распределения запросов. Поэтому после принудительной остановки одной VM приложение продолжает работать через второй сервер без изменения публичного адреса.
Однако высокая доступность не заканчивается на application-слое. Пользовательские сессии не следует хранить только в памяти отдельных VM, а постоянные данные и загруженные файлы — только на их локальных дисках. Для общего состояния используют Redis или базу данных, а для файлов может применяться Object Storage.
В результате отказ одного application-сервера становится штатной ситуацией, а не причиной полной остановки сервиса. По той же схеме можно выполнять поочередные обновления backend и в дальнейшем масштабировать приложение, добавляя новые серверы в Load Balancer.
FAQ
Нужен ли публичный IP каждой application VM?
Нет. В отказоустойчивой схеме публичной точкой входа обычно выступает Load Balancer. Application-серверы могут находиться только в приватной сети и получать запросы от балансировщика по внутренним IP.
Это уменьшает количество публично доступных компонентов и не позволяет пользователям обходить Load Balancer напрямую.
Что произойдет, если одна VM перестанет отвечать?
Health Monitor обнаружит, что backend больше не проходит проверку, и Load Balancer перестанет отправлять ему новые запросы. Трафик будет направляться на оставшуюся работоспособную VM.
После восстановления сервера и успешного прохождения health checks backend может автоматически вернуться в балансировку.
Почему одного Load Balancer недостаточно для высокой доступности?
Load Balancer защищает приложение от отказа отдельных backend-серверов, но другие компоненты также могут стать единой точкой отказа.
Отдельно нужно учитывать надежность:
- Базы данных;
- Redis или другого хранилища сессий;
- Object Storage;
- Сетевой инфраструктуры;
- Самого сервиса Load Balancer.
Для критичных систем эти компоненты также должны иметь собственные механизмы резервирования.
Можно ли хранить пользовательские сессии на application-серверах?
Хранить сессию только в памяти конкретной VM нежелательно. Следующий запрос пользователя может попасть на другой backend, где этой сессии нет.
Для общего состояния лучше использовать Redis, базу данных или другой доступный всем application-серверам storage, в худшем случае - репликацию сессий между VM.
Sticky sessions могут временно решить часть проблемы, но при отказе закрепленной VM локальная сессия все равно может быть потеряна.
Можно ли хранить загруженные пользователями файлы на локальном диске VM?
Для временных файлов — да, для постоянных пользовательских данных — нежелательно.
Если файл сохранен только на первой VM, вторая машина может не иметь к нему доступа. Кроме того, при повреждении или удалении VM такие данные могут быть потеряны.
Для пользовательских файлов лучше использовать общее хранилище, например Object Storage.
Какой алгоритм балансировки выбрать для двух одинаковых серверов?
Для простой схемы подойдет Round Robin. Он последовательно распределяет запросы между доступными backend.
В некоторых сценариях полезнее Least Connections, при котором Load Balancer выбирает сервер с меньшим количеством активных соединений.
Выбор зависит от особенностей приложения и характера нагрузки.
Зачем нужен отдельный health endpoint?
Проверка /health позволяет определить состояние самого приложения, а не только факт работы виртуальной машины.
VM может оставаться в состоянии ACTIVE, даже если веб-сервер или application process внутри нее уже завершился с ошибкой.
Health endpoint должен быть легким и достаточно точно отражать способность приложения обслуживать запросы.
Сразу ли Load Balancer переключится на вторую VM после сбоя?
Не обязательно. Время переключения зависит от параметров health monitor: интервала между проверками, timeout и допустимого количества неудачных попыток.
Слишком короткие интервалы позволяют быстрее обнаруживать сбои, но могут повысить вероятность ложных срабатываний. Слишком большие значения увеличивают время, в течение которого недоступный backend может оставаться в pool.
Можно ли обновлять application-серверы без полной остановки приложения?
Да. Один backend можно временно исключить из балансировки, обновить и проверить, после чего вернуть в pool. Затем те же действия выполняются со второй VM.
Пока хотя бы один исправный сервер остается доступным, Load Balancer может продолжать обслуживать пользовательские запросы.
Что произойдет, если одновременно выйдут из строя обе VM?
Load Balancer останется без доступных backend и не сможет обслуживать приложение.
Для более высоких требований к доступности можно использовать три и более application-сервера, распределять их между зонами доступности и отдельно резервировать базу данных и другие общие сервисы.


