From 208664848e2ff3b3495af98e1422891605dc9c34 Mon Sep 17 00:00:00 2001 From: Emiliano Frascaro Date: Sat, 13 Jun 2026 09:23:42 +0200 Subject: [PATCH] Rende persistenti i log di produzione e protegge il deploy. Monta /app/log sul disco video, sposta i cron log sullo stesso path, esclude log/ dal sync rsync e aggiunge pulizia volumi Docker obsoleti. Co-authored-by: Cursor --- docs/OPS_MONITORING.md | 5 ++-- docs/REPLAY_MODULE.md | 2 +- infra/.env.production.example | 4 +++- infra/docker-compose.prod.yml | 2 ++ infra/scripts/cleanup_obsolete_volumes.sh | 29 +++++++++++++++++++++++ infra/scripts/cron/scan_ops_logs.sh | 17 +++++++++++++ infra/scripts/install_production_cron.sh | 17 +++++++++++-- infra/scripts/migrate_videos_storage.sh | 12 +++++++++- scripts/deploy/sync_to_server.sh | 1 + 9 files changed, 82 insertions(+), 7 deletions(-) create mode 100755 infra/scripts/cleanup_obsolete_volumes.sh diff --git a/docs/OPS_MONITORING.md b/docs/OPS_MONITORING.md index 558630d..8bd25ec 100644 --- a/docs/OPS_MONITORING.md +++ b/docs/OPS_MONITORING.md @@ -114,7 +114,8 @@ Watchdog esterno se Rails è completamente giù: 5. Rebuild: `docker compose -f docker-compose.prod.yml up -d --build rails sidekiq` 6. Migrate: `docker compose exec -T rails bundle exec rails db:migrate` 7. Cron: `bash scripts/install_production_cron.sh` -8. Smoke test: +8. (Opzionale) `bash scripts/cleanup_obsolete_volumes.sh` — rimuove volumi Docker migrati +9. Smoke test: - `curl -sf https://www.matchlivetv.it/up` - `bundle exec rails ops:health_check` (nel container) - Apri `/admin/ops` @@ -130,4 +131,4 @@ Imposta `SENTRY_DSN` dopo `bundle install`. Gli errori creano anche incidenti op | Nessuna push | Verifica `OPS_NTFY_URL`, test con `ops:test_notify` | | Troppi alert | Aumenta `OPS_NOTIFY_COOLDOWN_MINUTES`, usa Mute 24h in admin | | Sidekiq stale | Verifica container `sidekiq` e log Sidekiq | -| Log scanner vuoto | Controlla `/opt/matchlivetv/log/cron-ops.log` | +| Log scanner vuoto | Controlla `${MATCHLIVETV_VIDEOS_ROOT}/log/cron-ops.log` (es. `/media/videos/matchlivetv/log/cron-ops.log`) | diff --git a/docs/REPLAY_MODULE.md b/docs/REPLAY_MODULE.md index f25172f..3916f67 100644 --- a/docs/REPLAY_MODULE.md +++ b/docs/REPLAY_MODULE.md @@ -109,7 +109,7 @@ Job attivi (utente `eminux`): | 03:00 | `recordings:purge_expired` — elimina replay scaduti (DB + Garage) | | 08:00 | `recordings:expiry_warnings` — email avviso scadenza (7 gg) | -Log: `/opt/matchlivetv/log/cron-replay.log` +Log: `${MATCHLIVETV_VIDEOS_ROOT}/log/cron-replay.log` (es. `/media/videos/matchlivetv/log/cron-replay.log`) ### Capacità Garage diff --git a/infra/.env.production.example b/infra/.env.production.example index 215c0f4..d4373e4 100644 --- a/infra/.env.production.example +++ b/infra/.env.production.example @@ -53,8 +53,10 @@ SMTP_SSL=true SMTP_STARTTLS=false PASSWORD_RESET_EXPIRY_HOURS=2 -# Disco video dedicato (recordings MediaMTX + dati Garage). Montare es. 800GB su /media/videos +# Disco video dedicato (recordings MediaMTX + dati Garage + log + Active Storage). Montare es. 800GB su /media/videos MATCHLIVETV_VIDEOS_ROOT=/media/videos/matchlivetv +# Log cron e YouTube relay ffmpeg (default: ${MATCHLIVETV_VIDEOS_ROOT}/log) +# MATCHLIVETV_LOG_DIR=/media/videos/matchlivetv/log # Replay storage — Garage (obbligatorio in produzione per Premium replay) # Dopo il primo deploy: bash scripts/setup_garage_production.sh (crea garage.prod.toml + chiavi) diff --git a/infra/docker-compose.prod.yml b/infra/docker-compose.prod.yml index cbe554f..f2940de 100644 --- a/infra/docker-compose.prod.yml +++ b/infra/docker-compose.prod.yml @@ -131,6 +131,7 @@ services: volumes: - ${MATCHLIVETV_VIDEOS_ROOT:-/media/videos/matchlivetv}/recordings:/recordings - ${MATCHLIVETV_VIDEOS_ROOT:-/media/videos/matchlivetv}/active_storage:/app/storage + - ${MATCHLIVETV_VIDEOS_ROOT:-/media/videos/matchlivetv}/log:/app/log healthcheck: test: ["CMD", "curl", "-f", "http://127.0.0.1:3000/up"] interval: 15s @@ -203,6 +204,7 @@ services: volumes: - ${MATCHLIVETV_VIDEOS_ROOT:-/media/videos/matchlivetv}/recordings:/recordings - ${MATCHLIVETV_VIDEOS_ROOT:-/media/videos/matchlivetv}/active_storage:/app/storage + - ${MATCHLIVETV_VIDEOS_ROOT:-/media/videos/matchlivetv}/log:/app/log garage: image: dxflrs/garage:v1.0.1 diff --git a/infra/scripts/cleanup_obsolete_volumes.sh b/infra/scripts/cleanup_obsolete_volumes.sh new file mode 100755 index 0000000..e560931 --- /dev/null +++ b/infra/scripts/cleanup_obsolete_volumes.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Rimuove volumi Docker obsoleti dopo migrazione storage su disco video. +set -euo pipefail + +PROJECT="${COMPOSE_PROJECT_NAME:-infra}" + +remove_if_empty() { + local name="${PROJECT}_$1" + if ! docker volume inspect "$name" >/dev/null 2>&1; then + echo "Volume ${name}: assente, salto" + return 0 + fi + + local mount + mount="$(docker volume inspect "$name" --format '{{ .Mountpoint }}')" + if [ -n "$(sudo ls -A "$mount" 2>/dev/null || true)" ]; then + echo "Volume ${name}: non vuoto (${mount}), salto" + return 0 + fi + + docker volume rm "$name" + echo "Volume ${name}: rimosso" +} + +echo "Pulizia volumi Docker obsoleti (progetto ${PROJECT})..." +remove_if_empty rails_storage +remove_if_empty recordings +remove_if_empty garage_data +remove_if_empty garage_meta diff --git a/infra/scripts/cron/scan_ops_logs.sh b/infra/scripts/cron/scan_ops_logs.sh index e7fdbb3..b04752a 100644 --- a/infra/scripts/cron/scan_ops_logs.sh +++ b/infra/scripts/cron/scan_ops_logs.sh @@ -4,13 +4,30 @@ set -euo pipefail ROOT="${MATCHLIVETV_INFRA:-/opt/matchlivetv/infra}" SINCE="${OPS_LOG_SCAN_SINCE:-10m}" +ENV_FILE="${ROOT}/.env" +VIDEOS_ROOT="/media/videos/matchlivetv" TMP="$(mktemp)" trap 'rm -f "$TMP"' EXIT +if [ -f "$ENV_FILE" ]; then + line="$(grep -E '^MATCHLIVETV_VIDEOS_ROOT=' "$ENV_FILE" 2>/dev/null | tail -1 || true)" + if [ -n "$line" ]; then + VIDEOS_ROOT="${line#MATCHLIVETV_VIDEOS_ROOT=}" + VIDEOS_ROOT="${VIDEOS_ROOT%\"}" + VIDEOS_ROOT="${VIDEOS_ROOT#\"}" + fi +fi + +LOG_DIR="${MATCHLIVETV_LOG_DIR:-${VIDEOS_ROOT}/log}" + cd "$ROOT" docker compose -f docker-compose.prod.yml logs --since "$SINCE" rails sidekiq mediamtx 2>/dev/null > "$TMP" || true +if compgen -G "${LOG_DIR}/youtube_relay_*.log" > /dev/null 2>&1; then + cat "${LOG_DIR}"/youtube_relay_*.log >> "$TMP" 2>/dev/null || true +fi + if [[ ! -s "$TMP" ]]; then exit 0 fi diff --git a/infra/scripts/install_production_cron.sh b/infra/scripts/install_production_cron.sh index bb9d159..c5cc1ee 100755 --- a/infra/scripts/install_production_cron.sh +++ b/infra/scripts/install_production_cron.sh @@ -5,7 +5,19 @@ set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" RUNNER="${ROOT}/scripts/cron/run_rails_task.sh" SCAN_LOGS="${ROOT}/scripts/cron/scan_ops_logs.sh" -LOG_DIR="${MATCHLIVETV_LOG_DIR:-/opt/matchlivetv/log}" +ENV_FILE="${ROOT}/.env" +VIDEOS_ROOT="/media/videos/matchlivetv" + +if [ -f "$ENV_FILE" ]; then + line="$(grep -E '^MATCHLIVETV_VIDEOS_ROOT=' "$ENV_FILE" 2>/dev/null | tail -1 || true)" + if [ -n "$line" ]; then + VIDEOS_ROOT="${line#MATCHLIVETV_VIDEOS_ROOT=}" + VIDEOS_ROOT="${VIDEOS_ROOT%\"}" + VIDEOS_ROOT="${VIDEOS_ROOT#\"}" + fi +fi + +LOG_DIR="${MATCHLIVETV_LOG_DIR:-${VIDEOS_ROOT}/log}" LOG_FILE="${LOG_DIR}/cron-replay.log" OPS_LOG_FILE="${LOG_DIR}/cron-ops.log" @@ -30,5 +42,6 @@ fi echo "$CRON_BLOCK" } | sed '/^$/d' | crontab - +echo "Log persistenti: ${LOG_DIR}" echo "Crontab installato:" -crontab -l | grep -A3 "$MARKER" +crontab -l | grep -A4 "$MARKER" diff --git a/infra/scripts/migrate_videos_storage.sh b/infra/scripts/migrate_videos_storage.sh index d63878e..339cd77 100755 --- a/infra/scripts/migrate_videos_storage.sh +++ b/infra/scripts/migrate_videos_storage.sh @@ -35,7 +35,17 @@ fs_rsync() { sudo rsync -aH "$@" } -fs_mkdir "${VIDEOS_ROOT}/recordings" "${VIDEOS_ROOT}/garage/meta" "${VIDEOS_ROOT}/garage/data" "${VIDEOS_ROOT}/active_storage" +fs_mkdir "${VIDEOS_ROOT}/recordings" "${VIDEOS_ROOT}/garage/meta" "${VIDEOS_ROOT}/garage/data" "${VIDEOS_ROOT}/active_storage" "${VIDEOS_ROOT}/log" + +legacy_log_dir="/opt/matchlivetv/log" +if [ -d "$legacy_log_dir" ] && [ -n "$(ls -A "$legacy_log_dir" 2>/dev/null || true)" ]; then + if [ -z "$(ls -A "${VIDEOS_ROOT}/log" 2>/dev/null || true)" ]; then + echo "Migro log legacy ${legacy_log_dir} → ${VIDEOS_ROOT}/log..." + fs_rsync "${legacy_log_dir}/" "${VIDEOS_ROOT}/log/" + else + echo "Log già presenti in ${VIDEOS_ROOT}/log, salto migrazione da ${legacy_log_dir}" + fi +fi if [ ! -w "$VIDEOS_ROOT" ]; then sudo chown -R "$(id -un)":"$(id -gn)" "$VIDEOS_ROOT" fi diff --git a/scripts/deploy/sync_to_server.sh b/scripts/deploy/sync_to_server.sh index 0a760b3..e10a975 100755 --- a/scripts/deploy/sync_to_server.sh +++ b/scripts/deploy/sync_to_server.sh @@ -17,6 +17,7 @@ if ssh "$SERVER" 'command -v rsync >/dev/null 2>&1'; then --exclude 'backend/tmp' \ --exclude 'backend/.bundle' \ --exclude 'infra/.env' \ + --exclude 'log/' \ --exclude 'node_modules' \ "$SRC/" "$SERVER:$DEST/" else