#!/bin/bash set -e DEPLOY_HOME="/home/kjh2064" PORT_FILE="$DEPLOY_HOME/taxbaik_port" TIMESTAMP=$(TZ=Asia/Seoul date +%Y%m%d_%H%M%S) echo "===== πŸš€ TaxBaik Green/Blue Deployment Script =====" if [ "${TAXBAIK_DEPLOY_FROM_CI:-}" != "1" ]; then echo "❌ This deployment script may only be run from CI." >&2 exit 1 fi # 1. Determine active port ACTIVE_PORT=5003 if [ -f "$PORT_FILE" ]; then ACTIVE_PORT=$(cat "$PORT_FILE" | tr -d '[:space:]') fi # 2. Determine target port TARGET_PORT=5003 if [ "$ACTIVE_PORT" -eq 5003 ]; then TARGET_PORT=5004 else TARGET_PORT=5003 fi echo "Active Port: $ACTIVE_PORT" echo "Target Port: $TARGET_PORT" # 3. New deploy dir is passed as first argument DEPLOY_DIR="$1" if [ -z "$DEPLOY_DIR" ]; then echo "Error: Deployment directory argument required" exit 1 fi echo "Deploy Directory: $DEPLOY_DIR" if [ ! -s "$DEPLOY_DIR/appsettings.Production.json" ]; then echo "❌ Missing production settings: $DEPLOY_DIR/appsettings.Production.json" >&2 exit 1 fi if [ ! -s "$DEPLOY_DIR/proxy/TaxBaik.Proxy.dll" ]; then echo "❌ Missing proxy artifact: $DEPLOY_DIR/proxy/TaxBaik.Proxy.dll" >&2 exit 1 fi is_taxbaik_proxy_on_5001() { local pids pids=$(ss -tlnp 2>/dev/null | grep ':5001 ' | grep -oP 'pid=\K\d+' | sort -u || true) [ -n "$pids" ] || return 1 for pid in $pids; do if tr '\0' ' ' < "/proc/$pid/cmdline" 2>/dev/null | grep -q 'TaxBaik.Proxy.dll'; then return 0 fi done return 1 } # 0. Ensure the local TCP proxy exists and is running. # Nginx and external traffic always enter through 127.0.0.1:5001. if ss -tln | grep -q ':5001 ' && ! is_taxbaik_proxy_on_5001; then echo "⚠️ Port 5001 is occupied by a non-proxy process. Attempting to stop legacy taxbaik.service..." echo " Current listener:" >&2 ss -tlnp 2>/dev/null | grep ':5001 ' >&2 || true if command -v systemctl >/dev/null 2>&1 && systemctl is-active --quiet taxbaik 2>/dev/null; then if command -v sudo >/dev/null 2>&1 && sudo -n true 2>/dev/null; then sudo -n systemctl stop taxbaik || true sudo -n systemctl disable taxbaik || true sleep 2 else echo " sudo -n is unavailable; cannot stop legacy taxbaik.service automatically." >&2 fi fi if ss -tln | grep -q ':5001 ' && ! is_taxbaik_proxy_on_5001; then echo "❌ Port 5001 is still occupied by a non-proxy process. Abort deploy to avoid routing traffic to the wrong app." >&2 echo " Expected: TaxBaik.Proxy.dll on 127.0.0.1:5001" >&2 echo " Manual fix: sudo systemctl stop taxbaik && sudo systemctl disable taxbaik" >&2 ss -tlnp 2>/dev/null | grep ':5001 ' >&2 || true exit 1 fi fi if ! is_taxbaik_proxy_on_5001; then echo "=== Starting proxy on 127.0.0.1:5001 ===" cd "$DEPLOY_DIR/proxy" nohup /usr/bin/dotnet TaxBaik.Proxy.dll > "$DEPLOY_HOME/taxbaik_proxy.log" 2>&1 & sleep 2 fi if ! is_taxbaik_proxy_on_5001; then echo "❌ Proxy on 127.0.0.1:5001 is not running. Abort deploy." >&2 ss -tlnp 2>/dev/null | grep ':5001 ' >&2 || true exit 1 fi # 4. Start the new app on the target port echo "=== Starting New App on Port $TARGET_PORT ===" cd "$DEPLOY_DIR" export ASPNETCORE_ENVIRONMENT=Production export ASPNETCORE_URLS="http://127.0.0.1:$TARGET_PORT" export ConnectionStrings__Default="Host=localhost;Database=taxbaikdb;Username=taxbaik;Password=taxbaik123" export ApiClient__BaseUrl="http://127.0.0.1:$TARGET_PORT/api/" export DOTNET_PRINT_TELEMETRY_MESSAGE=false # Run dotnet process nohup /usr/bin/dotnet TaxBaik.Web.dll > "web_${TARGET_PORT}.log" 2>&1 & NEW_PID=$! sleep 2 # Verify process is running if ! ps -p $NEW_PID > /dev/null; then echo "❌ Failed to start dotnet process on port $TARGET_PORT" exit 1 fi # 5. Enhanced Health Check Loop (닀쀑 μ—”λ“œν¬μΈνŠΈ 검증) echo "=== Enhanced Health Checking Port $TARGET_PORT ===" ATTEMPTS=20 SUCCESS=false API_PORT=$TARGET_PORT for i in $(seq 1 $ATTEMPTS); do # κΈ°λ³Έ ν—¬μŠ€ 체크 HEALTH_STATUS=$(curl -sf -o /dev/null -w '%{http_code}' "http://127.0.0.1:${TARGET_PORT}/healthz" 2>/dev/null || echo "000") # API μ—”λ“œν¬μΈνŠΈ 검증 (GET /api/blog - λ°μ΄ν„°λ² μ΄μŠ€ μ—°κ²° 확인) API_STATUS=$(curl -sf -o /dev/null -w '%{http_code}' "http://127.0.0.1:${TARGET_PORT}/api/blog?limit=1" 2>/dev/null || echo "000") if [ "$HEALTH_STATUS" = "200" ] && [ "$API_STATUS" = "200" ]; then echo "βœ“ All health checks passed on port $TARGET_PORT (Attempt $i/$ATTEMPTS)" echo " βœ“ /healthz: $HEALTH_STATUS" echo " βœ“ /api/blog: $API_STATUS" SUCCESS=true break fi if [ $i -eq 1 ] || [ $((i % 5)) -eq 0 ]; then echo " Waiting for health check... ($i/$ATTEMPTS)" echo " /healthz: $HEALTH_STATUS | /api/blog: $API_STATUS" fi sleep 2 done if [ "$SUCCESS" = "false" ]; then echo "❌ Enhanced health check failed on port $TARGET_PORT" echo " /healthz: $HEALTH_STATUS | /api/blog: $API_STATUS" echo "" echo "Log output (last 30 lines):" tail -30 "web_${TARGET_PORT}.log" 2>/dev/null || echo "No log file" echo "" echo "πŸ”™ Rolling back..." kill -9 $NEW_PID || true exit 1 fi # 6. Pre-Switch Validation (포트 μ „ν™˜ μ „ μ΅œμ’… 검증) echo "=== Pre-Switch Validation ===" # 6.1 둜그 νŒŒμΌμ—μ„œ FATAL/ERROR 확인 echo " Checking logs for critical errors..." if grep -i "fatal\|unhandled exception" "web_${TARGET_PORT}.log" 2>/dev/null; then echo "❌ Critical errors found in application logs" echo "πŸ”™ Rolling back..." kill -9 $NEW_PID || true exit 1 fi echo " βœ“ No critical errors in logs" # 6.2 Nginx μ„€μ • 검증 echo " Checking Nginx configuration..." if ! sudo nginx -t 2>&1 | grep -q "successful"; then echo "❌ Nginx configuration is invalid" echo "πŸ”™ Rolling back..." kill -9 $NEW_PID || true exit 1 fi echo " βœ“ Nginx configuration is valid" # 6.3 ν”„λ‘œμ„ΈμŠ€κ°€ μ—¬μ „νžˆ μ‹€ν–‰ 쀑인지 확인 if ! ps -p $NEW_PID > /dev/null; then echo "❌ New process has terminated unexpectedly" echo "πŸ”™ Rolling back..." exit 1 fi echo " βœ“ New process is running (PID: $NEW_PID)" echo "βœ“ All pre-switch validations passed" echo "" # 7. Switch Traffic # Nginx never needs per-deploy changes: it always proxies to the persistent # TaxBaik.Proxy on 127.0.0.1:5001, which reads this same PORT_FILE and # forwards to whichever port is currently active. See CLAUDE.md section 6. echo "=== Switching Traffic to Port $TARGET_PORT ===" echo "$TARGET_PORT" > "$PORT_FILE" echo "βœ“ Traffic routed to $TARGET_PORT (via TaxBaik.Proxy on 5001)" # 7. Terminate Old App echo "=== Stopping Old App on Port $ACTIVE_PORT ===" # Find PID listening on ACTIVE_PORT OLD_PID=$(ss -tlnp | grep ":$ACTIVE_PORT " | grep -oP 'pid=\K\d+' | head -n1) if [ -n "$OLD_PID" ]; then echo "Killing old process PID: $OLD_PID" kill -15 $OLD_PID || kill -9 $OLD_PID echo "βœ“ Old process terminated" else echo "No old process found on port $ACTIVE_PORT" fi # 8. Cleanup old deployment directories (Keep last 5) echo "=== Cleaning Up Old Deployments ===" ls -1dt $DEPLOY_HOME/deployments/taxbaik_* 2>/dev/null | tail -n +6 | xargs rm -rf 2>/dev/null || true echo "βœ“ Cleanup completed" echo "===== βœ… Green/Blue Deployment Completed Successfully ====="