Skip to content

feat: WebConfig 프론트 배포 url 추가 #100

feat: WebConfig 프론트 배포 url 추가

feat: WebConfig 프론트 배포 url 추가 #100

Workflow file for this run

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}}"