feat: production infra — nginx configs, deploy script, security hardening
Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 58s
Deploy / Build Web Image (push) Failing after 14s
Deploy / Rollback Production (push) Has been skipped
CI / E2E Tests (push) Has been skipped
Deploy / Build API Image (push) Failing after 3m8s
Deploy / Build AI Services Image (push) Failing after 10s
E2E Tests / Playwright E2E (push) Failing after 1m21s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Deploy / Rollback Staging (push) Has been skipped
Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 58s
Deploy / Build Web Image (push) Failing after 14s
Deploy / Rollback Production (push) Has been skipped
CI / E2E Tests (push) Has been skipped
Deploy / Build API Image (push) Failing after 3m8s
Deploy / Build AI Services Image (push) Failing after 10s
E2E Tests / Playwright E2E (push) Failing after 1m21s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Deploy / Rollback Staging (push) Has been skipped
- Add Nginx reverse-proxy configs for api.goodgo.vn and platform.goodgo.vn with SSL, gzip, rate limiting, security headers, and WebSocket support - Add Cloudflare DNS setup script for A/AAAA/CNAME records - Add server-setup.sh for Ubuntu provisioning (Docker, fail2ban, UFW, swap, unattended-upgrades) - Add deploy-production.sh for manual production deployments - Add env.production.example with all required environment variables - Bind container ports to 127.0.0.1 in docker-compose.prod.yml (security: prevent direct access bypassing Nginx) - Fix deploy workflow: add -T flag to exec, sync Nginx configs, copy pgbouncer and backup configs to server Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
This commit is contained in:
152
scripts/deploy-production.sh
Executable file
152
scripts/deploy-production.sh
Executable file
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env bash
|
||||
# ==============================================================================
|
||||
# GoodGo Platform — Manual Production Deploy Script
|
||||
# Backup for CI/CD pipeline. Use when GitHub Actions is unavailable.
|
||||
#
|
||||
# Usage (from the server):
|
||||
# cd ~/goodgo
|
||||
# ./deploy-production.sh [image-tag]
|
||||
#
|
||||
# Usage (from local machine):
|
||||
# ssh ubuntu@185.225.232.65 'cd ~/goodgo && ./deploy-production.sh abc1234'
|
||||
# ==============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── Configuration ─────────────────────────────────────────────────────────────
|
||||
COMPOSE_FILE="docker-compose.prod.yml"
|
||||
IMAGE_TAG="${1:-latest}"
|
||||
HEALTH_URL="http://127.0.0.1:3001/health"
|
||||
HEALTH_RETRIES=15
|
||||
HEALTH_INTERVAL=5
|
||||
ROLLBACK_ON_FAIL=true
|
||||
|
||||
# ── Colors ────────────────────────────────────────────────────────────────────
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
log() { echo -e "${GREEN}[DEPLOY]${NC} $(date +%H:%M:%S) $*"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $(date +%H:%M:%S) $*"; }
|
||||
err() { echo -e "${RED}[ERROR]${NC} $(date +%H:%M:%S) $*" >&2; }
|
||||
info() { echo -e "${CYAN}[INFO]${NC} $(date +%H:%M:%S) $*"; }
|
||||
|
||||
# ── Pre-flight Checks ────────────────────────────────────────────────────────
|
||||
if [ ! -f "$COMPOSE_FILE" ]; then
|
||||
err "Compose file not found: $COMPOSE_FILE"
|
||||
err "Are you in the ~/goodgo directory?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f ".env" ]; then
|
||||
err ".env file not found. Copy from infra/env.production.example"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "=========================================="
|
||||
log " GoodGo Platform — Production Deploy"
|
||||
log " Image tag: ${IMAGE_TAG}"
|
||||
log " Compose: ${COMPOSE_FILE}"
|
||||
log "=========================================="
|
||||
echo ""
|
||||
|
||||
# ── Step 1: Record Current State (for rollback) ──────────────────────────────
|
||||
log "Step 1/6: Recording current state for rollback..."
|
||||
PREV_API=$(docker inspect --format='{{.Config.Image}}' goodgo-api 2>/dev/null || echo "none")
|
||||
PREV_WEB=$(docker inspect --format='{{.Config.Image}}' goodgo-web 2>/dev/null || echo "none")
|
||||
PREV_AI=$(docker inspect --format='{{.Config.Image}}' goodgo-ai-services 2>/dev/null || echo "none")
|
||||
info "Previous API: ${PREV_API}"
|
||||
info "Previous Web: ${PREV_WEB}"
|
||||
info "Previous AI: ${PREV_AI}"
|
||||
|
||||
# ── Step 2: Pull New Images ──────────────────────────────────────────────────
|
||||
log "Step 2/6: Pulling new images (tag: ${IMAGE_TAG})..."
|
||||
export IMAGE_TAG
|
||||
docker compose -f "$COMPOSE_FILE" pull api web ai-services
|
||||
log "Images pulled successfully."
|
||||
|
||||
# ── Step 3: Rolling Update ───────────────────────────────────────────────────
|
||||
log "Step 3/6: Rolling update (zero-downtime)..."
|
||||
|
||||
info "Updating API..."
|
||||
docker compose -f "$COMPOSE_FILE" up -d --no-deps --wait api
|
||||
info "API updated and healthy."
|
||||
|
||||
info "Updating Web..."
|
||||
docker compose -f "$COMPOSE_FILE" up -d --no-deps --wait web
|
||||
info "Web updated and healthy."
|
||||
|
||||
info "Updating AI Services..."
|
||||
docker compose -f "$COMPOSE_FILE" up -d --no-deps --wait ai-services
|
||||
info "AI Services updated and healthy."
|
||||
|
||||
log "Rolling update complete."
|
||||
|
||||
# ── Step 4: Database Migrations ──────────────────────────────────────────────
|
||||
log "Step 4/6: Running database migrations..."
|
||||
docker compose -f "$COMPOSE_FILE" exec -T api npx prisma migrate deploy
|
||||
log "Migrations complete."
|
||||
|
||||
# ── Step 5: Health Check Verification ────────────────────────────────────────
|
||||
log "Step 5/6: Verifying deployment health..."
|
||||
HEALTHY=false
|
||||
for i in $(seq 1 "$HEALTH_RETRIES"); do
|
||||
if curl -sf "$HEALTH_URL" > /dev/null 2>&1; then
|
||||
HEALTHY=true
|
||||
break
|
||||
fi
|
||||
info "Waiting for health check... (${i}/${HEALTH_RETRIES})"
|
||||
sleep "$HEALTH_INTERVAL"
|
||||
done
|
||||
|
||||
if $HEALTHY; then
|
||||
log "Health check passed!"
|
||||
else
|
||||
err "Health check failed after ${HEALTH_RETRIES} attempts!"
|
||||
|
||||
if $ROLLBACK_ON_FAIL; then
|
||||
warn "Initiating rollback..."
|
||||
|
||||
# Rollback: stop current, docker compose will use previously cached images
|
||||
docker compose -f "$COMPOSE_FILE" stop api web ai-services
|
||||
docker compose -f "$COMPOSE_FILE" up -d --wait api web ai-services
|
||||
|
||||
warn "Rollback complete. Verifying..."
|
||||
sleep 5
|
||||
if curl -sf "$HEALTH_URL" > /dev/null 2>&1; then
|
||||
warn "Services recovered after rollback."
|
||||
else
|
||||
err "CRITICAL: Services still unhealthy after rollback!"
|
||||
err "Manual intervention required."
|
||||
fi
|
||||
fi
|
||||
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ── Step 6: Cleanup ──────────────────────────────────────────────────────────
|
||||
log "Step 6/6: Cleaning up old images..."
|
||||
docker image prune -f
|
||||
log "Cleanup complete."
|
||||
|
||||
# ── Summary ──────────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
log "=========================================="
|
||||
log " Deployment successful!"
|
||||
log "=========================================="
|
||||
log ""
|
||||
log " Services:"
|
||||
info " API: $(docker inspect --format='{{.Config.Image}}' goodgo-api)"
|
||||
info " Web: $(docker inspect --format='{{.Config.Image}}' goodgo-web)"
|
||||
info " AI: $(docker inspect --format='{{.Config.Image}}' goodgo-ai-services)"
|
||||
log ""
|
||||
log " Endpoints:"
|
||||
info " Web: https://platform.goodgo.vn"
|
||||
info " API: https://api.goodgo.vn"
|
||||
info " Grafana: https://grafana.goodgo.vn"
|
||||
log ""
|
||||
log " Run smoke tests:"
|
||||
info " ./scripts/smoke-test.sh https://api.goodgo.vn"
|
||||
log "=========================================="
|
||||
Reference in New Issue
Block a user