veza/scripts/archive/deploy-production.sh
2025-12-12 21:34:34 -05:00

265 lines
7.3 KiB
Bash
Executable file
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
ENVIRONMENT=${1:-production}
COMPOSE_FILE="docker-compose.production.yml"
BACKUP_DIR="./backups/$(date +%Y%m%d_%H%M%S)"
ROLLBACK_DIR="./rollback-backups"
MAX_BACKUPS=10
# Function to print colored messages
print_info() {
echo -e "${BLUE} $1${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
# Function to check prerequisites
check_prerequisites() {
print_info "Checking prerequisites..."
command -v docker >/dev/null 2>&1 || {
print_error "Docker is required but not installed. Aborting.";
exit 1;
}
command -v docker-compose >/dev/null 2>&1 || {
print_error "Docker Compose is required but not installed. Aborting.";
exit 1;
}
if ! docker info >/dev/null 2>&1; then
print_error "Docker daemon is not running. Please start Docker and try again.";
exit 1;
fi
if [ ! -f "$COMPOSE_FILE" ]; then
print_error "$COMPOSE_FILE not found. Aborting.";
exit 1;
fi
print_success "Prerequisites check passed"
}
# Function to create backup
create_backup() {
print_info "Creating backup..."
mkdir -p "${BACKUP_DIR}"
mkdir -p "${ROLLBACK_DIR}"
# Backup database
print_info "Backing up database..."
if docker-compose -f "$COMPOSE_FILE" ps postgres | grep -q "Up"; then
docker-compose -f "$COMPOSE_FILE" exec -T postgres pg_dump -U "${POSTGRES_USER:-veza_user}" "${POSTGRES_DB:-veza_db}" > "${BACKUP_DIR}/database.sql" 2>/dev/null || {
print_warning "Database backup failed (may not be critical if database is empty)"
}
else
print_warning "PostgreSQL container is not running, skipping database backup"
fi
# Backup Redis data
print_info "Backing up Redis data..."
if docker-compose -f "$COMPOSE_FILE" ps redis | grep -q "Up"; then
docker-compose -f "$COMPOSE_FILE" exec -T redis redis-cli --rdb - > "${BACKUP_DIR}/redis.rdb" 2>/dev/null || {
print_warning "Redis backup failed (may not be critical)"
}
else
print_warning "Redis container is not running, skipping Redis backup"
fi
# Save current image tags
print_info "Saving current image tags..."
docker-compose -f "$COMPOSE_FILE" config | grep "image:" > "${BACKUP_DIR}/image-tags.txt" || true
# Copy current docker-compose file
cp "$COMPOSE_FILE" "${BACKUP_DIR}/docker-compose.production.yml"
# Create rollback backup
cp -r "${BACKUP_DIR}" "${ROLLBACK_DIR}/latest"
print_success "Backup created at ${BACKUP_DIR}"
}
# Function to cleanup old backups
cleanup_old_backups() {
print_info "Cleaning up old backups (keeping last ${MAX_BACKUPS})..."
if [ -d "./backups" ]; then
cd ./backups
ls -t | tail -n +$((MAX_BACKUPS + 1)) | xargs rm -rf 2>/dev/null || true
cd ..
fi
print_success "Old backups cleaned up"
}
# Function to pull latest images
pull_latest_images() {
print_info "Pulling latest images..."
docker-compose -f "$COMPOSE_FILE" pull || {
print_error "Failed to pull images";
exit 1;
}
print_success "Images pulled successfully"
}
# Function to deploy services
deploy_services() {
print_info "Deploying services with zero-downtime strategy..."
# Deploy services one by one to minimize downtime
SERVICES=("backend-api" "chat-server" "stream-server" "frontend")
for service in "${SERVICES[@]}"; do
if docker-compose -f "$COMPOSE_FILE" ps "$service" | grep -q "Up"; then
print_info "Updating ${service}..."
docker-compose -f "$COMPOSE_FILE" up -d --no-deps "$service" || {
print_error "Failed to deploy ${service}";
return 1;
}
sleep 5
fi
done
print_success "Services deployed successfully"
}
# Function to check health
check_health() {
local service=$1
local url=$2
local name=$3
local max_attempts=${4:-30}
local attempt=0
print_info "Checking ${name} health..."
while [ $attempt -lt $max_attempts ]; do
if curl -sf "$url" >/dev/null 2>&1; then
print_success "${name} is healthy"
return 0
fi
attempt=$((attempt + 1))
sleep 2
done
print_error "${name} health check failed after ${max_attempts} attempts"
return 1
}
# Function to verify deployment
verify_deployment() {
print_info "Verifying deployment..."
local failed=0
# Check container status
if docker-compose -f "$COMPOSE_FILE" ps | grep -q "unhealthy\|Exited\|Restarting"; then
print_error "Some containers are unhealthy or stopped"
docker-compose -f "$COMPOSE_FILE" ps
failed=1
fi
# Check health endpoints
check_health "backend-api" "http://localhost:8080/health" "Backend API" 15 || failed=1
check_health "chat-server" "http://localhost:8081/health" "Chat Server" 15 || failed=1
check_health "stream-server" "http://localhost:8082/health" "Stream Server" 15 || failed=1
check_health "frontend" "http://localhost:80/health" "Frontend" 15 || failed=1
if [ $failed -eq 1 ]; then
return 1
fi
print_success "Deployment verification passed"
return 0
}
# Function to rollback
rollback() {
print_error "Deployment failed! Initiating rollback..."
if [ ! -d "${ROLLBACK_DIR}/latest" ]; then
print_error "No rollback backup found. Manual intervention required."
exit 1
fi
print_info "Rolling back to previous version..."
# Stop current services
docker-compose -f "$COMPOSE_FILE" down || true
# Restore docker-compose file
if [ -f "${ROLLBACK_DIR}/latest/docker-compose.production.yml" ]; then
cp "${ROLLBACK_DIR}/latest/docker-compose.production.yml" "$COMPOSE_FILE"
fi
# Restore database
if [ -f "${ROLLBACK_DIR}/latest/database.sql" ]; then
print_info "Restoring database..."
docker-compose -f "$COMPOSE_FILE" up -d postgres
sleep 10
docker-compose -f "$COMPOSE_FILE" exec -T postgres psql -U "${POSTGRES_USER:-veza_user}" -d "${POSTGRES_DB:-veza_db}" < "${ROLLBACK_DIR}/latest/database.sql" || true
fi
# Start services with previous images
docker-compose -f "$COMPOSE_FILE" up -d
print_warning "Rollback completed. Please verify services manually."
exit 1
}
# Main deployment flow
main() {
print_info "Starting production deployment to ${ENVIRONMENT}..."
# Trap errors and rollback
trap rollback ERR
check_prerequisites
create_backup
cleanup_old_backups
pull_latest_images
deploy_services
# Wait for services to start
print_info "Waiting for services to start..."
sleep 30
# Verify deployment
if ! verify_deployment; then
rollback
fi
# Disable trap on success
trap - ERR
print_success "Deployment to ${ENVIRONMENT} completed successfully!"
print_info "Backup location: ${BACKUP_DIR}"
print_info "To rollback, run: ${ROLLBACK_DIR}/latest"
}
# Run main function
main