#!/bin/bash # # Deployment script for mockupAWS # Usage: ./deploy.sh [environment] [version] # set -euo pipefail # Configuration ENVIRONMENT=${1:-production} VERSION=${2:-latest} PROJECT_NAME="mockupaws" AWS_REGION="us-east-1" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color # Logging functions log_info() { echo -e "${GREEN}[INFO]${NC} $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } # Check prerequisites check_prerequisites() { log_info "Checking prerequisites..." # Check AWS CLI if ! command -v aws &> /dev/null; then log_error "AWS CLI is not installed" exit 1 fi # Check Docker if ! command -v docker &> /dev/null; then log_error "Docker is not installed" exit 1 fi # Check AWS credentials if ! aws sts get-caller-identity &> /dev/null; then log_error "AWS credentials not configured" exit 1 fi log_info "Prerequisites check passed" } # Deploy to ECS deploy_ecs() { log_info "Deploying to ECS ($ENVIRONMENT)..." CLUSTER_NAME="${PROJECT_NAME}-${ENVIRONMENT}" SERVICE_NAME="backend" # Update service aws ecs update-service \ --cluster "$CLUSTER_NAME" \ --service "$SERVICE_NAME" \ --force-new-deployment \ --region "$AWS_REGION" log_info "Waiting for service to stabilize..." aws ecs wait services-stable \ --cluster "$CLUSTER_NAME" \ --services "$SERVICE_NAME" \ --region "$AWS_REGION" log_info "ECS deployment complete" } # Deploy to Docker Compose (Single Server) deploy_docker_compose() { log_info "Deploying with Docker Compose ($ENVIRONMENT)..." COMPOSE_FILE="docker-compose.${ENVIRONMENT}.yml" if [ ! -f "$COMPOSE_FILE" ]; then log_error "Compose file not found: $COMPOSE_FILE" exit 1 fi # Pull latest images log_info "Pulling latest images..." docker-compose -f "$COMPOSE_FILE" pull # Run migrations log_info "Running database migrations..." docker-compose -f "$COMPOSE_FILE" run --rm backend alembic upgrade head # Deploy log_info "Starting services..." docker-compose -f "$COMPOSE_FILE" up -d # Health check log_info "Performing health check..." sleep 10 MAX_RETRIES=30 RETRY_COUNT=0 while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do if curl -f http://localhost:8000/api/v1/health > /dev/null 2>&1; then log_info "Health check passed" break fi RETRY_COUNT=$((RETRY_COUNT + 1)) log_warn "Health check attempt $RETRY_COUNT/$MAX_RETRIES failed, retrying..." sleep 5 done if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then log_error "Health check failed after $MAX_RETRIES attempts" exit 1 fi # Cleanup old images log_info "Cleaning up old images..." docker image prune -f log_info "Docker Compose deployment complete" } # Blue-Green Deployment deploy_blue_green() { log_info "Starting blue-green deployment..." CLUSTER_NAME="${PROJECT_NAME}-${ENVIRONMENT}" SERVICE_NAME="backend" # Get current task definition CURRENT_TASK_DEF=$(aws ecs describe-services \ --cluster "$CLUSTER_NAME" \ --services "$SERVICE_NAME" \ --query 'services[0].taskDefinition' \ --output text \ --region "$AWS_REGION") log_info "Current task definition: $CURRENT_TASK_DEF" # Register new task definition with blue/green labels NEW_TASK_DEF=$(aws ecs describe-task-definition \ --task-definition "$CURRENT_TASK_DEF" \ --query 'taskDefinition' \ --region "$AWS_REGION" | \ jq '.family = "'"$SERVICE_NAME"'-green" | del(.taskDefinitionArn, .revision, .status, .requiresAttributes, .compatibilities, .registeredAt, .registeredBy)') echo "$NEW_TASK_DEF" > /tmp/new-task-def.json NEW_TASK_DEF_ARN=$(aws ecs register-task-definition \ --cli-input-json file:///tmp/new-task-def.json \ --query 'taskDefinition.taskDefinitionArn' \ --output text \ --region "$AWS_REGION") log_info "Registered new task definition: $NEW_TASK_DEF_ARN" # Create green service GREEN_SERVICE_NAME="${SERVICE_NAME}-green" aws ecs create-service \ --cluster "$CLUSTER_NAME" \ --service-name "$GREEN_SERVICE_NAME" \ --task-definition "$NEW_TASK_DEF_ARN" \ --desired-count 2 \ --launch-type FARGATE \ --network-configuration "awsvpcConfiguration={subnets=[$(aws ecs describe-services --cluster $CLUSTER_NAME --services $SERVICE_NAME --query 'services[0].networkConfiguration.awsvpcConfiguration.subnets' --output text --region $AWS_REGION)],securityGroups=[$(aws ecs describe-services --cluster $CLUSTER_NAME --services $SERVICE_NAME --query 'services[0].networkConfiguration.awsvpcConfiguration.securityGroups' --output text --region $AWS_REGION)],assignPublicIp=DISABLED}" \ --region "$AWS_REGION" 2>/dev/null || \ aws ecs update-service \ --cluster "$CLUSTER_NAME" \ --service "$GREEN_SERVICE_NAME" \ --task-definition "$NEW_TASK_DEF_ARN" \ --force-new-deployment \ --region "$AWS_REGION" log_info "Waiting for green service to stabilize..." aws ecs wait services-stable \ --cluster "$CLUSTER_NAME" \ --services "$GREEN_SERVICE_NAME" \ --region "$AWS_REGION" # Health check on green log_info "Performing health check on green service..." # Note: In production, you'd use the green service endpoint sleep 10 # Switch traffic (in production, update ALB target group) log_info "Switching traffic to green service..." # Update blue service to match green aws ecs update-service \ --cluster "$CLUSTER_NAME" \ --service "$SERVICE_NAME" \ --task-definition "$NEW_TASK_DEF_ARN" \ --force-new-deployment \ --region "$AWS_REGION" log_info "Waiting for blue service to stabilize..." aws ecs wait services-stable \ --cluster "$CLUSTER_NAME" \ --services "$SERVICE_NAME" \ --region "$AWS_REGION" # Remove green service log_info "Removing green service..." aws ecs delete-service \ --cluster "$CLUSTER_NAME" \ --service "$GREEN_SERVICE_NAME" \ --force \ --region "$AWS_REGION" log_info "Blue-green deployment complete" } # Rollback deployment rollback() { log_warn "Initiating rollback..." CLUSTER_NAME="${PROJECT_NAME}-${ENVIRONMENT}" SERVICE_NAME="backend" # Get previous task definition TASK_DEFS=$(aws ecs list-task-definitions \ --family-prefix "$SERVICE_NAME" \ --sort DESC \ --query 'taskDefinitionArns[1]' \ --output text \ --region "$AWS_REGION") if [ -z "$TASK_DEFS" ] || [ "$TASK_DEFS" = "None" ]; then log_error "No previous task definition found for rollback" exit 1 fi log_info "Rolling back to: $TASK_DEFS" # Update service to previous revision aws ecs update-service \ --cluster "$CLUSTER_NAME" \ --service "$SERVICE_NAME" \ --task-definition "$TASK_DEFS" \ --force-new-deployment \ --region "$AWS_REGION" log_info "Waiting for rollback to complete..." aws ecs wait services-stable \ --cluster "$CLUSTER_NAME" \ --services "$SERVICE_NAME" \ --region "$AWS_REGION" log_info "Rollback complete" } # Main deployment logic main() { log_info "Starting deployment: $PROJECT_NAME $VERSION to $ENVIRONMENT" check_prerequisites case "${DEPLOYMENT_TYPE:-ecs}" in ecs) deploy_ecs ;; docker-compose) deploy_docker_compose ;; blue-green) deploy_blue_green ;; rollback) rollback ;; *) log_error "Unknown deployment type: $DEPLOYMENT_TYPE" log_info "Supported types: ecs, docker-compose, blue-green, rollback" exit 1 ;; esac log_info "Deployment completed successfully!" } # Show usage usage() { echo "Usage: $0 [environment] [version]" echo "" echo "Arguments:" echo " environment Target environment (dev, staging, production)" echo " version Version to deploy (default: latest)" echo "" echo "Environment Variables:" echo " DEPLOYMENT_TYPE Deployment method (ecs, docker-compose, blue-green, rollback)" echo " AWS_REGION AWS region (default: us-east-1)" echo "" echo "Examples:" echo " $0 production v1.0.0" echo " DEPLOYMENT_TYPE=docker-compose $0 production" echo " DEPLOYMENT_TYPE=rollback $0 production" } # Handle arguments if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then usage exit 0 fi # Run main function main