food-market/deploy/prod-monitor.sh
nns b49abfdbd7
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
feat(prod): запуск admin.food-market.kz — бэкапы (hourly/daily/офсайт), restore-verify, мониторинг, deploy-prod
- prod-backup.sh (tier hourly 48ч / daily 30д + uploads), prod-backup-offsite-pull.sh (DR на dev-vm)
- prod-restore-verify.sh: restore во временную БД + сверка числа строк, дроп
- prod-monitor.sh: api/web/public/БД/диск/5xx → Telegram владельцу (+ --test)
- deploy-prod.sh: guard'ы (prod-postgres, .env chmod600, не dev-пароль, quick-login off,
  SEED_DEMO_DATA=false, issuer), бэкап ДО миграций, только --yes, НЕ в ночных
- systemd таймеры (backup-hourly/daily, monitor, offsite-pull); docs/prod-runbook.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 19:31:55 +05:00

81 lines
3.8 KiB
Bash
Executable file

#!/usr/bin/env bash
#
# ПРОД food-market: мониторинг здоровья. Проверяет api/web/БД, место на диске и
# число 5xx в логах api за последний интервал. При проблеме шлёт владельцу в
# Telegram (бот уже есть; token/chat — ~/.fm-watchdog/telegram-{token,chat}).
#
# Запуск из systemd-таймера (каждые ~5 мин). Аргументы:
# (без аргументов) — обычная проверка, уведомляет ТОЛЬКО при проблеме.
# --test — отправляет тестовое уведомление «мониторинг жив» и выходит.
# --heartbeat — всегда шлёт короткий статус (для ручной проверки).
set -uo pipefail
WATCH_DIR="${WATCH_DIR:-$HOME/.fm-watchdog}"
API_URL="${FM_API_URL:-http://localhost:8080/health/ready}"
WEB_URL="${FM_WEB_URL:-http://localhost:8081/}"
PUBLIC_URL="${FM_PUBLIC_HEALTH:-https://admin.food-market.kz/health/ready}"
CONTAINER="${FM_PG_CONTAINER:-food-market-prod-postgres}"
API_CONTAINER="${FM_API_CONTAINER:-food-market-api}"
DISK_PATH="${FM_DISK_PATH:-/opt/food-market-data}"
DISK_WARN_PCT="${FM_DISK_WARN_PCT:-85}"
LOG="${FM_MONITOR_LOG:-$WATCH_DIR/prod-monitor.log}"
log() { echo "[$(date -Is)] $*" | tee -a "$LOG" >/dev/null; }
notify() {
local msg="$1"
local token chat
token="$(cat "$WATCH_DIR/telegram-token" 2>/dev/null || true)"
chat="$(cat "$WATCH_DIR/telegram-chat" 2>/dev/null || true)"
if [[ -z "$token" || -z "$chat" ]]; then
log "telegram token/chat не настроены — notify пропущен: $msg"; return
fi
curl -sS --max-time 10 -X POST "https://api.telegram.org/bot$token/sendMessage" \
--data-urlencode "chat_id=$chat" \
--data-urlencode "text=$msg" >/dev/null 2>&1 \
&& log "notify отправлено: $msg" || log "notify FAILED: $msg"
}
if [[ "${1:-}" == "--test" ]]; then
notify "✅ ALL MARKET прод: тест мониторинга $(date '+%Y-%m-%d %H:%M'). Если видите это — уведомления работают."
exit 0
fi
problems=()
# API /health/ready
if ! curl -fsS --max-time 10 "$API_URL" 2>/dev/null | grep -q '"status":"Healthy"'; then
problems+=("API /health/ready не Healthy ($API_URL)")
fi
# Web SPA
code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 "$WEB_URL" 2>/dev/null || echo 000)
[[ "$code" =~ ^(200|301|302)$ ]] || problems+=("web вернул HTTP $code ($WEB_URL)")
# Публичный вход (через gateway)
pcode=$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 "$PUBLIC_URL" 2>/dev/null || echo 000)
[[ "$pcode" =~ ^(200)$ ]] || problems+=("публичный $PUBLIC_URL → HTTP $pcode")
# БД
if ! docker exec "$CONTAINER" pg_isready -U food_market -d food_market >/dev/null 2>&1; then
problems+=("БД $CONTAINER не отвечает (pg_isready)")
fi
# Диск
pct=$(df --output=pcent "$DISK_PATH" 2>/dev/null | tail -1 | tr -dc '0-9')
if [[ -n "$pct" && "$pct" -ge "$DISK_WARN_PCT" ]]; then
problems+=("диск $DISK_PATH заполнен на ${pct}% (порог ${DISK_WARN_PCT}%)")
fi
# 5xx в логах api за последние 5 минут
fivexx=$(docker logs "$API_CONTAINER" --since 5m 2>&1 | grep -cE ' (50[0-9]) ' || true)
if [[ -n "$fivexx" && "$fivexx" -gt 0 ]]; then
problems+=("$fivexx ответов 5xx в логах api за 5 мин")
fi
if [[ ${#problems[@]} -gt 0 ]]; then
msg="🔴 ALL MARKET прод — проблемы:"$'\n'"$(printf -- '- %s\n' "${problems[@]}")"
log "ПРОБЛЕМЫ: ${problems[*]}"
notify "$msg"
exit 1
fi
log "OK: api/web/public/БД/диск(${pct}%)/5xx=0"
[[ "${1:-}" == "--heartbeat" ]] && notify "🟢 ALL MARKET прод OK $(date '+%H:%M') (диск ${pct}%)"
exit 0