Перейти к содержимому

Эксплуатация

Эксплуатация Soul Stack после первого запуска: мониторинг, масштабирование, обновления, миграции состояния, бэкап и восстановление, типовой troubleshooting.

Базовая модель проста: Keeper-инстансы — stateless и stand за L4-балансировщиком, всё авторитетное состояние лежит в PostgreSQL, горячий слой и координация — в Redis. Большинство эксплуатационных операций сводится к манипуляциям с инстансами Keeper-а и обслуживанию этих двух внешних хранилищ.

Телеметрия идёт по трём каналам:

КаналЧто несётКак забирать
Prometheus /metricsсчётчики, gauge, гистограммы Keeper и агентовscrape (pull): Keeper — порт :9090, агент — metrics.listen (по умолчанию loopback 127.0.0.1:9091)
OpenTelemetryсквозные трейсы (оператор → Keeper → агент)push: Keeper и агент сами отправляют OTLP по gRPC на otel.endpoint
Логиструктурированный JSONstdout/файл (systemd journal), встроенная ротация

Важная асимметрия: метрики работают по модели pull (Prometheus сам ходит на /metrics), а OpenTelemetry — по модели push (Keeper сам отправляет трейсы в коллектор; входящего OTel-порта у него нет).

  • Keeper/metrics обслуживается на отдельном порту без auth-цепочки Operator API. Опционально — HTTP Basic-auth (metrics.auth.basic, пароль из Vault), см. keeper.yml → metrics.
  • Агент — по умолчанию bind на loopback, наружу не торчит. При внешнем scrape — bind на интерфейс + Basic-auth с паролем из файла, см. soul.yml → metrics.

Метрики Keeper-а имеют префикс keeper_, агента — soul_. На что смотреть в первую очередь:

СигналМетрика (ориентир)О чём говорит
Доступность инстансаup{job="keeper"}инстанс не отвечает — investigate через логи
Подключённые агентыkeeper_grpc_streams_activeпадение ниже ожидаемого числа — часть душ отвалилась
Фоновая чисткаgauge лидерства Reaper-а (sum по кластеру = 1)ноль — чистка БД остановлена, таблицы будут расти; больше единицы — аномалия координации (перезагрузить Redis)
Планировщик расписанийgauge лидерства планировщика (sum = 1, если включён)ноль при включённом планировщике — регулярные запуски не порождаются
Доля провальных прогоновrate провальных / всех scenario-прогоноврост выше нормы — investigate сценарий или хосты
Латентность Vaultгистограмма чтения Vaultрост — Vault отвечает медленно, новые операции замедляются

Доменные идентификаторы (id прогона, sid, имя сценария) живут в трейсах, а не в метках метрик — это сознательно, чтобы не раздувать кардинальность Prometheus. Конкретного виновника ищите по трейсам и логам (по correlation-id и id прогона).

Keeper-инстансы stateless: всё авторитетное состояние — в общей PostgreSQL, presence и координация — в общем Redis. Поэтому масштабирование горизонтальное:

flowchart TB
    A["агенты"] --> LB["L4-балансировщик"]
    LB --> K1["keeper-1"]
    LB --> K2["keeper-2"]
    LB --> K3["keeper-N"]
    K1 --> D[("PostgreSQL + Redis — общие")]
    K2 --> D
    K3 --> D
  • Любой инстанс обслуживает любой запрос оператора и любой стрим агента.
  • Добавление мощности = запуск ещё одного инстанса на тот же PostgreSQL + Redis за тем же балансировщиком. Репликации состояния между инстансами нет.
  • Для health-probe балансировщику достаточно TCP-проверки порта EventStream; у Keeper-а также есть HTTP /readyz.
  • Каждый инстанс имеет уникальный в пределах кластера kid.

Требования к хранилищам. Суммарный поток подключений к PostgreSQL примерно равен postgres.pool.max × число инстансов — закладывайте это в max_connections PostgreSQL. Redis должен выдерживать presence/heartbeat всех душ; для крупных инсталляций используйте кластерную топологию Redis.

Кластер из нескольких инстансов обновляется rolling-способом, по одному инстансу:

  1. Снимите бэкап PostgreSQL (см. ниже) и сверьтесь с changelog обновляемой версии.
  2. Выведите первый инстанс из активного backend балансировщика.
  3. Установите новую версию и перезапустите инстанс. При остановке инстанс gracefully закрывает стримы — агенты по своему fallback-list-у уходят на оставшиеся инстансы. При старте новой версии применяются миграции схемы БД (если есть).
  4. Дождитесь готовности (/readyz → 200, число активных стримов растёт по мере возврата агентов), верните инстанс в балансировщик.
  5. Выдержите паузу, чтобы агенты перераспределились, и переходите к следующему инстансу.

Совместимость. Контракт Keeper↔агент — forward-compatible (только добавление полей, без удаления и переиспользования). Новый Keeper понимает старого агента и наоборот, поэтому в окне обновления версии Keeper и агентов могут сосуществовать. Breaking-изменения выносятся в новую версию контракта.

Откат. Если новая версия не стартует — оставшиеся инстансы продолжают обслуживать. Откатите пакет на предыдущую версию и перезапустите инстанс, затем разбирайтесь с проблемной версией отдельно. Поскольку миграции состояния — forward-only, после применившейся миграции схемы откат версии может потребовать восстановления из бэкапа — отсюда правило «бэкап до обновления».

Обновление агентов идёт независимо от Keeper-а и не требует одновременности (forward-compat контракта). Раскатывайте новую версию soul своей системой доставки пакетов; в pull-режиме агент сам переподключится после перезапуска сервиса.

Обновление инкарнации (миграции состояния)

Заголовок раздела «Обновление инкарнации (миграции состояния)»

Состояние инкарнации (runtime-инстанса сервиса) версионируется. Переход на новую версию схемы — явный шаг оператора, а не автоматическое «ленивое» обновление:

  • Оператор инициирует upgrade инкарнации до целевой версии через Operator API.
  • Цепочка миграций применяется атомарно, одной транзакцией PostgreSQL: либо состояние полностью переходит на новую версию, либо остаётся прежним при ошибке.
  • Перед изменением сохраняется snapshot прежнего состояния в историю — это путь отката (миграции forward-only, обратной миграции нет).

Миграция — это чистая функция «старое состояние → новое состояние» без побочных эффектов на хостах. Грамматика DSL, раскладка файлов migrations/ и тесты миграций — в справочнике DSL → Миграции состояния.

PostgreSQL — единственное холодное хранилище состояния кластера: реестры агентов и операторов, каталог сервисов, состояния инкарнаций, журналы. Поэтому:

  • Бэкап = бэкап PostgreSQL. Используйте штатные средства резервного копирования вашей PostgreSQL (логический дамп или непрерывное архивирование WAL для point-in-time recovery). Снимайте бэкап перед каждым обновлением.
  • Redis бэкапить не нужно для восстановления состояния — там только горячие, эфемерные данные (presence, lease, кэш). После потери Redis инстансы перезаполнят его сами; на время недоступности Redis деградируют функции, опирающиеся на координацию (выбор лидеров, presence), но авторитетное состояние не теряется.
  • Vault обслуживается и резервируется по собственному регламенту вашей инсталляции Vault.

Восстановление. Поднимите PostgreSQL из бэкапа, убедитесь в доступности Redis и Vault, запустите инстансы Keeper-а — они подхватят состояние из БД. Агенты переподключатся по своим fallback-list-ам. Если прогон «завис» из-за внезапной потери инстанса-владельца, есть штатный механизм реклейма зависших прогонов — см. troubleshooting ниже.

Каталог ролей и реестр операторов живут в PostgreSQL и управляются через Operator API (не через конфиг-файл):

  • первый оператор создаётся административной подкомандой keeper init при первичной настройке кластера;
  • дальнейшие операторы и роли заводятся через API авторизованным оператором;
  • отзыв оператора действует почти мгновенно: при ревокации все инстансы кластера пересобирают RBAC-снимок, и следующий же запрос отозванного оператора отклоняется (401) — единицы миллисекунд при здоровом Redis, не дольше интервала обновления снимка (по умолчанию ~10 секунд) при потере сигнала. Ограниченный TTL токена (auth.jwt.ttl_default) остаётся дополнительным рубежом защиты, а не основным.

Подробнее о bootstrap первого оператора — Установка из пакетов.

Развёрнутый разбор типовых ситуаций по схеме «симптом → причина → проверка/решение» — в отдельном разделе Решение проблем: агент не переходит в connected, инкарнация в error_locked, истёкший bootstrap-токен, ошибки рендера/шаблонов, недоступный Vault/PKI, mTLS не сходится, отказ доступа.

Краткая шпаргалка «куда смотреть»:

СимптомВероятная причинаЧто проверить
Агент не в статусе connectedсбой подключения / просроченная идентичность / упавший сервислоги агента (bootstrap, mTLS, verify); сетевую доступность bootstrap- и EventStream-портов Keeper-а; статус systemd-сервиса soul
Прогон долго в статусе applyingинстанс-владелец прогона недоступен (актуально при нескольких инстансах и выключенном воркер-пуле)включён ли acolytes > 0; число живых инстансов; механизм реклейма зависших прогонов
Инкарнация в статусе error_lockedпрогон завершился ошибкой, изменения не закоммичены в БДотчёт о прогоне и логи; устранить причину и повторить прогон (unlock + повтор — операции Operator API)
Доступ запрещён (403)у оператора нет нужного permission / истёк токенроль оператора и его permissions; срок действия токена
Сбой резолва секрета на стартеVault недоступен или путь/ключ неверныдоступность Vault; корректность vault:-ref-ов в keeper.yml; наличие нужных полей в Vault KV
Hot-reload не применилсяошибка валидации нового конфига / поле требует перезапускалоги на предмет события неудачного reload; таблицу «reload-able / restart-required» в keeper.yml → Hot-reload

Логи структурированы (JSON по умолчанию) и несут kid/sid, id прогона и trace-id — этого достаточно, чтобы связать запись в логах с трейсом и сетевым событием.