fix: SSE emitter 관리 로직 개선 및 구독 안정성 향상 (#275) #146
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: SpringBoot Docker CI/CD - Unified | |
| on: | |
| push: | |
| branches: | |
| # - main | |
| - develop | |
| - release/** | |
| jobs: | |
| build-and-deploy: | |
| runs-on: ubuntu-latest | |
| environment: ${{ github.ref_name == 'main' && 'prod' || 'dev' }} | |
| steps: | |
| - name: 🐵 Checkout Repository | |
| uses: actions/checkout@v3 | |
| with: | |
| persist-credentials: false | |
| - name: 🐵 Set up JDK 21 | |
| uses: actions/setup-java@v3 | |
| with: | |
| distribution: 'temurin' | |
| java-version: '21' | |
| - name: 🐵 Grant execute permission to Gradle | |
| run: chmod +x gradlew | |
| - name: 🐵 Extract version from build.gradle | |
| id: version | |
| if: github.ref_name == 'main' | |
| run: | | |
| VERSION=$(grep '^version' build.gradle | awk -F"'" '{print $2}') | |
| echo "version=$VERSION" | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| - name: 🐵 Create Git Tag (if not exists) | |
| if: github.ref_name == 'main' | |
| env: | |
| GH_PAT: ${{ secrets.GH_PAT }} | |
| run: | | |
| echo "현재 토큰: ${GH_PAT:0:4}****" | |
| git remote set-url origin https://x-access-token:${GH_PAT}@github.com/${{ github.repository }} | |
| git config --get remote.origin.url | |
| git fetch --tags | |
| TAG=${{ steps.version.outputs.version }} | |
| if git rev-parse "refs/tags/$TAG" >/dev/null 2>&1; then | |
| echo "✅ Tag $TAG already exists" | |
| else | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git tag $TAG | |
| git remote set-url origin https://x-access-token:${GH_PAT}@github.com/${GITHUB_REPOSITORY} | |
| git push origin $TAG | |
| echo "✅ Created Git tag: $TAG" | |
| fi | |
| - name: 🐵 Build JAR (no tests) | |
| run: | | |
| echo "🐵 Building JAR without tests..." | |
| ./gradlew clean build -x test || { echo '🚨 Build failed!'; exit 1; } | |
| echo "✅ Build completed successfully!" | |
| - name: 🐵 Docker Login to DockerHub | |
| run: echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin | |
| - name: 🐵 Set Docker Tag | |
| id: tagger | |
| run: | | |
| REF_NAME=${{ github.ref_name }} | |
| COMMIT_SHA=${{ github.sha }} | |
| # 기본값: dev 이미지 | |
| IMAGE_NAME=4moa/moa-be-dev | |
| TAG=${COMMIT_SHA::7} | |
| # main 레포인 경우 | |
| if [[ "$REF_NAME" == "main" ]] ; then | |
| IMAGE_NAME=4moa/moa-be | |
| TAG=${{ steps.version.outputs.version }} | |
| fi | |
| echo "Image Name: $IMAGE_NAME" | |
| echo "Tag: $TAG" | |
| echo "image_name=$IMAGE_NAME" >> $GITHUB_OUTPUT | |
| echo "tag=$TAG" >> $GITHUB_OUTPUT | |
| - name: 🐵 Build & Push Docker Image | |
| run: | | |
| REF_NAME=${{ github.ref_name }} | |
| DOCKERFILE_PATH=Dockerfile | |
| if [[ "$REF_NAME" != "main" ]]; then | |
| DOCKERFILE_PATH=Dockerfile.dev | |
| fi | |
| echo "🐵 Using Dockerfile: $DOCKERFILE_PATH" | |
| docker buildx build \ | |
| --platform linux/amd64 \ | |
| -f $DOCKERFILE_PATH \ | |
| -t ${{ steps.tagger.outputs.image_name }}:${{ steps.tagger.outputs.tag }} \ | |
| --push . | |
| echo "✅ Docker image pushed: ${{ steps.tagger.outputs.image_name }}:${{ steps.tagger.outputs.tag }}" | |
| - name: 🐵 Save SSH Key | |
| run: | | |
| echo "${{ secrets.GCP_CICD_SSH_KEY }}" > key.pem | |
| chmod 600 key.pem | |
| - name: 🐵 Deploy to Remote Server | |
| env: | |
| IMAGE_NAME: ${{ steps.tagger.outputs.image_name }} | |
| IMAGE_TAG: ${{ steps.tagger.outputs.tag }} | |
| REF_NAME: ${{ github.ref_name }} | |
| run: | | |
| ssh -i key.pem -o StrictHostKeyChecking=no cicd@${{ secrets.GCP_BE_HOST }} <<EOF | |
| set -e | |
| IMAGE_NAME=${IMAGE_NAME} | |
| IMAGE_TAG=${IMAGE_TAG} | |
| echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login -u "${{ secrets.DOCKERHUB_USERNAME }}" --password-stdin | |
| echo "🐵 Pulling image \$IMAGE_NAME:\$IMAGE_TAG" | |
| docker pull \$IMAGE_NAME:\$IMAGE_TAG | |
| echo "🐵 Stopping existing container..." | |
| docker stop moa-backend || true | |
| docker rm moa-backend || true | |
| echo "🐵 Writing .env on remote server..." | |
| if [[ "$REF_NAME" == "main" ]]; then | |
| cat <<EENV | sudo tee /home/cicd/moa-backend.env > /dev/null | |
| SPRING_PROFILES_ACTIVE=${{ secrets.SPRING_PROFILE }} | |
| SPRING_DATASOURCE_URL=${{ secrets.DB_URL }} | |
| SPRING_DATASOURCE_USERNAME=${{ secrets.DB_USERNAME }} | |
| SPRING_DATASOURCE_PASSWORD=${{ secrets.DB_PASSWORD }} | |
| JWT_SECRET=${{ secrets.JWT_SECRET }} | |
| FRONTEND_URL=${{ secrets.FRONTEND_URL }} | |
| AI_SERVER_URL=${{ secrets.AI_SERVER_URL }} | |
| KAKAO_CLIENT_ID=${{ secrets.KAKAO_CLIENT_ID }} | |
| KAKAO_ADMIN_KEY=${{ secrets.KAKAO_ADMIN_KEY }} | |
| REDIS_URL=${{ secrets.REDIS_URL }} | |
| REDIS_PORT=${{ secrets.REDIS_PORT }} | |
| REDIS_PASSWORD=${{ secrets.REDIS_PASSWORD }} | |
| AWS_ACCESS_KEY=${{ secrets.AWS_ACCESS_KEY }} | |
| AWS_REGION=${{ secrets.AWS_REGION }} | |
| AWS_SECRET_KEY=${{ secrets.AWS_SECRET_KEY }} | |
| S3_BUCKET_NAME=${{ secrets.S3_BUCKET_NAME }} | |
| CDN_IMAGE_BASE_URL=${{ secrets.CDN_IMAGE_BASE_URL }} | |
| OTEL_SERVICE_NAME=${{ secrets.OTEL_SERVICE_NAME }} | |
| OTEL_EXPORTER_OTLP_ENDPOINT=${{ secrets.OTEL_EXPORTER_OTLP_ENDPOINT }} | |
| OTEL_EXPORTER_OTLP_HEADERS=${{ secrets.OTEL_EXPORTER_OTLP_HEADERS }} | |
| OTEL_RESOURCE_ATTRIBUTES=${{ secrets.OTEL_RESOURCE_ATTRIBUTES }} | |
| MONGODB_URI=${{ secrets.MONGODB_URI }} | |
| EENV | |
| else | |
| cat <<EENV | sudo tee /home/cicd/moa-backend.env > /dev/null | |
| SPRING_PROFILES_ACTIVE=${{ secrets.SPRING_PROFILE }} | |
| SPRING_DATASOURCE_URL=${{ secrets.DB_URL }} | |
| SPRING_DATASOURCE_USERNAME=${{ secrets.DB_USERNAME }} | |
| SPRING_DATASOURCE_PASSWORD=${{ secrets.DB_PASSWORD }} | |
| JWT_SECRET=${{ secrets.JWT_SECRET }} | |
| FRONTEND_URL=${{ secrets.FRONTEND_URL }} | |
| AI_SERVER_URL=${{ secrets.AI_SERVER_URL }} | |
| KAKAO_CLIENT_ID=${{ secrets.KAKAO_CLIENT_ID }} | |
| KAKAO_ADMIN_KEY=${{ secrets.KAKAO_ADMIN_KEY }} | |
| REDIS_URL=${{ secrets.REDIS_URL }} | |
| REDIS_PORT=${{ secrets.REDIS_PORT }} | |
| REDIS_PASSWORD=${{ secrets.REDIS_PASSWORD }} | |
| AWS_ACCESS_KEY=${{ secrets.AWS_ACCESS_KEY }} | |
| AWS_REGION=${{ secrets.AWS_REGION }} | |
| AWS_SECRET_KEY=${{ secrets.AWS_SECRET_KEY }} | |
| S3_BUCKET_NAME=${{ secrets.S3_BUCKET_NAME }} | |
| CDN_IMAGE_BASE_URL=${{ secrets.CDN_IMAGE_BASE_URL }} | |
| ELASTIC_APM_SERVER_URL=${{ secrets.ELASTIC_APM_SERVER_URL }} | |
| ELASTIC_APM_SERVICE_NAME=${{ secrets.ELASTIC_APM_SERVICE_NAME }} | |
| ELASTIC_APM_ENVIRONMENT=${{ secrets.ELASTIC_APM_ENVIRONMENT }} | |
| MONGODB_URI=${{ secrets.MONGODB_URI }} | |
| EENV | |
| fi | |
| sudo chmod 600 /home/cicd/moa-backend.env | |
| sudo chown cicd:cicd /home/cicd/moa-backend.env | |
| echo "🐵 Running new container..." | |
| docker run -d --name moa-backend \ | |
| --env-file /home/cicd/moa-backend.env \ | |
| -p 8080:8080 \ | |
| --restart unless-stopped \ | |
| \$IMAGE_NAME:\$IMAGE_TAG | |
| echo "🐵 Pruning unused Docker images (safe cleanup)" | |
| docker image prune -af --filter "until=48h" || true | |
| echo "🐵 Listing running containers..." | |
| echo "✅ Container started!" | |
| docker ps | |
| EOF |