The --force-recreate flag alone doesn't handle containers that exist outside the compose project context. Now explicitly: 1. Run docker compose down first 2. Force remove any orphaned containers with known names 3. Then create fresh containers
189 lines
5.5 KiB
Bash
Executable File
189 lines
5.5 KiB
Bash
Executable File
#!/bin/bash
|
||
# ==============================================================================
|
||
# Application Deployment Script
|
||
# ==============================================================================
|
||
# Deploys application to staging or production environment
|
||
# Usage: ./deploy.sh <environment> [options]
|
||
# ==============================================================================
|
||
|
||
set -e
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||
cd "$PROJECT_ROOT"
|
||
|
||
# Colors for output
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# Function to print colored output
|
||
print_info() {
|
||
echo -e "${BLUE}ℹ${NC} $1"
|
||
}
|
||
|
||
print_success() {
|
||
echo -e "${GREEN}✅${NC} $1"
|
||
}
|
||
|
||
print_warning() {
|
||
echo -e "${YELLOW}⚠️${NC} $1"
|
||
}
|
||
|
||
print_error() {
|
||
echo -e "${RED}❌${NC} $1"
|
||
}
|
||
|
||
# Parse arguments
|
||
ENVIRONMENT=$1
|
||
BUILD_IMAGES=${2:-false}
|
||
|
||
if [ -z "$ENVIRONMENT" ]; then
|
||
print_error "Usage: $0 <environment> [build]"
|
||
print_info "Environments: staging, production"
|
||
print_info "Options: build - Build Docker images before deployment"
|
||
exit 1
|
||
fi
|
||
|
||
if [ "$ENVIRONMENT" != "staging" ] && [ "$ENVIRONMENT" != "production" ]; then
|
||
print_error "Invalid environment: $ENVIRONMENT"
|
||
print_info "Valid environments: staging, production"
|
||
exit 1
|
||
fi
|
||
|
||
# Set compose files
|
||
COMPOSE_BASE="docker-compose.base.yml"
|
||
if [ "$ENVIRONMENT" = "staging" ]; then
|
||
COMPOSE_ENV="docker-compose.staging.yml"
|
||
elif [ "$ENVIRONMENT" = "production" ]; then
|
||
COMPOSE_ENV="docker-compose.prod.yml"
|
||
fi
|
||
|
||
COMPOSE_FILES="-f $COMPOSE_BASE -f $COMPOSE_ENV"
|
||
|
||
print_info "Deploying to $ENVIRONMENT environment..."
|
||
|
||
# Check if secrets exist
|
||
SECRETS_DIR="deployment/secrets/$ENVIRONMENT"
|
||
if [ ! -d "$SECRETS_DIR" ]; then
|
||
print_warning "Secrets directory not found: $SECRETS_DIR"
|
||
print_info "Creating secrets directory..."
|
||
mkdir -p "$SECRETS_DIR"
|
||
fi
|
||
|
||
MISSING_SECRETS=()
|
||
REQUIRED_SECRETS=("db_password.txt" "redis_password.txt" "app_key.txt")
|
||
|
||
for secret_file in "${REQUIRED_SECRETS[@]}"; do
|
||
if [ ! -f "$SECRETS_DIR/$secret_file" ]; then
|
||
MISSING_SECRETS+=("$secret_file")
|
||
fi
|
||
done
|
||
|
||
if [ ${#MISSING_SECRETS[@]} -gt 0 ]; then
|
||
print_error "Missing required secrets:"
|
||
for secret in "${MISSING_SECRETS[@]}"; do
|
||
print_error " - $SECRETS_DIR/$secret"
|
||
done
|
||
print_info "See deployment/infrastructure/SECRETS.md for instructions"
|
||
exit 1
|
||
fi
|
||
|
||
# Check if infrastructure networks exist
|
||
print_info "Checking infrastructure networks..."
|
||
if ! docker network ls | grep -q "traefik-public"; then
|
||
print_error "traefik-public network not found"
|
||
print_info "Please deploy infrastructure stacks first:"
|
||
print_info " cd deployment/infrastructure && ./deploy.sh traefik"
|
||
exit 1
|
||
fi
|
||
|
||
if ! docker network ls | grep -q "app-internal"; then
|
||
print_error "app-internal network not found"
|
||
print_info "Please deploy infrastructure stacks first:"
|
||
print_info " cd deployment/infrastructure && ./deploy.sh postgresql"
|
||
exit 1
|
||
fi
|
||
|
||
# Build images if requested
|
||
if [ "$BUILD_IMAGES" = "build" ]; then
|
||
print_info "Building Docker images..."
|
||
docker compose $COMPOSE_FILES build
|
||
fi
|
||
|
||
# Pull latest images
|
||
print_info "Pulling latest images..."
|
||
docker compose $COMPOSE_FILES pull || print_warning "Failed to pull some images, continuing..."
|
||
|
||
# Stop and remove existing containers to prevent name conflicts
|
||
print_info "Stopping existing containers..."
|
||
docker compose $COMPOSE_FILES down --remove-orphans || print_warning "No existing containers to stop"
|
||
|
||
# Remove any orphaned containers with conflicting names
|
||
for container in nginx php redis scheduler queue-worker; do
|
||
if docker ps -a --format '{{.Names}}' | grep -q "^${container}$"; then
|
||
print_warning "Removing orphaned container: $container"
|
||
docker rm -f "$container" 2>/dev/null || true
|
||
fi
|
||
done
|
||
|
||
# Deploy stack
|
||
print_info "Deploying application stack..."
|
||
docker compose $COMPOSE_FILES up -d --force-recreate --remove-orphans
|
||
|
||
# Wait for services to be healthy
|
||
print_info "Waiting for services to be healthy..."
|
||
sleep 10
|
||
|
||
# Check service status
|
||
print_info "Checking service status..."
|
||
docker compose $COMPOSE_FILES ps
|
||
|
||
# Health checks
|
||
print_info "Running health checks..."
|
||
HEALTH_CHECK_FAILED=0
|
||
|
||
# Check PHP service
|
||
if docker compose $COMPOSE_FILES exec -T php php -v > /dev/null 2>&1; then
|
||
print_success "PHP service is healthy"
|
||
else
|
||
print_error "PHP service health check failed"
|
||
HEALTH_CHECK_FAILED=1
|
||
fi
|
||
|
||
# Check Redis service
|
||
if docker compose $COMPOSE_FILES exec -T redis redis-cli ping > /dev/null 2>&1; then
|
||
print_success "Redis service is healthy"
|
||
else
|
||
print_error "Redis service health check failed"
|
||
HEALTH_CHECK_FAILED=1
|
||
fi
|
||
|
||
# Check Nginx service
|
||
if docker compose $COMPOSE_FILES exec -T nginx wget --quiet --tries=1 --spider http://localhost/health > /dev/null 2>&1; then
|
||
print_success "Nginx service is healthy"
|
||
else
|
||
print_warning "Nginx health check endpoint not available (this may be normal)"
|
||
fi
|
||
|
||
if [ $HEALTH_CHECK_FAILED -eq 1 ]; then
|
||
print_error "Some health checks failed. Check logs:"
|
||
print_info " docker compose $COMPOSE_FILES logs"
|
||
exit 1
|
||
fi
|
||
|
||
print_success "Deployment to $ENVIRONMENT completed successfully!"
|
||
|
||
# Show service URLs
|
||
if [ "$ENVIRONMENT" = "production" ]; then
|
||
print_info "Application URL: https://michaelschiemer.de"
|
||
elif [ "$ENVIRONMENT" = "staging" ]; then
|
||
print_info "Application URL: https://staging.michaelschiemer.de"
|
||
fi
|
||
|
||
print_info "View logs: docker compose $COMPOSE_FILES logs -f"
|
||
print_info "View status: docker compose $COMPOSE_FILES ps"
|
||
|