Эксплуатация¶
Повседневная эксплуатация Alatyr: метрики, backup/restore PostgreSQL,
мониторинг webhook-очереди доставки, мониторинг очереди async-SCEP issuance
(ca_pending) и планирование волны одобрений при продлении
сертификатов. Про отказоустойчивость при нескольких репликах сервера —
отдельная страница, Отказоустойчивость (HA), читайте её тоже,
если планируете горизонтальное масштабирование.
Метрики Prometheus¶
Сервер отдаёт метрики на GET /metrics — обычная текстовая экспозиция,
забирается любым сборщиком Prometheus. Это не /api/v1/metrics: путь
лежит вне префикса API.
| Метрика | Метки | О чём |
|---|---|---|
alatyr_http_requests_total |
method, route, status |
счётчик запросов |
alatyr_http_request_duration_seconds |
method, route |
гистограмма времени ответа, верхние корзины до 60 с |
alatyr_audit_write_failures_total |
action |
записи аудита, которые не удалось сохранить |
alatyr_keyholder_requests_without_token_total |
gate, reason |
обращения к Keyholder без годного серверного токена |
alatyr_agent_channel_identity_total |
verdict |
агентские запросы по тому, предъявлен ли клиентский сертификат |
alatyr_content_type_violations_total |
route, rung |
запросы с телом без Content-Type: application/json |
Плюс стандартные сборщики процесса и Go-рантайма — память, файловые дескрипторы, горутины.
Метка route — это шаблон маршрута (/api/v1/devices/:serial), а не
путь запроса. Так сделано намеренно: серийники и идентификаторы заявок не
попадают в метрики, а число рядов не растёт вместе с флотом. Неизвестный
маршрут даёт пустую метку — все 404 складываются в один ряд.
Что стоит вывести на дашборд в первую очередь:
- доля
status=5xxотalatyr_http_requests_total— здоровье сервера; alatyr_audit_write_failures_total— любое ненулевое значение означает, что административное действие произошло, а записи о нём нет;alatyr_keyholder_requests_without_token_totalс меткойgate=advisory— сколько серверов ещё ходит без токена, то есть что сломается, если поднять ступень до «обязательно».
Эндпоинт не аутентифицируется
У сборщика Prometheus нет учётных данных, а метрики за аутентификацией
тихо перестают собирать. Идентификаторов устройств и людей в экспозиции
нет по построению, но объём запросов и доля ошибок — тоже сведения о
развёртывании. Закрывайте /metrics на уровне ingress, как любой
внутренний адрес.
Backup/restore PostgreSQL¶
Alatyr не поставляет backup-инфраструктуру сам
Ни один из поставляемых демо-манифестов (Docker Compose, Helm-чарт) не разворачивает job/cron для бэкапа PostgreSQL — база всегда один под без реплики и без scheduled-снапшотов (см. Отказоустойчивость и Расчёт ресурсов). Backup/restore — целиком забота оператора, разворачивающего PostgreSQL для Alatyr. Ниже — не описание встроенного механизма, а минимальный практический рецепт поверх стандартных инструментов PostgreSQL.
Всё persistent-состояние приложения — одна база, адрес которой задаёт
ALATYR_DB_URL (см. Конфигурация). Схема
версионируется миграциями,
которые применяются через goose при старте сервера — восстановленная
база должна быть на той версии схемы, которую ожидает разворачиваемый
исполняемый файл сервера.
Логический бэкап (pg_dump/pg_restore, подходит для большинства
инсталляций текущего масштаба — см. Расчёт ресурсов про
ожидаемый объём данных):
# Бэкап (custom-формат — сжатый, restore умеет частичный/параллельный)
pg_dump --format=custom --file=alatyr-$(date -u +%Y%m%dT%H%M%SZ).dump \
"$ALATYR_DB_URL"
# Restore в пустую базу той же major-версии PostgreSQL
pg_restore --clean --if-exists --no-owner \
--dbname="$ALATYR_DB_URL" alatyr-<timestamp>.dump
- Снимайте бэкап регулярно по расписанию (cron/CronJob вне приложения) и
проверяйте
pg_restore --listна свежем дампе — непроверяемый бэкап эквивалентен отсутствию бэкапа. - Для непрерывной защиты (RPO меньше, чем интервал между
pg_dump) — включайте WAL-архивирование или используйте managed-Postgres с point-in-time recovery; ни то, ни другое не настроено ни в одном манифесте репозитория по умолчанию. - Секреты, которые приложение хранит зашифрованными в самой базе
(значения секретов вебхуков и состояние SCEP-поллинга), расшифровываются
тем же ключом, что задан в окружении сервера — сохраняйте резервную
копию
ALATYR_WEBHOOK_ENC_KEY/ALATYR_SCEP_ENC_KEYотдельно от самой базы (например, в секрет-хранилище), иначе восстановленная база окажется с нечитаемыми полями. - После restore на новый инстанс сервера убедитесь, что схема совпадает с ожидаемой версией миграций до того, как пускать туда трафик — goose применит недостающие миграции при следующем старте автоматически, но сверка «какая версия схемы была в дампе» — на операторе.
Обновление сервера на новую версию¶
Порядок ниже — для развёртывания через Docker Compose. Сервер и админка
поставляются разными образами, и версии у них расходиться не должны:
берите ALATYR_IMAGE_TAG из одних релизных заметок для обоих.
Перед обновлением: снимите бэкап¶
Это не формальность и не «рекомендуется». Схема базы едет только вперёд. Миграции применяются автоматически при старте сервера, а обратного хода у них нет: в продукте нет ни команды отката, ни кнопки — администратору нечем его выполнить. Единственное, что возвращает базу в прежнее состояние, — заранее снятый дамп (см. Backup/restore PostgreSQL выше).
«Поставлю прежний тег обратно» — не план отката
Запуск предыдущего образа на уже обновлённой схеме может пройти успешно: если миграция только ДОБАВЛЯЛА колонки со значениями по умолчанию, старый сервер их просто не читает и работает. Так бывает, и это проверяется одной командой — но продукт такого совпадения не гарантирует и не проверяет. Миграция, которая не только добавляет, оставит прежнюю версию сервера без данных, которых она ждёт.
Поэтому откат считайте возможным только при наличии дампа, снятого ДО обновления, и не планируйте его «через переключение тега».
Порядок¶
# 1. Бэкап. Без него шаги ниже необратимы.
pg_dump --format=custom --file=alatyr-$(date -u +%Y%m%dT%H%M%SZ).dump "$ALATYR_DB_URL"
# 2. Новая версия поставки в .env — одна на оба образа.
# ALATYR_IMAGE_TAG=<версия из релизных заметок>
# 3. Забрать образы заранее, чтобы простой равнялся перезапуску,
# а не скачиванию.
docker compose pull
# 4. Поднять. Миграции применятся при старте сервера сами.
docker compose up -d
Как убедиться, что обновление прошло¶
curl -s http://localhost:8090/api/v1/version
Ответ называет версию, коммит и время сборки. Сверьте версию админки с версией сервера: в подвале интерфейса выводятся обе, и при расхождении там появляется явная отметка. Расходятся они обычно по одной причине — обновили не оба образа.
Если сервер не поднялся, смотрите его журнал: ошибка миграции или подключения к базе выводится при старте и называет причину.
Что происходит с парком агентов¶
Обновление сервера не требует переустановки агентов: уже зарегистрированные машины продолжают работать. Обновление самого агента — отдельная процедура со своими проверками, см. Агенты.
Если сервер начинает требовать версию агента новее установленной, это видно в админке в карточке устройства, и лечится обновлением агента, а не сервера.
Мониторинг webhook-очереди (outbox)¶
Исходящие вебхуки (ALATYR_WEBHOOKS_ENABLED=true) идут через
outbox-таблицу webhook_deliveries, которую вычитывает фоновый воркер каждые
ALATYR_WEBHOOK_POLL_SECONDS секунд (по умолчанию 10). Статус доставки —
pending → delivered, либо pending → failed после
ALATYR_WEBHOOK_MAX_ATTEMPTS попыток (по умолчанию 6). Устаревшие записи
(created_at старше ALATYR_WEBHOOK_RETENTION_DAYS, по умолчанию 30 дней)
воркер автоматически удаляет каждые 6 часов — отдельного cron для этого не
нужно.
Через API (не требует прямого доступа к БД):
GET /api/v1/admin/webhooks/{id}/deliveries— постраничный журнал доставок конкретного эндпоинта (limit/page), включаяstatus/attempts/last_response_code/last_errorкаждой попытки. См. REST API.POST /api/v1/admin/webhooks/{id}/test— вручную поставить в очередь тестовую доставку, чтобы проверить эндпоинт end-to-end.
Через БД (для алертинга/дашборда, не открывая Admin UI на каждый эндпоинт):
-- Сколько доставок застряло в pending дольше ожидаемого интервала опроса
SELECT endpoint_id, count(*), min(next_attempt_at)
FROM webhook_deliveries
WHERE status = 'pending' AND next_attempt_at < now() - interval '5 minutes'
GROUP BY endpoint_id;
-- Недавние окончательные отказы (превышен max_attempts)
SELECT id, endpoint_id, event_type, attempts, last_response_code, last_error, created_at
FROM webhook_deliveries
WHERE status = 'failed' AND created_at > now() - interval '24 hours'
ORDER BY created_at DESC;
Растущее число pending-строк с next_attempt_at в прошлом обычно
значит, что воркер не запущен (ALATYR_WEBHOOKS_ENABLED было включено
после старта процесса без рестарта) либо эндпоинт систематически
недоступен — проверьте last_error/last_response_code последних
попыток через GET .../deliveries.
Мониторинг очереди async-SCEP issuance (ca_pending)¶
Когда issuer — SCEP (ALATYR_ISSUER=scep), выпуск не синхронный: заявка
переходит в статус ca_pending (cert_requests.status), а таблица
scep_pending держит одну строку на каждую заявку, ожидающую ответа
внешнего CA. Фоновый поллер вычитывает её каждые
ALATYR_SCEP_POLL_SECONDS секунд (по умолчанию 30) и повторяет запрос к
CA, пока сертификат не будет выпущен или заявка не исчерпает
retry-бюджет.
Только одна реплика — см. Отказоустойчивость
Поллинг ca_pending корректен только на одной реплике сервера. При
нескольких репликах два поллера могут забрать одну и ту же строку и
отправить дублирующий PKCSReq во внешний CA. Подробный разбор
механизма — Отказоустойчивость (HA), раздел «Что НЕ
безопасно на N репликах сегодня» → «SCEP-поллинг».
Прямого REST-эндпоинта для очереди ca_pending нет — мониторинг идёт
через SQL или через сами заявки:
-- Заявки, зависшие в ca_pending дольше нескольких интервалов поллинга
SELECT sp.cert_request_id, sp.attempts, sp.next_poll_at, sp.last_error,
cr.status, cr.created_at
FROM scep_pending sp
JOIN cert_requests cr ON cr.id = sp.cert_request_id
WHERE sp.next_poll_at < now() - interval '10 minutes'
ORDER BY sp.next_poll_at;
Растущее attempts с непустым last_error на одной и той же строке —
сигнал, что внешний SCEP CA систематически не выпускает сертификат по
этому запросу (истёкший challenge, недоступность CA, и т.п.) — смотрите
last_error за деталями конкретного отказа CA. Единичные «потерянные»
сертификаты у осиротевшего SCEP-poll (см. предупреждение выше про
несколько реплик) обычно не видны через эту таблицу — они уже покинули
scep_pending на стороне Alatyr, но не были никогда получены обратно;
искать их нужно в логах CA, не здесь.
Продление сертификатов и волна одобрений¶
Агент продлевает сертификат самостоятельно, проходя тот же цикл enroll, что и при первичной выдаче, — подробный разбор механизма: Агенты → Продление сертификата. Для планирования эксплуатации важно одно следствие этого механизма: продление создаёт новую заявку, и она проходит ту же проверку одобрения, что и первичная.
- При ручном одобрении заявок ожидайте периодическую волну заявок на
одобрение по мере того, как у устройств флота подходит к концу срок
действия сертификата, а не равномерный редкий поток. По умолчанию
срок действия машинного сертификата (
wifi) — 3 года; это единственная цель, у которой значение настраивается на сервере. У остальных целей срок фиксированный:user_mtlsиad_logon— 1 год,ssh— 24 часа. Цельk8sв волну не попадает вовсе: credential для кластера живёт 10 минут и выдаётся на каждый вызовkubectl, без заявки и без одобрения (см. Доступ к Kubernetes). - Если для соответствующего purpose настроено автоодобрение (сервисный
аккаунт с ролью
cert-auto-approver, см. Администрирование → Approve / reject / revoke заявок), волна проходит без ручных действий со стороны оператора.
Хранение снапшотов логов устройств¶
Загруженные через POST /api/v1/devices/{serial}/request-logs снапшоты
логов устройств хранятся не дольше 7 дней — фоновая задача сама очищает
устаревшие снапшоты, отдельного обслуживания не требует.
См. также¶
- Отказоустойчивость (HA) — что безопасно/небезопасно при нескольких репликах сервера.
- Расчёт ресурсов — ожидаемые ресурсы и объём данных.
- Логирование — структурные логи для разбора инцидентов.
- Диагностика — указатель по типовым отказам.
- Известные ограничения — чего продукт не делает.