feat: add production health monitoring
This commit is contained in:
@@ -32,4 +32,10 @@ RETENTION_APPROVED_PERSONAL_DAYS=180
|
||||
RETENTION_STAGING_DAYS=90
|
||||
RETENTION_AUDIT_DAYS=365
|
||||
RETENTION_PUBLISHED_PAYLOAD_DAYS=365
|
||||
|
||||
# Host-side monitoring. BACKUP_ROOT must match the backup.sh destination.
|
||||
BACKUP_ROOT=/srv/rf4-backups
|
||||
MONITOR_DISK_MAX_PERCENT=85
|
||||
MONITOR_TLS_MIN_DAYS=14
|
||||
MONITOR_BACKUP_MAX_HOURS=26
|
||||
LOG_LEVEL=INFO
|
||||
|
||||
@@ -18,6 +18,8 @@ Production-контур для домена `rf4spotter.ru`, TLS, секреты
|
||||
|
||||
Политика минимизации данных и ежедневная dry-run-first очистка описаны в [`docs/data-retention.md`](docs/data-retention.md).
|
||||
|
||||
Host-side мониторинг контейнеров, readiness, диска, резервных копий и TLS описан в [`docs/production-monitoring.md`](docs/production-monitoring.md).
|
||||
|
||||
Gitea Actions workflow `.gitea/workflows/ci.yml` на каждый push и pull request проверяет Python, миграции на чистой PostgreSQL, Astro build и полный Compose/Playwright-сценарий. При падении E2E сохраняются логи контейнеров и Playwright-артефакты.
|
||||
|
||||
Актуальная инвентаризация источников и правила подключения адаптеров находятся в [`docs/data-source-audit.md`](docs/data-source-audit.md). Разрешённый технический пилот RF4DB/RF4-STAT описан в [`docs/community-source-pilot.md`](docs/community-source-pilot.md), а статус разрешений и лимитов — в [`docs/data-permissions.md`](docs/data-permissions.md). Данные сохраняются только в промежуточный staging и не влияют на индекс без явной проверки и публикации администратором.
|
||||
|
||||
+12
-2
@@ -107,9 +107,19 @@ docker compose --env-file .env.production -f compose.production.yaml exec -T api
|
||||
|
||||
Сроки и состав данных описаны в [`docs/data-retention.md`](../docs/data-retention.md). Автоматизацию включайте только после проверки dry-run на рабочем наборе.
|
||||
|
||||
## 8. Что ещё блокирует приглашение альфа-пользователей
|
||||
## 8. Мониторинг
|
||||
|
||||
- базовый мониторинг `/ready`, диска и срока TLS-сертификата;
|
||||
После настройки DNS, TLS и первого backup выполните:
|
||||
|
||||
```bash
|
||||
./deploy/monitor.sh
|
||||
```
|
||||
|
||||
Проверка контролирует контейнеры, `/ready`, диск, свежесть backup и срок TLS. Установка systemd timer, пороги и порядок реакции описаны в [`docs/production-monitoring.md`](../docs/production-monitoring.md). До подключения реального канала уведомлений одного журнала systemd недостаточно.
|
||||
|
||||
## 9. Что ещё блокирует приглашение альфа-пользователей
|
||||
|
||||
- подключение уведомлений о сбоях мониторинга;
|
||||
- проверка DNS/TLS и полного запуска на целевом сервере;
|
||||
- внешнее зашифрованное хранилище резервных копий.
|
||||
|
||||
|
||||
Executable
+80
@@ -0,0 +1,80 @@
|
||||
#!/bin/sh
|
||||
set -u
|
||||
|
||||
repo=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)
|
||||
cd "$repo" || exit 2
|
||||
env_file=${COMPOSE_ENV_FILE:-.env.production}
|
||||
if [ ! -r "$env_file" ]; then
|
||||
printf 'CRITICAL rf4spotter failures="env-file-unreadable"\n'
|
||||
exit 2
|
||||
fi
|
||||
|
||||
read_env() {
|
||||
key=$1
|
||||
value=$(sed -n "s/^${key}=//p" "$env_file" | tail -n 1)
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
site_domain=${SITE_DOMAIN:-$(read_env SITE_DOMAIN)}
|
||||
backup_root=${BACKUP_ROOT:-$(read_env BACKUP_ROOT)}
|
||||
disk_max=${MONITOR_DISK_MAX_PERCENT:-$(read_env MONITOR_DISK_MAX_PERCENT)}
|
||||
tls_days=${MONITOR_TLS_MIN_DAYS:-$(read_env MONITOR_TLS_MIN_DAYS)}
|
||||
backup_hours=${MONITOR_BACKUP_MAX_HOURS:-$(read_env MONITOR_BACKUP_MAX_HOURS)}
|
||||
disk_max=${disk_max:-85}
|
||||
tls_days=${tls_days:-14}
|
||||
backup_hours=${backup_hours:-26}
|
||||
for value in "$disk_max" "$tls_days" "$backup_hours"; do
|
||||
case "$value" in ''|*[!0-9]*) printf 'CRITICAL rf4spotter failures="monitor-threshold-invalid"\n'; exit 2 ;; esac
|
||||
done
|
||||
base_url=${MONITOR_BASE_URL:-https://$site_domain}
|
||||
compose="docker compose --env-file $env_file -f compose.production.yaml"
|
||||
failures=""
|
||||
|
||||
fail() {
|
||||
failures="${failures}${failures:+; }$1"
|
||||
}
|
||||
|
||||
running=$($compose ps --status running --services 2>/dev/null) || running=""
|
||||
for service in proxy db minio api web; do
|
||||
printf '%s\n' "$running" | grep -qx "$service" || fail "service:$service"
|
||||
done
|
||||
|
||||
ready_payload=$(curl -fsS --max-time 10 "$base_url/ready" 2>/dev/null) || ready_payload=""
|
||||
printf '%s' "$ready_payload" | grep -Eq '"status"[[:space:]]*:[[:space:]]*"ready"' || fail "readiness"
|
||||
|
||||
disk_used=$(df -Pk "$repo" | awk 'NR==2 {gsub(/%/, "", $5); print $5}')
|
||||
case "$disk_used" in
|
||||
''|*[!0-9]*) fail "disk:unknown" ;;
|
||||
*) [ "$disk_used" -lt "$disk_max" ] || fail "disk:${disk_used}%" ;;
|
||||
esac
|
||||
|
||||
if [ -z "$backup_root" ] || [ ! -d "$backup_root" ]; then
|
||||
fail "backup:directory"
|
||||
else
|
||||
recent_backup=$(find "$backup_root" -mindepth 2 -maxdepth 2 -name SHA256SUMS -mmin "-$((backup_hours * 60))" -print -quit 2>/dev/null)
|
||||
if [ -z "$recent_backup" ]; then
|
||||
fail "backup:stale"
|
||||
else
|
||||
(cd "$(dirname "$recent_backup")" && sha256sum -c --quiet SHA256SUMS) >/dev/null 2>&1 || fail "backup:checksum"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$site_domain" ]; then
|
||||
fail "tls:domain"
|
||||
else
|
||||
tls_seconds=$((tls_days * 86400))
|
||||
certificate=$(openssl s_client -servername "$site_domain" -connect "$site_domain:443" </dev/null 2>/dev/null | openssl x509 -outform PEM 2>/dev/null) || certificate=""
|
||||
if [ -z "$certificate" ]; then
|
||||
fail "tls:unavailable"
|
||||
else
|
||||
printf '%s\n' "$certificate" | openssl x509 -checkend "$tls_seconds" -noout >/dev/null 2>&1 || fail "tls:expires-soon"
|
||||
fi
|
||||
fi
|
||||
|
||||
timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
if [ -n "$failures" ]; then
|
||||
printf 'CRITICAL rf4spotter timestamp=%s failures="%s"\n' "$timestamp" "$failures"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf 'OK rf4spotter timestamp=%s disk_used=%s%% backup_max_age=%sh tls_min=%sd\n' "$timestamp" "$disk_used" "$backup_hours" "$tls_days"
|
||||
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=RF4 Spotter production health check
|
||||
After=docker.service network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=rf4spotter
|
||||
WorkingDirectory=/opt/rf4-spotter
|
||||
ExecStart=/opt/rf4-spotter/deploy/monitor.sh
|
||||
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=Run RF4 Spotter health check every five minutes
|
||||
|
||||
[Timer]
|
||||
OnBootSec=2min
|
||||
OnUnitActiveSec=5min
|
||||
RandomizedDelaySec=30s
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
+2
-1
@@ -57,6 +57,7 @@
|
||||
## Подготовка MVP к пилоту
|
||||
|
||||
- [x] Добавить health/readiness-проверки PostgreSQL, MinIO, API и импорта; отразить их в Compose (`/health` без зависимостей, `/ready` с компонентами и режимом обязательного импорта).
|
||||
- [x] Добавить host-side production monitor контейнеров, `/ready`, диска, свежести/целостности backup и срока TLS; подготовлены systemd timer и runbook, реальный канал уведомлений подключается на сервере.
|
||||
- [x] Добавить структурированные JSON-логи без пользовательских секретов и персональных технических данных (whitelist полей, redaction, request ID; Uvicorn access-log отключён).
|
||||
- [x] Добавить Gitea Actions CI: backend tests, Astro check/build, E2E и применение всех миграций на чистой PostgreSQL; сохранять логи Compose и Playwright-артефакты при падении (`.gitea/workflows/ci.yml`).
|
||||
- [x] Добавить отдельный тест полного bootstrap: пустые production volumes → миграции `0010` → seed без демо-уловов → readiness → браузерная отправка и проверка moderation API (`deploy/test-production-bootstrap.sh`, 6 сентября 2026).
|
||||
@@ -79,7 +80,7 @@
|
||||
- [ ] Добавить smoke-проверку административной очереди внешних источников на desktop/mobile без публикации реальных записей.
|
||||
- [ ] Обновить README: архитектура, все переменные окружения, импорт, модерация, backup/restore, эксплуатация логов и известные ограничения.
|
||||
- [ ] Выбрать лицензию кода и политику использования данных.
|
||||
- [ ] Завершить production-профиль для `rf4spotter.ru`: Compose, Caddy/TLS, закрытые внутренние сервисы, CORS, resource limits, fail-fast секреты, backup/restore, runbook и полный bootstrap готовы; остаётся проверка DNS/TLS на целевом сервере.
|
||||
- [ ] Завершить production-профиль для `rf4spotter.ru`: Compose, Caddy/TLS, закрытые сервисы, CORS, resource limits, fail-fast секреты, backup/restore, bootstrap и host-side monitor готовы; остаются канал уведомлений и проверка DNS/TLS на целевом сервере.
|
||||
- [ ] Заменить демонстрационные секреты и определить целевое размещение перед внешней публикацией.
|
||||
|
||||
## Источники данных и согласование
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# Мониторинг закрытой альфы
|
||||
|
||||
`deploy/monitor.sh` — host-side проверка production-контура. Она не изменяет данные и не печатает секреты. Успех возвращает exit code `0` и одну строку `OK`; любая проблема возвращает `1`/`2` и строку `CRITICAL`.
|
||||
|
||||
Проверяются:
|
||||
|
||||
- контейнеры `proxy`, `db`, `minio`, `api`, `web` находятся в running;
|
||||
- публичный `https://rf4spotter.ru/ready` сообщает `ready`;
|
||||
- файловая система проекта заполнена менее чем на 85%;
|
||||
- последняя полная резервная копия не старше 26 часов и проходит проверку `SHA256SUMS`;
|
||||
- TLS-сертификат домена действует ещё минимум 14 дней.
|
||||
|
||||
Пороги задаются в `.env.production`: `BACKUP_ROOT`, `MONITOR_DISK_MAX_PERCENT`, `MONITOR_BACKUP_MAX_HOURS`, `MONITOR_TLS_MIN_DAYS`. Проверка TLS использует SNI и поэтому обнаруживает как истечение, так и выдачу сертификата для неверного домена.
|
||||
|
||||
## Ручная проверка
|
||||
|
||||
```bash
|
||||
./deploy/monitor.sh
|
||||
echo $?
|
||||
```
|
||||
|
||||
Первый успешный запуск следует сделать только после появления DNS, TLS и первой резервной копии. До этого `CRITICAL` для этих компонентов ожидаем.
|
||||
|
||||
## systemd timer
|
||||
|
||||
Шаблоны рассчитаны на пользователя `rf4spotter` и каталог `/opt/rf4-spotter`. При другом размещении измените оба пути в service-файле.
|
||||
|
||||
```bash
|
||||
sudo cp deploy/systemd/rf4spotter-monitor.service /etc/systemd/system/
|
||||
sudo cp deploy/systemd/rf4spotter-monitor.timer /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable --now rf4spotter-monitor.timer
|
||||
systemctl list-timers rf4spotter-monitor.timer
|
||||
journalctl -u rf4spotter-monitor.service -n 20 --no-pager
|
||||
```
|
||||
|
||||
Сам timer записывает результат в journal. Для реального оповещения подключите failed unit к существующему серверному мониторингу либо настройте внешний HTTPS-monitor на `/ready`; уведомления должны приходить минимум по состояниям readiness, disk, backup и TLS. Не передавайте `.env.production` или вывод `docker compose config` внешнему сервису.
|
||||
|
||||
## Реакция
|
||||
|
||||
1. `service:*` — посмотреть `docker compose ... ps` и логи конкретного контейнера; не выполнять `down -v`.
|
||||
2. `readiness` — проверить `/ready`, PostgreSQL и MinIO; на время сбоя остановить приём пользовательских заявок.
|
||||
3. `disk:*` — сначала перенести старые backup во внешнее хранилище; не удалять Docker volumes вслепую.
|
||||
4. `backup:*` — запустить `deploy/backup.sh`, проверить `SHA256SUMS` и устранить причину пропуска расписания.
|
||||
5. `tls:*` — проверить DNS, доступность портов 80/443 и логи Caddy; не отключать проверку сертификата.
|
||||
Reference in New Issue
Block a user