В кластере с привязкой клиента к одному экземпляру после отказа реплики растёт доля ошибок. Как механизм привязки объясняет это?
Привязка клиента к экземпляру направляет его запросы преимущественно на тот же экземпляр, поэтому отказ этой реплики нарушает маршрут для всех закреплённых за ней клиентов. Если состояние сессии хранится локально, новый экземпляр не сможет продолжить обработку без восстановления состояния, что увеличивает число ошибок и время восстановления.
Даже при внешнем хранении состояния привязка может временно ухудшить ситуацию: балансировщик должен обнаружить отказ, удалить экземпляр из маршрутизации и перенаправить клиентов. До этого момента запросы могут попадать в недоступную реплику.
Привязка клиента к экземпляру появилась как практический способ поддержать приложения, которые хранили пользовательскую сессию в памяти процесса. Она позволяла не выносить состояние в отдельное хранилище и уменьшала задержки доступа к локальным данным.
Такой подход был особенно распространён в монолитных веб-приложениях и системах, где перенос сессии между экземплярами не был реализован. Цена простоты — зависимость доступности клиента от конкретной реплики.
Пусть балансировщик закрепляет клиента за экземпляром на основании cookie, исходного адреса или другого ключа. Пока экземпляр работает, запросы распределяются предсказуемо, но при его отказе большое число клиентов одновременно теряет привычный маршрут.
Риски зависят от места хранения состояния:
Неверное решение создаёт скрытую зависимость от конкретной реплики. В результате горизонтальное масштабирование становится менее эффективным, а отказ одной реплики затрагивает не только новые запросы, но и уже закреплённых клиентов.
При session affinity балансировщик сохраняет соответствие между клиентом и экземпляром. Это уменьшает число перемещений запросов, но одновременно снижает свободу балансировщика перераспределять нагрузку и усложняет отказоустойчивость.
После отказа возможны следующие этапы:
Если состояние хранится локально, перенаправление само по себе недостаточно: новый экземпляр не располагает прежней сессией. Поэтому для отказоустойчивости предпочтительнее внешнее хранилище сессий, репликация состояния или архитектура, в которой сервер не хранит пользовательское состояние между запросами.
Статeless-подход упрощает масштабирование: любой экземпляр может обработать запрос, а данные сессии находятся во внешнем хранилище или передаются в проверяемом клиентском контексте. Однако внешнее хранилище добавляет сетевую задержку, стоимость, собственную точку отказа и необходимость контролировать согласованность.
Полностью отказаться от привязки удаётся не всегда. Она может быть оправдана для временной совместимости с legacy-приложением или для данных, которые слишком дороги для постоянной передачи между узлами. В таком случае нужны короткие сроки жизни привязки, корректные health check, быстрый failover и контроль распределения нагрузки.
Важно отличать обнаружение отказа от восстановления состояния. Health check помогает перестать направлять новые запросы на неисправную реплику, но не возвращает потерянную локальную сессию. Для этого требуется отдельный механизм хранения или восстановления данных.
Интернет-магазин использовал привязку по cookie, а корзина и часть состояния оформления заказа хранились в памяти веб-экземпляра. Во время обновления одного узла доля ошибок оформления выросла: клиенты продолжали отправлять запросы на прежний экземпляр, а после переключения на новый теряли корзину.
Рассматривались три варианта. Увеличение времени ожидания перед отключением узла уменьшало число ложных переключений, но продлевало недоступность. Сохранение привязки и репликация сессий между веб-узлами сохраняли поведение приложения, но добавляли сложность, сетевой трафик и задержку синхронизации. Полный отказ от привязки требовал изменения приложения, зато позволял свободно распределять запросы.
Выбрали перенос корзины и сессии в отказоустойчивое внешнее хранилище, после чего отключили постоянную привязку для новых маршрутов. Для плановых работ добавили корректное исключение узла из балансировки и ожидание завершения активных запросов. В результате отказ или вывод одного экземпляра перестал автоматически приводить к потере пользовательского состояния, а нагрузка стала распределяться равномернее.
1. Достаточно ли внешнего хранилища сессий, чтобы привязка больше не влияла на отказоустойчивость?
Нет. Внешнее состояние устраняет проблему потери локальной сессии, но не саму зависимость от маршрута. Пока балансировщик не обнаружил отказ, запросы закреплённых клиентов могут получать таймауты. Кроме того, привязка по-прежнему может вызвать дисбаланс нагрузки и увеличить число клиентов, одновременно затронутых отказом одного экземпляра.
2. Почему привязка может ухудшать масштабирование даже без отказов?
Новые экземпляры получают только тех клиентов, которые были назначены им после добавления в пул. Уже существующие привязки не перераспределяются сразу, поэтому нагрузка остаётся сосредоточенной на старых экземплярах. Особенно заметен эффект при долгоживущих сессиях, неравномерной активности пользователей или ключах с плохим распределением.
3. Чем опасно автоматическое повторное подключение клиента после смены экземпляра?
Повторное подключение может восстановить доступность, но не гарантирует сохранение состояния операции. Если предыдущий экземпляр успел выполнить действие, а ответ потерялся, повтор может создать дубликат, например два заказа или два списания. Поэтому переключение должно сочетаться с идемпотентностью операций, идентификаторами запросов и явным определением того, какие данные можно безопасно повторить.