Merge pull request #157 from TimeBank-Sparta/20152842-patch-60 #110
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Deploy to EC2 (Blue-Green) | |
| on: | |
| push: | |
| branches: | |
| - develop | |
| - main | |
| workflow_dispatch: | |
| jobs: | |
| deploy: | |
| runs-on: ubuntu-latest | |
| env: | |
| SLACK_DEV_WEBHOOK_URL: ${{ secrets.SLACK_DEV_WEBHOOK_URL }} | |
| SLACK_PROD_WEBHOOK_URL: ${{ secrets.SLACK_PROD_WEBHOOK_URL }} | |
| SERVICES: "gateway-service help-service notification-service review-service point-service user-service config-server eureka-server" | |
| EC2_HOST: ${{ secrets.EC2_HOST }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to DockerHub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKER_USERNAME }} | |
| password: ${{ secrets.DOCKER_PASSWORD }} | |
| - name: Define TAG | |
| id: define_tag | |
| run: echo "TAG=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV | |
| - name: Detect Changed Services | |
| id: detect_services | |
| run: | | |
| CHANGED_FILES=$(git diff --name-only ${{ github.event.before }} ${{ github.sha }}) | |
| if echo "$CHANGED_FILES" | grep -Eq '(^build.gradle|^settings.gradle|^gradlew|^gradlew\.bat|^build\.gradle\.kts|^common/)'; then | |
| echo "CHANGED=ALL" >> $GITHUB_ENV | |
| else | |
| CHANGED=$(echo "$CHANGED_FILES" \ | |
| | grep -E '^(gateway-service|help-service|notification-service|review-service|point-service|user-service|config-server|eureka-server)/' \ | |
| | awk -F/ '{print $1}' | sort -u | xargs) | |
| echo "CHANGED=$CHANGED" >> $GITHUB_ENV | |
| fi | |
| - name: Count existing services on EC2 | |
| id: remote_count | |
| uses: appleboy/ssh-action@v0.1.8 | |
| with: | |
| host: ${{ secrets.EC2_HOST }} | |
| username: ${{ secrets.EC2_USER }} | |
| key: ${{ secrets.EC2_SSH_KEY }} | |
| port: 22 | |
| sync: true | |
| script: | | |
| docker ps \ | |
| --filter name=gateway-blue \ | |
| --filter name=gateway-green \ | |
| --filter name=help-service \ | |
| --filter name=notification-service \ | |
| --filter name=review-service \ | |
| --filter name=point-service \ | |
| --filter name=user-service \ | |
| --filter name=config-server \ | |
| --filter name=eureka-server \ | |
| --format '{{.Names}}' | wc -l | |
| - name: Export EXISTING_COUNT | |
| run: | | |
| RAW="${{ steps.remote_count.outputs.stdout || '0' }}" | |
| NUM=$(echo "$RAW" | tr -cd '0-9') | |
| echo "EXISTING_COUNT=$NUM" >> $GITHUB_ENV | |
| - name: Decide whether to skip deploy | |
| run: | | |
| echo "Existing containers: $EXISTING_COUNT" | |
| if [ -z "$CHANGED" ] && [ "$EXISTING_COUNT" -gt 13 ]; then | |
| echo "No changes & already deployed → skipping" | |
| exit 0 | |
| fi | |
| - name: Build & Push images | |
| id: build_push | |
| run: | | |
| if [ "$CHANGED" = "ALL" ] || { [ -z "$CHANGED" ] && [ "$EXISTING_COUNT" -le 13 ]; }; then | |
| TARGETS=($SERVICES) | |
| else | |
| TARGETS=($CHANGED) | |
| fi | |
| echo "Deploy targets: ${TARGETS[*]}" | |
| # for svc in "${TARGETS[@]}"; do | |
| # docker build -t namgyu967/$svc:${TAG} -f $svc/Dockerfile . | |
| # docker push namgyu967/$svc:${TAG} | |
| # docker tag namgyu967/$svc:${TAG} namgyu967/$svc:latest | |
| # docker push namgyu967/$svc:latest | |
| # done | |
| echo "TARGETS=${TARGETS[*]}" >> $GITHUB_ENV | |
| - name: EC2에 Blue-Green 및 서비스 배포 | |
| uses: appleboy/ssh-action@master | |
| with: | |
| host: ${{ secrets.EC2_HOST }} | |
| username: ${{ secrets.EC2_USER }} | |
| key: ${{ secrets.EC2_SSH_KEY }} | |
| port: 22 | |
| protocol: tcp | |
| timeout: 30s | |
| command_timeout: 10m | |
| sync: true | |
| capture_stdout: true | |
| curl_insecure: false | |
| envs: | | |
| TAG=${{ env.TAG }} | |
| SERVICES=${{ env.SERVICES }} | |
| CHANGED=${{ env.CHANGED }} | |
| script: | | |
| set -e | |
| cd ~/timebank | |
| source .env-tag | |
| # 0) nginx 보장 | |
| docker-compose up -d nginx | |
| # 1) Blue-Green gateway | |
| if docker-compose ps | grep -q gateway-blue; then | |
| NEW=green; OLD=blue | |
| else | |
| NEW=blue; OLD=green | |
| fi | |
| docker-compose pull gateway-$NEW | |
| docker-compose up -d gateway-$NEW | |
| sed -i "s/server gateway-$OLD:8080;/server gateway-$NEW:8080;/" nginx-conf/gw.conf | |
| docker exec nginx nginx -s reload | |
| # 2) deploy 대상 계산 | |
| if [ "$CHANGED" = "ALL" ] || [ -z "$CHANGED" ]; then | |
| IFS=' ' read -r -a targets <<< "$SERVICES" | |
| else | |
| IFS=' ' read -r -a targets <<< "$CHANGED" | |
| fi | |
| echo "=== DEBUG:Final deploy targets ===" | |
| for svc in "${targets[*]}"; do | |
| echo " - $svc" | |
| done | |
| # 3) 나머지 서비스 순차 배포 | |
| for svc in "${targets[*]}"; do | |
| [ "$svc" = "gateway-service" ] && continue | |
| echo "Deploying $svc..." | |
| docker-compose pull $svc | |
| docker-compose up -d $svc | |
| done | |
| - name: 헬스체크 및 결과 수집 | |
| id: health | |
| shell: bash | |
| run: | | |
| set +e | |
| SUCCESS=""; FAILED="" | |
| BASE="http://${{ env.EC2_HOST }}" | |
| # 헬스체크 대상도 동일하게 계산 | |
| if [ "${CHANGED}" = "ALL" ] || [ -z "${CHANGED}" ]; then | |
| IFS=' ' read -r -a targets <<< "${SERVICES}" | |
| else | |
| IFS=' ' read -r -a targets <<< "${CHANGED}" | |
| fi | |
| for svc in "${targets[@]}"; do | |
| if [ "$svc" = "gateway-service" ]; then | |
| END="$BASE/actuator/health" | |
| else | |
| END="$BASE/$svc/actuator/health" | |
| fi | |
| for i in {1..10}; do | |
| sleep 5 | |
| CODE=$(curl -s -o /dev/null -w "%{http_code}" "$END" || echo "000") | |
| if [ "$CODE" = "200" ]; then | |
| SUCCESS="$SUCCESS $svc" | |
| break | |
| fi | |
| [ "$i" -eq 10 ] && FAILED="$FAILED $svc" | |
| done | |
| done | |
| echo "SUCCESS=${SUCCESS:-none}" >> $GITHUB_ENV | |
| echo "FAILED=${FAILED:-none}" >> $GITHUB_ENV | |
| - name: Slack Notification (Success) | |
| if: success() | |
| uses: slackapi/slack-github-action@v1.24.0 | |
| with: | |
| payload: | | |
| { | |
| "text": "${{ github.ref == 'refs/heads/main' && ':white_check_mark: *[PROD]* 배포 성공!*' || ':white_check_mark: *[DEV]* 배포 성공!*' }}\n> 대상: ${{ env.TARGETS || 'all' }}\n> 정상: ${{ env.SUCCESS }}\n> 실패: ${{ env.FAILED }}\n> 태그: ${{ env.TAG }}\n> Eureka Server: http://${{ env.EC2_HOST }}:8761" | |
| } | |
| env: | |
| SLACK_WEBHOOK_URL: ${{ github.ref == 'refs/heads/main' && secrets.SLACK_PROD_WEBHOOK_URL || secrets.SLACK_DEV_WEBHOOK_URL }} | |
| - name: Slack Notification (Failure) | |
| if: failure() | |
| uses: slackapi/slack-github-action@v1.24.0 | |
| with: | |
| payload: | | |
| { | |
| "text": "${{ github.ref == 'refs/heads/main' && ':x: *[PROD]* 배포 실패!*' || ':x: *[DEV]* 배포 실패!*' }}\n> 에러 확인해주세요." | |
| } | |
| env: | |
| SLACK_WEBHOOK_URL: ${{ github.ref == 'refs/heads/main' && secrets.SLACK_PROD_WEBHOOK_URL || secrets.SLACK_DEV_WEBHOOK_URL }} |