Отказоустойчивость и горизонтальное масштабирование ПринтМенеджера
Назначение
Эта статья разбирает отказоустойчивую и масштабируемую архитектуру обработки заданий печати, копирования и сканирования.
Когда нужна эта схема
Базовой конфигурации из одного ПринтМенеджера достаточно, пока хватает производительности одного сервера и нет требований по отказоустойчивости. Горизонтальное масштабирование и балансировка нагрузки решают задачи:
- Производительность — несколько ПринтМенеджеров обрабатывают задания параллельно, увеличивая общую пропускную способность.
- Отказоустойчивость — потеря одного или нескольких серверов не останавливает обработку заданий на оставшихся.
Расположение компонентов
Главное архитектурное отличие этой схемы от базовой конфигурации в наборе компонентов и в взаимном расположении: несколько равноправных узлов обработки перед общими базой данных и реплицированным хранилищем.
Что меняется относительно базовой конфигурации
| Компонент базовой конфигурации | Что происходит в кластере |
|---|---|
| Nginx как точка входа | Точкой входа становится балансировщик HAProxy. Это реверс-прокси, но умеющий распределять запросы между несколькими активными ПринтМенеджерами по определенным правилам. Сам nginx не пропадает, но перестает быть точкой входа. |
| Redis (брокер) | Улучшается до master-slave кластера с поддержкой Redis Sentinel, который обеспечивает отказоустойчивость кластера Redis. |
| PostgreSQL | Становится общей базой данных на весь кластер — логически один компонент, к которому обращаются все узлы. Внутри эта база данных должна быть физически или логически реплицирована. |
| Локальное хранилище образов документов CUPS | Меняется на общее NFS-хранилище — папка которая доступна на чтение и запись каждому приложению, т.е. задание может быть создано на одном узле, а прочитано на другом. По одному CUPS на каждый из узлов, каждый из CUPS содержит одинаковое количество принтеров. Поскольку CUPS становится больше, то нагрузка на каждый из них становится меньше, а значит возможна печать большего количества документов одновременно без перегрузки. |
| Celery (один экземпляр) | По одному Celery на каждом узле — обрабатывают различные задачи; именно это даёт прирост производительности. |
| Django | По одному Django на каждом узле. Поскольку Django становится больше, они независимо обрабатывают разные запросы извне |
| Планировщик | Становится распределённым: экземпляр существует на каждом узле, но согласовывает запуск периодических задач через Redis Sentinel, чтобы одна и та же задача не запускалась одновременно на всех узлах. |
| Конвертер-сервер | Не меняет своих функций, но иным способом обрабатывает TCP запросы полученные от сокета HAProxy, чтобы правильно определять адрес физического ковертера. |
Несмотря на появление HAProxy, Redis Sentinel и общего NFS-хранилища, архитектура обработки самого задания принципиально не меняется. Все изменения направлены на одно: чтобы несколько экземпляров ПринтМенеджера могли совместно обрабатывать задания, используя общие данные и не мешая друг другу.
Как обрабатывается запрос
В базовой конфигурации клиент отправляет запрос напрямую на единственный сервер: nginx → Django → Celery → CUPS.
В кластере тот же путь проходит через дополнительную точку координации:
- Клиент отправляет запрос не на конкретный сервер, а на балансировщик.
- Балансировщик направляет его на один из активных узлов.
- На выбранном узле запрос обрабатывается как обычно: nginx → Django.
- Django передаёт задачу через Redis — кластер координирует, чтобы её взял в работу ровно один свободный Celery, независимо от того, на каком узле он находится.
- Метаданные задания кладутся в общую PostgreSQL, образ документа — в общее NFS-хранилище.
- Печать выполняет тот узел, куда Celery отправитзадачу.
Поскольку Celery и Django на каждом узле работает независимо и асинхронно друг относительно друга, то с ростом числа узлов растёт и пропускная способность по заданиям — это и есть причина увеличивать число узлов ради производительности, отдельно от вопроса отказоустойчивости.
Принцип отказоустойчивости
Основа отказоустойчивости – это возможности системы перераспредлять запросы и задачи с узлов, которые отказали, на работающие узлы.
- для перераспределения запросов нужен HAProxy
- для перераспределения задач нужен Redis Sentinel
HAProxy внутри себя имеет реплику, и внешний трафик с запросами может быть направлен на одну или другую реплику с использованием различных механизмов. Балансировщик определяет доступен ли узел, и если нет, то отправляет запрос в первый встречный работающий узел.
Redis Sentinel кластеру для работы необходим кворум — большинство узлов Redis должно оставаться активным: активных узлов должно быть строго больше, чем неактивных. Если это условие нарушается, Redis Sentinel не могут договориться, кто из оставшихся узлов должен взять на себя роль мастера, и кластер теряет работоспособность целиком.
| Узлов в кластере | Можно потерять одновременно |
|---|---|
| 3 | 1 |
| 5 | 2 |
| 7 | 3 |
Классический пример — 3 узла (1 мастер + 2 слейва): такая конфигурация переживает потерю любого одного узла, но не двух сразу.