Перейти к содержанию

Отказоустойчивость

Страница отвечает на вопрос «что произойдёт, если это упадёт» по каждому компоненту. Формулировки намеренно прямые: там, где отказоустойчивости нет, так и написано.

Сводка

Компонент Переживает отказ узла Что происходит при отказе
frontend Да, при нескольких репликах Интерфейс недоступен, данные не страдают
backend Да, при нескольких репликах API недоступен, загрузка отчётов отклоняется
worker Да, при нескольких репликах Обработку подхватывает backend — медленнее, но не останавливается
PostgreSQL Нет в поставляемой конфигурации Останавливается всё
Хранилище отчётов Только сам том Приём новых отчётов прекращается
DomainScope Нет, экземпляр один Обследование периметра приостанавливается
OpenVAS, OWASP ZAP Нет, экземпляры одиночные Соответствующие проверки не выполняются

Компоненты без состояния

frontend отдаёт собранную статику. Реплик может быть сколько угодно, согласование между ними не требуется.

backend не хранит состояния между запросами: сессия пользователя — это подписанный токен, а не запись в памяти процесса. Реплики можно добавлять и убирать свободно, липкие сессии на балансировщике не нужны.

Отдельного пояснения требуют периодические задачи. Их планировщики работают в процессе backend и не выбирают лидера — тикает каждая реплика. Это безопасно, потому что постановка задач в очередь дедуплицируется по временному окну: окно заведомо короче интервала тика (например, 55 минут при часовом), поэтому вторая и последующие реплики не создают дубликат, а попадают в уже поставленную задачу.

Практический вывод: несколько реплик backend не приводят к двойному выполнению периодических задач. Проверять это отдельно не требуется.

worker берёт задачи из общей очереди в базе. Тот же обработчик работает и внутри backend, поэтому очередь разбирается, даже если ни одной реплики worker не осталось. Реплики разбирают задачи конкурентно; задача, чей исполнитель погиб, возвращается в очередь и выполняется повторно. Отсюда важное свойство: доставка «не менее одного раза». Задача может выполниться дважды, если процесс умер после совершения действия, но до отметки о завершении. Обработчики к этому готовы, но у внешних систем это может проявиться: например, повторным уведомлением.

PostgreSQL как единственная точка отказа

База данных хранит и данные, и очередь задач. Её потеря останавливает решение целиком: интерфейс не открывается, загрузка отчётов отклоняется, фоновая обработка не идёт.

Поставляемые манифесты разворачивают один экземпляр PostgreSQL без репликации и без автоматического переключения. Это осознанное упрощение для пилота, а не готовая к промышленной эксплуатации конфигурация.

Для промышленной среды используйте внешнюю базу с тем уровнем доступности, который у вас принят: управляемый сервис облачного провайдера либо кластер с репликой и переключением. Hub подключается к внешней базе обычными переменными DB_* — от него ничего специального не требуется.

Что необходимо в любом случае:

  1. Резервные копии базы — ежедневно, с хранением не менее двух недель.
  2. Проверка восстановления — раз в квартал развернуть копию на отдельном стенде и убедиться, что Hub стартует и видит данные. Непроверенная резервная копия равносильна её отсутствию.
  3. Наблюдение за местом на диске — переполнение тома базы останавливает запись.

Хранилище отчётов

Загруженные файлы отчётов лежат на отдельном томе backend. Потеря тома не приводит к потере находок: находки уже разобраны и лежат в базе. Теряется возможность посмотреть исходный файл отчёта.

Переполнение тома — более неприятный отказ: файл не сохраняется, и загрузка завершается ошибкой. Место освобождает периодическая очистка по срокам хранения (CLEANUP_RETENTION_*), но она не поможет, если сроки заданы слишком щедро для имеющегося объёма. Следите за заполнением тома.

Том отчётов и разбор

В поставляемых манифестах у backend и worker разные тома, а том worker'а вообще временный. Поэтому отчёт, доставшийся worker'у, читается не с диска, а из базы, куда его содержимое сохраняется при загрузке. Последствия для планирования объёма базы и памяти описаны в Известных ограничениях.

Провайдер SSO

Если вход настроен через внешний провайдер, его недоступность мешает новым входам. Уже вошедшие пользователи продолжают работать, пока действует их токен обновления — по умолчанию до 7 суток, при условии что вкладка периодически его обновляет.

Недоступность провайдера при старте backend не мешает запуску: сведения о провайдере получаются по мере надобности, и сервис поднимается даже с недостижимым провайдером. Кнопки входа при этом появятся не сразу.

На случай полной недоступности провайдера имеет смысл заранее держать работоспособной хотя бы одну учётную запись администратора — способ зависит от выбранного режима входа.

Внешние интеграции

Отказ любой из них не останавливает Hub: находки продолжают приниматься и обрабатываться.

Интеграция Что происходит при недоступности
Jira Задача не создаётся, задание повторяется по нарастающей задержке
Telegram, MAX, Mattermost, SMTP Уведомление не доставляется, задание повторяется
LLM-сервис Находка остаётся без разбора моделью, статус не меняется
NetBox Синхронизация периметра пропускает цикл
Сервер лицензий Проверка пропускается; работа не блокируется

Повторы выполняются с нарастающей задержкой. Задание, исчерпавшее попытки, остаётся в очереди в состоянии ошибки — его видно в интерфейсе управления заданиями, и повтор можно запустить вручную.

DomainScope и сканеры

DomainScope — одиночный процесс с циклами по расписанию. Второй экземпляр поверх той же базы не предусмотрен: два процесса начнут выполнять одни и те же циклы одновременно. При падении обследование приостанавливается до подъёма; уже переданные в Hub находки не страдают.

OpenVAS и OWASP ZAP тоже разворачиваются одиночными экземплярами. Их недоступность означает, что соответствующие проверки не выполняются, — на работу Hub это не влияет.

Обновления без простоя

Backend, worker и frontend обновляются последовательной заменой реплик — при двух и более репликах API остаётся доступным.

Исключение — миграции схемы базы данных. Они применяются при старте backend под блокировкой, чтобы одновременный запуск нескольких реплик не привёл к параллельному изменению схемы: первая реплика мигрирует, остальные ждут. Отдельного шага в процедуре обновления это не требует, но означает, что обновление с миграцией занимает больше времени, чем обычная замена реплик, и на это время старая и новая схемы должны быть совместимы. Подробнее: Обновления.

Чего в решении нет

Честный перечень того, что при необходимости придётся строить снаружи:

  • Развёртывание в нескольких зонах или регионах. Решение рассчитано на один кластер или один узел.
  • Автоматическое переключение базы данных. Обеспечивается вашей инфраструктурой.
  • Гарантия «ровно один раз» для фоновых задач. Гарантия — «не менее одного раза».
  • Резервный экземпляр DomainScope. Второй экземпляр на той же базе работать не рассчитан.