Aggiunge Garage al compose di produzione e script di bootstrap.
Rails e Sidekiq usano S3 interno (http://garage:3900); setup_garage_production.sh configura bucket, chiavi e infra/.env sul server. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -21,6 +21,8 @@ infra/recordings/
|
|||||||
infra/.env
|
infra/.env
|
||||||
infra/.env.production.generated
|
infra/.env.production.generated
|
||||||
infra/garage/dev-credentials.env
|
infra/garage/dev-credentials.env
|
||||||
|
infra/garage/prod-credentials.env
|
||||||
|
infra/garage/garage.prod.toml
|
||||||
|
|
||||||
# OS
|
# OS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
@@ -74,6 +74,14 @@ docker compose up -d rails sidekiq
|
|||||||
docker compose exec -T rails bundle exec rails replay:e2e_garage
|
docker compose exec -T rails bundle exec rails replay:e2e_garage
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Produzione (`/opt/matchlivetv`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd infra
|
||||||
|
bash scripts/setup_garage_production.sh # garage.prod.toml, bucket, chiavi → .env
|
||||||
|
# Rails/Sidekiq usano REPLAY_STORAGE_ENDPOINT=http://garage:3900 (rete Docker)
|
||||||
|
```
|
||||||
|
|
||||||
**Credenziali in `.env`:** non compilarle a mano in `.env.example`. Lo script scrive in `infra/.env`:
|
**Credenziali in `.env`:** non compilarle a mano in `.env.example`. Lo script scrive in `infra/.env`:
|
||||||
- `REPLAY_STORAGE_ACCESS_KEY_ID` = Key ID Garage (es. `GK...`)
|
- `REPLAY_STORAGE_ACCESS_KEY_ID` = Key ID Garage (es. `GK...`)
|
||||||
- `REPLAY_STORAGE_SECRET_ACCESS_KEY` = Secret mostrato una sola volta alla creazione chiave
|
- `REPLAY_STORAGE_SECRET_ACCESS_KEY` = Secret mostrato una sola volta alla creazione chiave
|
||||||
|
|||||||
@@ -50,3 +50,12 @@ SMTP_PASSWORD=
|
|||||||
SMTP_AUTH=plain
|
SMTP_AUTH=plain
|
||||||
SMTP_STARTTLS=true
|
SMTP_STARTTLS=true
|
||||||
PASSWORD_RESET_EXPIRY_HOURS=2
|
PASSWORD_RESET_EXPIRY_HOURS=2
|
||||||
|
|
||||||
|
# Replay storage — Garage (obbligatorio in produzione per Premium replay)
|
||||||
|
# Dopo il primo deploy: bash scripts/setup_garage_production.sh (crea garage.prod.toml + chiavi)
|
||||||
|
REPLAY_STORAGE_ENDPOINT=http://garage:3900
|
||||||
|
REPLAY_STORAGE_BUCKET=matchlivetv-replays
|
||||||
|
REPLAY_STORAGE_REGION=garage
|
||||||
|
REPLAY_STORAGE_ACCESS_KEY_ID=
|
||||||
|
REPLAY_STORAGE_SECRET_ACCESS_KEY=
|
||||||
|
REPLAY_STORAGE_FORCE_PATH_STYLE=true
|
||||||
|
|||||||
@@ -111,6 +111,8 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
mediamtx:
|
mediamtx:
|
||||||
condition: service_started
|
condition: service_started
|
||||||
|
garage:
|
||||||
|
condition: service_started
|
||||||
volumes:
|
volumes:
|
||||||
- recordings:/recordings
|
- recordings:/recordings
|
||||||
healthcheck:
|
healthcheck:
|
||||||
@@ -151,10 +153,24 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
rails:
|
rails:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
garage:
|
||||||
|
condition: service_started
|
||||||
volumes:
|
volumes:
|
||||||
- recordings:/recordings
|
- recordings:/recordings
|
||||||
|
|
||||||
|
garage:
|
||||||
|
image: dxflrs/garage:v1.0.1
|
||||||
|
restart: unless-stopped
|
||||||
|
# API S3 solo rete Docker (rails/sidekiq → http://garage:3900). Non pubblicare 3900 su WAN.
|
||||||
|
volumes:
|
||||||
|
- garage_meta:/var/lib/garage/meta
|
||||||
|
- garage_data:/var/lib/garage/data
|
||||||
|
- ./garage/garage.prod.toml:/etc/garage.toml:ro
|
||||||
|
command: ["/garage", "-c", "/etc/garage.toml", "server"]
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
redis_data:
|
redis_data:
|
||||||
recordings:
|
recordings:
|
||||||
|
garage_meta:
|
||||||
|
garage_data:
|
||||||
|
|||||||
22
infra/garage/garage.prod.toml.example
Normal file
22
infra/garage/garage.prod.toml.example
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# Copia in garage.prod.toml sul server e sostituisci i placeholder (o usa scripts/ensure_garage_prod_config.sh)
|
||||||
|
metadata_dir = "/var/lib/garage/meta"
|
||||||
|
data_dir = "/var/lib/garage/data"
|
||||||
|
db_engine = "sqlite"
|
||||||
|
replication_factor = 1
|
||||||
|
|
||||||
|
rpc_bind_addr = "[::]:3901"
|
||||||
|
rpc_public_addr = "garage:3901"
|
||||||
|
rpc_secret = "CHANGE_ME_RPC_SECRET_64_HEX"
|
||||||
|
|
||||||
|
[s3_api]
|
||||||
|
api_bind_addr = "[::]:3900"
|
||||||
|
s3_region = "garage"
|
||||||
|
root_domain = ".s3.matchlivetv.internal"
|
||||||
|
|
||||||
|
[s3_web]
|
||||||
|
bind_addr = "[::]:3902"
|
||||||
|
root_domain = ".web.matchlivetv.internal"
|
||||||
|
|
||||||
|
[admin]
|
||||||
|
api_bind_addr = "[::]:3903"
|
||||||
|
admin_token = "CHANGE_ME_ADMIN_TOKEN_64_HEX"
|
||||||
25
infra/scripts/ensure_garage_prod_config.sh
Executable file
25
infra/scripts/ensure_garage_prod_config.sh
Executable file
@@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Crea garage/garage.prod.toml con secret casuali se mancante (solo produzione).
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
DEST="${ROOT}/garage/garage.prod.toml"
|
||||||
|
TEMPLATE="${ROOT}/garage/garage.prod.toml.example"
|
||||||
|
|
||||||
|
if [ -f "$DEST" ]; then
|
||||||
|
echo "Config Garage produzione già presente: $DEST"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$TEMPLATE" ]; then
|
||||||
|
echo "Manca $TEMPLATE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
RPC="$(openssl rand -hex 32)"
|
||||||
|
ADMIN="$(openssl rand -hex 32)"
|
||||||
|
sed -e "s/CHANGE_ME_RPC_SECRET_64_HEX/${RPC}/" \
|
||||||
|
-e "s/CHANGE_ME_ADMIN_TOKEN_64_HEX/${ADMIN}/" \
|
||||||
|
"$TEMPLATE" > "$DEST"
|
||||||
|
chmod 600 "$DEST"
|
||||||
|
echo "Creato $DEST"
|
||||||
24
infra/scripts/setup_garage_production.sh
Executable file
24
infra/scripts/setup_garage_production.sh
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Garage replay storage su server produzione (/opt/matchlivetv/infra)
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
|
cd "$ROOT"
|
||||||
|
|
||||||
|
export COMPOSE_FILE="docker-compose.prod.yml"
|
||||||
|
export ENV_FILE="${ROOT}/.env"
|
||||||
|
export CREDS_FILE="${ROOT}/garage/prod-credentials.env"
|
||||||
|
|
||||||
|
bash "${ROOT}/scripts/ensure_garage_prod_config.sh"
|
||||||
|
|
||||||
|
echo "Avvio Garage..."
|
||||||
|
docker compose -f docker-compose.prod.yml --env-file .env up -d garage
|
||||||
|
|
||||||
|
bash "${ROOT}/scripts/setup_garage_replays.sh"
|
||||||
|
|
||||||
|
echo "Riavvio Rails e Sidekiq..."
|
||||||
|
docker compose -f docker-compose.prod.yml --env-file .env up -d rails sidekiq
|
||||||
|
|
||||||
|
echo "Verifica storage:"
|
||||||
|
docker compose -f docker-compose.prod.yml --env-file .env exec -T rails bundle exec rails runner \
|
||||||
|
'puts({local: MatchLiveTv.replay_storage_local?, backend: Recordings::Storage.new.backend_name}.inspect)'
|
||||||
@@ -3,11 +3,23 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
GARAGE_CONTAINER="${GARAGE_CONTAINER:-infra-garage-1}"
|
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.yml}"
|
||||||
|
GARAGE_CONTAINER="${GARAGE_CONTAINER:-}"
|
||||||
BUCKET="${REPLAY_STORAGE_BUCKET:-matchlivetv-replays}"
|
BUCKET="${REPLAY_STORAGE_BUCKET:-matchlivetv-replays}"
|
||||||
KEY_NAME="${REPLAY_STORAGE_KEY_NAME:-replay-uploader}"
|
KEY_NAME="${REPLAY_STORAGE_KEY_NAME:-replay-uploader}"
|
||||||
CREDS_FILE="${ROOT}/garage/dev-credentials.env"
|
CREDS_FILE="${CREDS_FILE:-${ROOT}/garage/dev-credentials.env}"
|
||||||
ENV_FILE="${ROOT}/.env"
|
ENV_FILE="${ENV_FILE:-${ROOT}/.env}"
|
||||||
|
|
||||||
|
compose() {
|
||||||
|
docker compose -f "${ROOT}/${COMPOSE_FILE}" --env-file "${ENV_FILE}" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve_garage_container() {
|
||||||
|
if [ -n "$GARAGE_CONTAINER" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
GARAGE_CONTAINER="$(docker ps --format '{{.Names}}' | grep -E 'garage-1$' | head -1 || true)"
|
||||||
|
}
|
||||||
|
|
||||||
gexec() {
|
gexec() {
|
||||||
docker exec "$GARAGE_CONTAINER" /garage "$@"
|
docker exec "$GARAGE_CONTAINER" /garage "$@"
|
||||||
@@ -21,7 +33,7 @@ wait_garage() {
|
|||||||
fi
|
fi
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
echo "Garage non risponde. Avvia: cd $ROOT && docker compose up -d garage"
|
echo "Garage non risponde. Avvia: cd $ROOT && docker compose -f $COMPOSE_FILE up -d garage"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,9 +138,11 @@ EOF
|
|||||||
|
|
||||||
main() {
|
main() {
|
||||||
cd "$ROOT"
|
cd "$ROOT"
|
||||||
if ! docker ps --format '{{.Names}}' | grep -q "^${GARAGE_CONTAINER}$"; then
|
resolve_garage_container
|
||||||
echo "Avvio container garage..."
|
if [ -z "$GARAGE_CONTAINER" ] || ! docker ps --format '{{.Names}}' | grep -q "^${GARAGE_CONTAINER}$"; then
|
||||||
docker compose up -d garage
|
echo "Avvio container garage ($COMPOSE_FILE)..."
|
||||||
|
compose up -d garage
|
||||||
|
resolve_garage_container
|
||||||
fi
|
fi
|
||||||
wait_garage
|
wait_garage
|
||||||
ensure_layout
|
ensure_layout
|
||||||
@@ -137,7 +151,7 @@ main() {
|
|||||||
write_credentials
|
write_credentials
|
||||||
merge_into_env
|
merge_into_env
|
||||||
echo ""
|
echo ""
|
||||||
echo "Riavvia app: docker compose up -d rails sidekiq"
|
echo "Riavvia app: docker compose -f $COMPOSE_FILE up -d rails sidekiq"
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
Reference in New Issue
Block a user