- 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>
81 lines
3.8 KiB
Bash
Executable file
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
|