Допустим, внутренний сервис согласования заявок перевели с одного сервера на два. Главная страница стала открываться быстрее. Но сотрудник заполняет форму, нажимает «Сохранить» и получает пустой экран: следующий запрос попал на другой сервер, который ничего не знает о черновике. Инфраструктуру масштабировали, а приложение продолжает жить так, будто оно одно.
Балансировщик распределяет входящую работу между экземплярами приложения. Благодаря этому можно добавлять вычислительную мощность и выводить отдельные экземпляры из эксплуатации. Но прежде придётся разобраться, что именно разрешено переносить между ними, а что пока привязано к конкретной машине.
Два экземпляра должны уметь обслужить одного пользователя
Если состояние сессии, загруженный файл или незавершённая операция находятся только в памяти одного процесса, другой экземпляр их не восстановит. Нужен согласованный способ работы с состоянием: общее хранилище, подходящий механизм сессий, сохранение результата до ответа клиенту. Это не требует немедленно превращать приложение в микросервисы.
Есть обходной путь — sticky sessions, привязка пользователя к определённому экземпляру. Она бывает полезна, например для старого приложения. Однако привязка сама по себе не копирует данные и не спасает сессию при потере этого экземпляра. Кроме того, активные пользователи могут распределиться неравномерно.
Отдельно нужно проверить фоновые задания. Если каждый экземпляр запускает одну и ту же ночную рассылку, второй сервер способен удвоить число писем, а не только мощность. Балансировщик управляет входящими запросами; выбор исполнителя фоновой работы остаётся отдельной задачей.
Поровну запросов — не обязательно поровну работы
Открыть карточку заявки и выгрузить историю за год — совсем разные нагрузки. Последовательное распределение запросов по кругу, round robin, удобно для примерно одинаковой работы. Когда длительность существенно различается, может помочь выбор экземпляра с меньшим числом незавершённых запросов. Но и этот показатель не знает, насколько тяжёлым окажется следующий запрос.
Алгоритм зависит от возможностей балансировщика и характера соединений. На одном уровне выбирается получатель целого сетевого соединения, на другом — отдельного HTTP-запроса. Длительное соединение, например WebSocket, обычно остаётся с выбранным сервером. Добавление третьего экземпляра не означает, что существующие соединения немедленно переедут на него.
Поэтому полезно смотреть не только на общее число запросов, но и на время ответа каждого экземпляра, активные соединения и состав операций. В документации Application Load Balancer эти ограничения разобраны вместе с алгоритмами маршрутизации и привязкой сессий. У другого продукта конкретные правила могут отличаться.
На оставшихся серверах должно хватить места
Возьмём условный пример: каждый из двух одинаковых экземпляров устойчиво выполняет сто операций в минуту, а продукту требуется сто двадцать. Пока работают оба, мощности достаточно. После отказа одного балансировщик направит запросы на второй, но тот не станет выполнять сто двадцать операций вместо ста только благодаря новой схеме маршрутизации.
Запас следует оценивать для режима, который компания обещает пережить: потеря одного экземпляра, целой площадки или вывод части мощности на обновление. В расчёт входит и время запуска замены. Автомасштабирование может добавить ресурсы, но сначала клиентские запросы должны где-то обслуживаться.
Это не рекомендация всегда держать половину инфраструктуры без дела. Можно выбрать меньший запас и заранее ограничивать необязательную работу. Важно, чтобы решение принималось с пониманием последствий, а не обнаруживалось во время первого отказа.
Убрать сервер из схемы — ещё не закончить его работу
При плановом обновлении сначала прекращают направлять на экземпляр новые запросы, затем дают уже принятым операциям завершиться в пределах оговорённого времени. Такой режим называют draining. Его настройки должны согласовываться с завершением самого приложения: если процесс убить раньше, ожидание на стороне балансировщика ничего не сохранит.
При внезапном отказе завершить работу аккуратно уже нельзя. Часть запросов оборвётся. Некоторые изменения могли успеть сохраниться до потери ответа, поэтому автоматически отправлять любую операцию на соседний экземпляр небезопасно. Для значимых изменений нужны проверка статуса и защита от повторного выполнения.
Релиз тоже стоит проверять как обычный рабочий сценарий. Например, сотрудник загружает вложение в момент обновления. Сохранился ли файл? Можно ли продолжить действие? Появилась ли понятная ошибка вместо молчаливой потери данных?
Что остаётся общим после балансировщика
Несколько экземпляров не помогают, если они одновременно зависят от одной недоступной базы, одного хранилища файлов или ошибочной конфигурации. Даже сам балансировщик может оказаться единственной точкой отказа, если его развёртывание не предусматривает резервирование. Количество прямоугольников на схеме тут мало о чём говорит.
Проверки здоровья определяют, кому сейчас можно направлять трафик. Слишком поверхностная проверка оставит неисправный экземпляр в работе; слишком широкая может исключить сразу все экземпляры из-за общего внешнего сбоя. Поведение при отсутствии здоровых получателей нужно проверить у конкретного продукта: некоторые конфигурации продолжают направлять запросы даже на получателей с отрицательной оценкой здоровья.
Есть и общие настройки, которые редко попадают в обсуждение отказоустойчивости: сертификаты, лимиты размера запроса, срок ожидания соединения. Ошибка в них способна одинаково испортить работу всех экземпляров. Управляемый балансировщик снимает часть эксплуатации, но не ответственность за эти параметры.
Что показать на приёмке
Для проверки пригодятся обычная нагрузка с отключением одного экземпляра, плановое обновление во время активной работы и возвращение нового экземпляра в общий поток. В каждом случае нужно видеть не только зелёные индикаторы, но и завершённые действия пользователей, задержки и ошибки.
Если после добавления серверов вход в систему периодически теряется, фоновые задачи дублируются, а база перегружается сильнее, работа ещё не закончена. При этом небольшой продукт со стабильной нагрузкой вполне может остаться на одном экземпляре: когда допустимое время восстановления известно, более простое решение тоже имеет право на существование.