feat: WebConfig 프론트 배포 url 추가 #100
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: Backend Deploy | |
| on: | |
| push: | |
| branches: [develop] | |
| paths: | |
| - '**' | |
| workflow_dispatch: | |
| env: | |
| IMAGE_NAME: ${{secrets.DOCKERHUB_USERNAME}}/jakku-backend | |
| jobs: | |
| deploy: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Set DATE_TAG (KST) | |
| run: echo "DATE_TAG=$(TZ=Asia/Seoul date +%Y%m%d-%H%M)" >> $GITHUB_ENV | |
| # 코드 가져오기 | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| # Java 17 설치 | |
| - name: Set up JDK 17 | |
| uses: actions/setup-java@v4 | |
| with: | |
| java-version: '17' | |
| distribution: 'temurin' | |
| cache: 'gradle' | |
| # 빌드 권한 설정 | |
| - name: Grant execute permission for gradlew | |
| run: chmod +x gradlew | |
| # 애플리케이션 빌드 | |
| - name: Build application | |
| run: ./gradlew clean bootJar -x test | |
| # plain JAR 제거 (COPY *.jar 매칭 1개 보장) | |
| - name: Remove plain jars | |
| run: rm -f build/libs/*plain*.jar || true | |
| # === (Docker) Docker Hub 로그인 & 메타 === | |
| - name: Login to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{secrets.DOCKERHUB_USERNAME}} | |
| password: ${{secrets.DOCKERHUB_TOKEN}} | |
| - name: Docker meta(tags & labels) | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{env.IMAGE_NAME}} | |
| tags: | | |
| type=raw,value=${{env.DATE_TAG}} | |
| - name: Set up Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Build & Push Docker image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| # --- EC2에 배포: Blue/Green + Healthcheck + 안전 스왑 --- | |
| - name: Deploy on EC2 via Docker (blue / green swap) | |
| uses: appleboy/ssh-action@v1.0.3 | |
| timeout-minutes: 5 | |
| with: | |
| host: ${{secrets.AWS_HOST}} | |
| username: ${{secrets.AWS_USERNAME}} | |
| key: ${{secrets.AWS_SSH_KEY}} | |
| script: | | |
| set -euo pipefail | |
| IMAGE="${{env.IMAGE_NAME}}:${{env.DATE_TAG}}" | |
| APP_DIR="$HOME/jakku" | |
| MAIN_NAME="jakku" | |
| CANARY_NAME="jakku_canary" | |
| MAIN_PORT=8080 | |
| CANARY_PORT=18080 | |
| mkdir -p "$APP_DIR" | |
| cat > "$APP_DIR/jakku.env" << 'EOF' | |
| SPRING_PROFILES_ACTIVE=prod | |
| SPRING_DATASOURCE_URL=${{secrets.SPRING_DATASOURCE_URL}} | |
| SPRING_DATASOURCE_USERNAME=${{secrets.SPRING_DATASOURCE_USERNAME}} | |
| SPRING_DATASOURCE_PASSWORD=${{secrets.SPRING_DATASOURCE_PASSWORD}} | |
| S3_ACCESSKEY=${{secrets.S3_ACCESSKEY}} | |
| S3_SECRETKEY=${{secrets.S3_SECRETKEY}} | |
| S3_BUCKET=${{secrets.S3_BUCKET}} | |
| S3_REGION=${{secrets.S3_REGION}} | |
| KAKAOPAY_CID=${{secrets.KAKAOPAY_CID}} | |
| KAKAOPAY_SECRET_KEY=${{secrets.KAKAOPAY_SECRET_KEY}} | |
| CODEF_CLIENTID=${{secrets.CODEF_CLIENTID}} | |
| CODEF_CLIENTSECRET=${{secrets.CODEF_CLIENTSECRET}} | |
| CODEF_PUBLICKEY=${{secrets.CODEF_PUBLICKEY}} | |
| APP_PUBLICBASEURL=${{secrets.APP_PUBLICBASEURL}} | |
| WEBSOCKET_ALLOWED_ORIGINS=${{secrets.WEBSOCKET_ALLOWED_ORIGINS}} | |
| OPENAI_API_KEY=${{secrets.OPENAI_API_KEY}} | |
| TTS_API=${{secrets.TTS_API}} | |
| EOF | |
| chmod 600 "$APP_DIR/jakku.env" | |
| echo "${{secrets.DOCKERHUB_TOKEN}}" | docker login -u "${{secrets.DOCKERHUB_USERNAME}}" --password-stdin | |
| echo "[1/5] Pull image: $IMAGE" | |
| docker pull "$IMAGE" | |
| echo "[2/5] Start canary on :${CANARY_PORT}" | |
| docker rm -f "$CANARY_NAME" > /dev/null 2>&1 || true | |
| docker run -d --name "$CANARY_NAME" --restart "no" \ | |
| -p ${CANARY_PORT}:${MAIN_PORT} \ | |
| --env-file "$APP_DIR/jakku.env" \ | |
| "$IMAGE" | |
| echo "[3/5] Health check canary" | |
| ok=0 | |
| for i in $(seq 1 30); do | |
| if curl -fsS "http://localhost:${CANARY_PORT}/actuator/health" >/dev/null 2>&1; then | |
| ok=1; break; | |
| fi | |
| sleep 1 | |
| done | |
| if [ "$ok" -ne 1 ]; then | |
| echo "X Canary failed health check. Keeping existing main container." | |
| docker logs "$CANARY_NAME" || true | |
| docker rm -f "$CANARY_NAME" || true | |
| exit 1 | |
| fi | |
| echo "[4/5] Swap to main (:${MAIN_PORT})" | |
| docker rm -f "$MAIN_NAME" >/dev/null 2>&1 || true | |
| if ss -ltn | awk '{print $4}' | grep -q ":${MAIN_PORT}$"; then | |
| echo "X Port ${MAIN_PORT} is occupied by a non-docker process. Aborting swap." | |
| docker rm -f "$CANARY_NAME" || true | |
| exit 1 | |
| fi | |
| docker run -d --name "$MAIN_NAME" --restart unless-stopped \ | |
| -p ${MAIN_PORT}:${MAIN_PORT} \ | |
| --env-file "$APP_DIR/jakku.env" \ | |
| "$IMAGE" | |
| echo "[5/5] Cleanup canary" | |
| docker rm -f "$CANARY_NAME" >/dev/null 2>&1 || true | |
| echo "O Deployed. Current containers:" | |
| docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}" |