feat(infra): GlitchTip self-hosted error tracking (#204 devops) #205
4 changed files with 193 additions and 0 deletions
|
|
@ -10,3 +10,10 @@ POSTGRES_PASSWORD=changeme
|
||||||
# YC_REGISTRY_ID=
|
# YC_REGISTRY_ID=
|
||||||
# IMAGE_TAG=latest
|
# IMAGE_TAG=latest
|
||||||
# NEXT_PUBLIC_API_BASE_URL=https://your-domain.example.ru
|
# NEXT_PUBLIC_API_BASE_URL=https://your-domain.example.ru
|
||||||
|
|
||||||
|
# GlitchTip (errors.gendsgn.ru)
|
||||||
|
GLITCHTIP_DB_PASS=
|
||||||
|
GLITCHTIP_SECRET=
|
||||||
|
# DSN-ы из GlitchTip UI после первого старта (→ .env.runtime или backend/.env)
|
||||||
|
GLITCHTIP_DSN=
|
||||||
|
NEXT_PUBLIC_GLITCHTIP_DSN=
|
||||||
|
|
|
||||||
12
Caddyfile
12
Caddyfile
|
|
@ -44,6 +44,18 @@ obsidian.gendsgn.ru {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# GlitchTip — self-hosted error tracking (Sentry-compatible).
|
||||||
|
# DNS: A-record errors.gendsgn.ru → IP VPS.
|
||||||
|
errors.gendsgn.ru {
|
||||||
|
encode zstd gzip
|
||||||
|
|
||||||
|
reverse_proxy glitchtip-web:8080
|
||||||
|
|
||||||
|
log {
|
||||||
|
output file /var/log/caddy/errors.gendsgn.ru.log
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Plain HTTP by IP — kept for ssh-tunnel / debugging.
|
# Plain HTTP by IP — kept for ssh-tunnel / debugging.
|
||||||
# Caddy issues no TLS here (no hostname).
|
# Caddy issues no TLS here (no hostname).
|
||||||
:80 {
|
:80 {
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,57 @@ services:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
command: ["celery", "-A", "app.workers.celery_app", "beat", "--loglevel=info"]
|
command: ["celery", "-A", "app.workers.celery_app", "beat", "--loglevel=info"]
|
||||||
|
|
||||||
|
glitchtip-web:
|
||||||
|
image: glitchtip/glitchtip:6.1.6
|
||||||
|
container_name: glitchtip-web
|
||||||
|
# profiles: ["glitchtip"] keeps this service from starting on plain `compose up -d`.
|
||||||
|
# Bootstrap script activates the profile after DB + secrets are ready.
|
||||||
|
# On subsequent deploys, set COMPOSE_PROFILES=glitchtip in /opt/gendesign/.env.
|
||||||
|
profiles: ["glitchtip"]
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_started
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: postgres://glitchtip:${GLITCHTIP_DB_PASS}@postgres:5432/glitchtip
|
||||||
|
REDIS_URL: redis://redis:6379/2
|
||||||
|
SECRET_KEY: ${GLITCHTIP_SECRET}
|
||||||
|
PORT: "8080"
|
||||||
|
EMAIL_URL: dummy://
|
||||||
|
GLITCHTIP_DOMAIN: https://errors.gendsgn.ru
|
||||||
|
DEFAULT_FROM_EMAIL: errors@gendsgn.ru
|
||||||
|
ENABLE_USER_REGISTRATION: "false"
|
||||||
|
ENABLE_ORGANIZATION_CREATION: "false"
|
||||||
|
restart: always
|
||||||
|
mem_limit: 512m
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/api/0/"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
start_period: 60s
|
||||||
|
networks: [default]
|
||||||
|
|
||||||
|
glitchtip-worker:
|
||||||
|
image: glitchtip/glitchtip:6.1.6
|
||||||
|
container_name: glitchtip-worker
|
||||||
|
profiles: ["glitchtip"]
|
||||||
|
depends_on:
|
||||||
|
postgres:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_started
|
||||||
|
command: ./bin/run-celery-with-beat.sh
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: postgres://glitchtip:${GLITCHTIP_DB_PASS}@postgres:5432/glitchtip
|
||||||
|
REDIS_URL: redis://redis:6379/2
|
||||||
|
SECRET_KEY: ${GLITCHTIP_SECRET}
|
||||||
|
CELERY_WORKER_AUTOSCALE: "1,3"
|
||||||
|
restart: always
|
||||||
|
mem_limit: 384m
|
||||||
|
networks: [default]
|
||||||
|
|
||||||
caddy:
|
caddy:
|
||||||
image: caddy:2
|
image: caddy:2
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
|
||||||
123
scripts/bootstrap_glitchtip.sh
Normal file
123
scripts/bootstrap_glitchtip.sh
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# bootstrap_glitchtip.sh
|
||||||
|
# One-shot bootstrap for GlitchTip on the VPS.
|
||||||
|
#
|
||||||
|
# Run ONCE manually after merging feat/204-glitchtip-infra.
|
||||||
|
# Do NOT add to deploy.yml — this is idempotent setup, not a recurring step.
|
||||||
|
#
|
||||||
|
# Usage (on VPS via SSH):
|
||||||
|
# cd /opt/gendesign
|
||||||
|
# # Ensure GLITCHTIP_DB_PASS and GLITCHTIP_SECRET are set in .env
|
||||||
|
# bash scripts/bootstrap_glitchtip.sh
|
||||||
|
#
|
||||||
|
# After running:
|
||||||
|
# 1. docker compose -p gendesign -f docker-compose.prod.yml exec glitchtip-web ./manage.py createsuperuser
|
||||||
|
# 2. Log in to https://errors.gendsgn.ru → create org "gendesign"
|
||||||
|
# 3. Create projects: "backend", "frontend"
|
||||||
|
# 4. Copy DSNs → add to /opt/gendesign/backend/.env (GLITCHTIP_DSN)
|
||||||
|
# and /opt/gendesign/frontend/.env or .env.runtime (NEXT_PUBLIC_GLITCHTIP_DSN)
|
||||||
|
# 5. Recreate affected containers:
|
||||||
|
# docker compose -p gendesign -f docker-compose.prod.yml up -d --force-recreate --no-deps backend
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
COMPOSE_FILE="docker-compose.prod.yml"
|
||||||
|
COMPOSE_PROJECT="gendesign"
|
||||||
|
COMPOSE="docker compose -p $COMPOSE_PROJECT -f $COMPOSE_FILE"
|
||||||
|
|
||||||
|
# ── Load .env.runtime for GLITCHTIP_DB_PASS / POSTGRES_* ──────────────────
|
||||||
|
if [ -f ".env" ]; then
|
||||||
|
set -a; source .env; set +a
|
||||||
|
fi
|
||||||
|
if [ -f "backend/.env.runtime" ]; then
|
||||||
|
set -a; source backend/.env.runtime; set +a
|
||||||
|
fi
|
||||||
|
|
||||||
|
: "${GLITCHTIP_DB_PASS:?GLITCHTIP_DB_PASS not set — add to .env or export}"
|
||||||
|
: "${POSTGRES_USER:?POSTGRES_USER not set}"
|
||||||
|
: "${POSTGRES_PASSWORD:?POSTGRES_PASSWORD not set}"
|
||||||
|
: "${POSTGRES_DB:?POSTGRES_DB not set}"
|
||||||
|
|
||||||
|
echo "==> Ensuring postgres service is healthy..."
|
||||||
|
for i in $(seq 1 30); do
|
||||||
|
if $COMPOSE exec -T postgres pg_isready -U "$POSTGRES_USER" -q; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo " Waiting for postgres... ($i/30)"
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
# ── Create glitchtip DB + user (idempotent) ────────────────────────────────
|
||||||
|
# Password is passed via psql variable -v to avoid SQL injection from special
|
||||||
|
# chars (quotes, backslashes) in $GLITCHTIP_DB_PASS. format('%L', :'gt_pass')
|
||||||
|
# properly quotes the value server-side, so the shell never interpolates it
|
||||||
|
# into the SQL string.
|
||||||
|
echo "==> Creating glitchtip database and user (idempotent)..."
|
||||||
|
$COMPOSE exec -T postgres \
|
||||||
|
psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" \
|
||||||
|
-v "gt_pass=$GLITCHTIP_DB_PASS" \
|
||||||
|
-v ON_ERROR_STOP=on <<'SQL'
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'glitchtip') THEN
|
||||||
|
EXECUTE format('CREATE ROLE glitchtip LOGIN PASSWORD %L', :'gt_pass');
|
||||||
|
RAISE NOTICE 'Created role glitchtip';
|
||||||
|
ELSE
|
||||||
|
RAISE NOTICE 'Role glitchtip already exists';
|
||||||
|
END IF;
|
||||||
|
END$$;
|
||||||
|
|
||||||
|
SELECT 'CREATE DATABASE glitchtip OWNER glitchtip'
|
||||||
|
WHERE NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'glitchtip')
|
||||||
|
\gexec
|
||||||
|
SQL
|
||||||
|
|
||||||
|
# ── Start GlitchTip containers ─────────────────────────────────────────────
|
||||||
|
# glitchtip-web and glitchtip-worker carry `profiles: ["glitchtip"]` in compose,
|
||||||
|
# so plain `up -d` skips them. Activate the profile explicitly here.
|
||||||
|
# After bootstrap succeeds, add COMPOSE_PROFILES=glitchtip to /opt/gendesign/.env
|
||||||
|
# so that future automatic deploys also bring these services up.
|
||||||
|
echo "==> Pulling glitchtip image..."
|
||||||
|
$COMPOSE --profile glitchtip pull glitchtip-web glitchtip-worker
|
||||||
|
|
||||||
|
echo "==> Starting glitchtip-web and glitchtip-worker..."
|
||||||
|
$COMPOSE --profile glitchtip up -d glitchtip-web glitchtip-worker
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "NOTE: Add the following line to /opt/gendesign/.env so future deploys"
|
||||||
|
echo " keep GlitchTip running:"
|
||||||
|
echo " COMPOSE_PROFILES=glitchtip"
|
||||||
|
|
||||||
|
# ── Wait for glitchtip-web to become healthy ───────────────────────────────
|
||||||
|
# Use wget (available in glitchtip image) matching the compose healthcheck probe.
|
||||||
|
echo "==> Waiting for glitchtip-web to become healthy (up to 60s)..."
|
||||||
|
for i in $(seq 1 30); do
|
||||||
|
if $COMPOSE exec -T glitchtip-web wget -q --spider http://localhost:8080/api/0/ >/dev/null 2>&1; then
|
||||||
|
echo " glitchtip-web is healthy."
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo " Waiting... ($i/30)"
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "==> Bootstrap complete."
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo " 1. Create superuser:"
|
||||||
|
echo " $COMPOSE exec glitchtip-web ./manage.py createsuperuser"
|
||||||
|
echo ""
|
||||||
|
echo " 2. Log in to https://errors.gendsgn.ru"
|
||||||
|
echo " - Create organization: gendesign"
|
||||||
|
echo " - Create project: backend (Platform: Python)"
|
||||||
|
echo " - Create project: frontend (Platform: JavaScript / Browser)"
|
||||||
|
echo ""
|
||||||
|
echo " 3. Copy DSNs from each project's Settings → DSN"
|
||||||
|
echo " - Add GLITCHTIP_DSN=<backend-dsn> to /opt/gendesign/backend/.env"
|
||||||
|
echo " - Add NEXT_PUBLIC_GLITCHTIP_DSN=<frontend-dsn> to /opt/gendesign/backend/.env or .env"
|
||||||
|
echo ""
|
||||||
|
echo " 4. Recreate services to pick up new DSNs:"
|
||||||
|
echo " $COMPOSE up -d --force-recreate --no-deps backend"
|
||||||
|
echo " $COMPOSE up -d --force-recreate --no-deps worker beat"
|
||||||
|
echo ""
|
||||||
|
echo " 5. Verify: trigger a test exception and confirm it appears in GlitchTip."
|
||||||
Loading…
Add table
Reference in a new issue