veza/scripts/archive/deploy-production.sh

266 lines
7.3 KiB
Bash
Raw Normal View History

#!/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