Skip to content

Merge pull request #1 from MSA-Service-12th/feat/keycloak #13

Merge pull request #1 from MSA-Service-12th/feat/keycloak

Merge pull request #1 from MSA-Service-12th/feat/keycloak #13

Workflow file for this run

# Launch Template v2: IAM Instance Profile 추가 완료 (loopang-gateway-ec2-profile)
name: Gateway CI/CD (Blue/Green)
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
env:
AWS_REGION: ap-northeast-2
ECR_REPOSITORY: loopang-gateway-ecr
ALB_ARN: arn:aws:elasticloadbalancing:ap-northeast-2:775935273810:loadbalancer/app/loopang-gateway-alb/0dc6211a130898f3
LAUNCH_TEMPLATE_ID: lt-025b757cc7af3b750
VPC_ID: vpc-0b5a9f471e94527aa
SUBNET_IDS: "subnet-00134fbc900699eb1,subnet-07502e6a5c5560eb8,subnet-04bc8446301c7faeb,subnet-09d786eb10a8ba3e6"
jobs:
# =============================================
# CI: 빌드 & 테스트 (모든 브랜치)
# =============================================
ci:
name: Build & Test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Cache Gradle packages
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Build with Gradle
run: ./gradlew build --no-daemon
- name: Run tests
run: ./gradlew test --no-daemon
# =============================================
# CD: Blue/Green 배포 (main push 시에만)
# 흐름: Docker Build → ECR Push → Green TG/ASG 생성
# → 헬스체크 → ALB 트래픽 전환 → Blue 정리
# =============================================
cd:
name: Blue/Green Deploy
needs: ci
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Build & Push Docker image to ECR
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
IMAGE_TAG: ${{ github.sha }}
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:latest
docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
# ========================================
# 1단계: 현재 Blue 환경 정보 수집
# ========================================
- name: Get current Blue deployment info
id: blue
run: |
# HTTPS(443) 리스너 ARN 조회
LISTENER_ARN=$(aws elbv2 describe-listeners \
--load-balancer-arn $ALB_ARN \
--query "Listeners[?Port==\`443\`].ListenerArn | [0]" \
--output text)
echo "listener_arn=$LISTENER_ARN" >> $GITHUB_OUTPUT
# 현재 활성 Target Group ARN 조회 (= Blue)
BLUE_TG_ARN=$(aws elbv2 describe-listeners \
--load-balancer-arn $ALB_ARN \
--query "Listeners[?Port==\`443\`].DefaultActions[0].TargetGroupArn | [0]" \
--output text)
echo "blue_tg_arn=$BLUE_TG_ARN" >> $GITHUB_OUTPUT
# Blue TG에 연결된 ASG 이름 조회
BLUE_ASG=$(aws autoscaling describe-auto-scaling-groups \
--query "AutoScalingGroups[?contains(TargetGroupARNs, '${BLUE_TG_ARN}')].AutoScalingGroupName | [0]" \
--output text)
echo "blue_asg=$BLUE_ASG" >> $GITHUB_OUTPUT
echo "=== 현재 Blue 환경 ==="
echo " Listener ARN: $LISTENER_ARN"
echo " Blue TG ARN: $BLUE_TG_ARN"
echo " Blue ASG: $BLUE_ASG"
# ========================================
# 2단계: Green 환경 생성
# ========================================
- name: Create Green Target Group
id: green-tg
run: |
GREEN_TG_NAME="lp-gw-green-tg-${{ github.run_number }}"
GREEN_TG_ARN=$(aws elbv2 create-target-group \
--name "$GREEN_TG_NAME" \
--protocol HTTP \
--port 18080 \
--vpc-id $VPC_ID \
--target-type instance \
--health-check-protocol HTTP \
--health-check-port "18080" \
--health-check-path /actuator/health \
--health-check-interval-seconds 20 \
--health-check-timeout-seconds 5 \
--healthy-threshold-count 2 \
--unhealthy-threshold-count 2 \
--matcher HttpCode=200 \
--query "TargetGroups[0].TargetGroupArn" \
--output text)
echo "green_tg_arn=$GREEN_TG_ARN" >> $GITHUB_OUTPUT
echo "green_tg_name=$GREEN_TG_NAME" >> $GITHUB_OUTPUT
echo "Green TG 생성 완료: $GREEN_TG_NAME"
- name: Create Green ASG
id: green-asg
run: |
GREEN_ASG_NAME="lp-gw-green-asg-${{ github.run_number }}"
aws autoscaling create-auto-scaling-group \
--auto-scaling-group-name "$GREEN_ASG_NAME" \
--launch-template "LaunchTemplateId=${LAUNCH_TEMPLATE_ID},Version=\$Latest" \
--min-size 2 \
--max-size 5 \
--desired-capacity 2 \
--vpc-zone-identifier "$SUBNET_IDS" \
--target-group-arns "${{ steps.green-tg.outputs.green_tg_arn }}" \
--health-check-type ELB \
--health-check-grace-period 300 \
--tags \
"Key=Name,Value=loopang-gateway-green,PropagateAtLaunch=true" \
"Key=Environment,Value=green,PropagateAtLaunch=true" \
"Key=DeployRun,Value=${{ github.run_number }},PropagateAtLaunch=true"
# Green ASG에 Target Tracking 스케일링 정책 추가 (CPU 70%)
aws autoscaling put-scaling-policy \
--auto-scaling-group-name "$GREEN_ASG_NAME" \
--policy-name "${GREEN_ASG_NAME}-cpu-target-tracking" \
--policy-type TargetTrackingScaling \
--target-tracking-configuration '{
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ASGAverageCPUUtilization"
},
"TargetValue": 70.0
}'
echo "green_asg=$GREEN_ASG_NAME" >> $GITHUB_OUTPUT
echo "Green ASG 생성 완료: $GREEN_ASG_NAME (인스턴스 2대 + CPU 70% 스케일링)"
# ========================================
# 2.5단계: Green TG를 ALB에 임시 연결 (헬스체크 활성화)
# Weight 1로 연결하여 ALB 헬스체크만 활성화 (트래픽 거의 없음)
# ========================================
- name: Register Green TG with ALB for health checks
run: |
LISTENER_ARN="${{ steps.blue.outputs.listener_arn }}"
BLUE_TG_ARN="${{ steps.blue.outputs.blue_tg_arn }}"
GREEN_TG_ARN="${{ steps.green-tg.outputs.green_tg_arn }}"
aws elbv2 modify-listener \
--listener-arn "$LISTENER_ARN" \
--default-actions "[{
\"Type\": \"forward\",
\"ForwardConfig\": {
\"TargetGroups\": [
{\"TargetGroupArn\": \"${BLUE_TG_ARN}\", \"Weight\": 100},
{\"TargetGroupArn\": \"${GREEN_TG_ARN}\", \"Weight\": 1}
]
}
}]"
echo "Green TG를 ALB에 임시 연결 완료 (Blue:100, Green:1)"
# ========================================
# 3단계: Green 헬스체크 대기 (최대 20분)
# ========================================
- name: Wait for Green instances to be healthy
run: |
GREEN_TG_ARN="${{ steps.green-tg.outputs.green_tg_arn }}"
echo "Green TG 헬스체크 대기 중... (20초 간격, 최대 60회 = 20분)"
for i in $(seq 1 60); do
HEALTHY=$(aws elbv2 describe-target-health \
--target-group-arn "$GREEN_TG_ARN" \
--query "TargetHealthDescriptions[?TargetHealth.State=='healthy'] | length(@)" \
--output text 2>/dev/null || echo "0")
TOTAL=$(aws elbv2 describe-target-health \
--target-group-arn "$GREEN_TG_ARN" \
--query "TargetHealthDescriptions | length(@)" \
--output text 2>/dev/null || echo "0")
echo "[$i/60] Healthy: $HEALTHY / Total: $TOTAL"
if [ "$HEALTHY" -ge 2 ] 2>/dev/null; then
echo "Green 환경 정상! (${HEALTHY}개 인스턴스 healthy)"
exit 0
fi
sleep 20
done
echo "ERROR: Green 헬스체크 타임아웃 (20분 초과)"
exit 1
# ========================================
# 4단계: ALB 트래픽을 Green으로 전환
# ========================================
- name: Switch ALB traffic to Green
run: |
LISTENER_ARN="${{ steps.blue.outputs.listener_arn }}"
GREEN_TG_ARN="${{ steps.green-tg.outputs.green_tg_arn }}"
aws elbv2 modify-listener \
--listener-arn "$LISTENER_ARN" \
--default-actions "Type=forward,TargetGroupArn=${GREEN_TG_ARN}"
echo "=== ALB 트래픽 Green으로 전환 완료! ==="
# ========================================
# 5단계: 배포 후 헬스체크 검증
# ========================================
- name: Verify deployment health
run: |
echo "DNS 전파 대기 (30초)..."
sleep 30
for i in $(seq 1 5); do
HEALTH=$(curl -s -o /dev/null -w "%{http_code}" https://loopang.site/actuator/health || true)
if [ "$HEALTH" == "200" ]; then
echo "배포 검증 성공! Health check passed (HTTP 200)"
exit 0
fi
echo "시도 $i/5: HTTP $HEALTH, 재시도 중..."
sleep 10
done
echo "ERROR: Health check 응답 비정상 ($HEALTH)"
exit 1
# ========================================
# 6단계: Blue 환경 정리
# ========================================
- name: Cleanup Blue environment
if: success()
run: |
BLUE_ASG="${{ steps.blue.outputs.blue_asg }}"
BLUE_TG_ARN="${{ steps.blue.outputs.blue_tg_arn }}"
# Blue ASG 축소 → 삭제
if [ -n "$BLUE_ASG" ] && [ "$BLUE_ASG" != "None" ]; then
echo "Blue ASG 축소 중: $BLUE_ASG"
aws autoscaling update-auto-scaling-group \
--auto-scaling-group-name "$BLUE_ASG" \
--min-size 0 --max-size 0 --desired-capacity 0
echo "인스턴스 종료 대기 (60초)..."
sleep 60
echo "Blue ASG 삭제 중: $BLUE_ASG"
aws autoscaling delete-auto-scaling-group \
--auto-scaling-group-name "$BLUE_ASG" \
--force-delete || echo "WARNING: Blue ASG 삭제 실패 — 수동 삭제 필요"
fi
# Blue TG 삭제
if [ -n "$BLUE_TG_ARN" ] && [ "$BLUE_TG_ARN" != "None" ]; then
echo "Blue TG 등록 해제 대기 (30초)..."
sleep 30
aws elbv2 delete-target-group \
--target-group-arn "$BLUE_TG_ARN" \
|| echo "WARNING: Blue TG 삭제 실패 — 수동 삭제 필요"
fi
echo "=== Blue 환경 정리 완료! ==="
# ========================================
# 롤백: 배포 실패 시 자동 복원
# ========================================
- name: Rollback on failure
if: failure()
run: |
echo "=== 배포 실패! 자동 롤백 시작 ==="
# ALB를 Blue TG로 복원
LISTENER_ARN="${{ steps.blue.outputs.listener_arn }}"
BLUE_TG_ARN="${{ steps.blue.outputs.blue_tg_arn }}"
if [ -n "$LISTENER_ARN" ] && [ -n "$BLUE_TG_ARN" ] && [ "$BLUE_TG_ARN" != "None" ]; then
aws elbv2 modify-listener \
--listener-arn "$LISTENER_ARN" \
--default-actions "Type=forward,TargetGroupArn=${BLUE_TG_ARN}" 2>/dev/null || true
echo "ALB → Blue TG 복원 완료"
fi
# Green ASG 삭제
GREEN_ASG="${{ steps.green-asg.outputs.green_asg }}"
if [ -n "$GREEN_ASG" ]; then
aws autoscaling delete-auto-scaling-group \
--auto-scaling-group-name "$GREEN_ASG" \
--force-delete 2>/dev/null || true
echo "Green ASG 삭제 완료: $GREEN_ASG"
fi
# Green TG 삭제
GREEN_TG_ARN="${{ steps.green-tg.outputs.green_tg_arn }}"
if [ -n "$GREEN_TG_ARN" ]; then
echo "Green TG 등록 해제 대기 (30초)..."
sleep 30
aws elbv2 delete-target-group \
--target-group-arn "$GREEN_TG_ARN" 2>/dev/null || true
echo "Green TG 삭제 완료"
fi
echo "=== 롤백 완료 — 서비스는 Blue 환경에서 정상 운영 중 ==="