Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1ae2d1f
[docs] application-dev.yml을 gitignore에 추가
KiSeungMin Dec 23, 2025
5dae573
[feat] 개발 환경 CI/CD workflow 작성
KiSeungMin Dec 23, 2025
6258a0f
[feat] 프로덕션 환경 CI/CD workflow 작성
KiSeungMin Dec 23, 2025
82d3497
[feat] 개발 환경 docker-compose 파일 작성
KiSeungMin Dec 23, 2025
8560594
[feat] 프로덕션 환경 docker-compose 파일 작성
KiSeungMin Dec 23, 2025
7f7f23c
[test] test를 위해 workflow 브랜치 수정
KiSeungMin Dec 23, 2025
9dd2134
[fix] 환경변수명이 잘못 설정되어 있는 문제 해결
KiSeungMin Dec 23, 2025
b227679
[fix] 환경변수 오류 해결
KiSeungMin Dec 23, 2025
e46ab1d
[fix] 환경변수 파일이 로드되지 않던 문제 해결
KiSeungMin Dec 23, 2025
33ca9c2
[fix] 환경변수 파일이 로드되지 않던 문제 해결
KiSeungMin Dec 23, 2025
0a2a823
[fix] application.yml 배포 스크립트 수정
KiSeungMin Dec 23, 2025
5c33b1a
[fix] SecurityConfig의 불필요한 필드 제거
KiSeungMin Dec 23, 2025
a0c6311
[chore] CI/CD 시 Health check 시간을 2분 -> 1분으로 단축
KiSeungMin Dec 23, 2025
b3a6e0f
[test] 프로덕션 테스트를 위한 workflow 수정
KiSeungMin Dec 23, 2025
a0a55c9
[docs] yml 파일 이름 변경
KiSeungMin Dec 23, 2025
db07278
[docs] 불필요한 EOF 문구 삭제
KiSeungMin Dec 23, 2025
c813d24
[test] 개발환경 배포 테스트
KiSeungMin Dec 23, 2025
a52d0c4
[chore] 불필요한 로그 제거
KiSeungMin Dec 23, 2025
36518e1
[chore] 사용하지 않는 workflow 파일 제거
KiSeungMin Dec 23, 2025
e816359
[chore] workflow 테스트 완료. 브랜치명 원상복구
KiSeungMin Dec 23, 2025
9722d45
[Feat] 개발 서버와 프로덕션 서버 분리
KiSeungMin Dec 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions .github/workflows/deploy-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
name: Deploy to Development Server (Mac Mini)

on:
push:
branches: ["develop"] # develop 브랜치에 푸시될 때 개발 배포
workflow_dispatch: # 수동 실행 가능

permissions:
contents: read

jobs:
build-and-deploy-dev:
runs-on: self-hosted # Mac Mini의 Self-hosted Runner 사용

steps:
# 1. 코드 체크아웃
- name: Checkout code
uses: actions/checkout@v4

# 2. JDK 17 설정
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: 'gradle'

# 3. application-dev.yml 생성 (개발 환경 설정)
- name: Create application-dev.yml
run: |
mkdir -p ./src/main/resources
echo "${{ secrets.APPLICATION_DEV }}" > ./src/main/resources/application.yml
shell: bash

# 4. Firebase Admin Key 생성
- name: Create FirebaseAdminKey.json
run: |
rm -rf ./FirebaseAdminKey.json
cat > ./FirebaseAdminKey.json << 'EOF'
${{ secrets.FIREBASE_ADMIN_KEY }}
EOF
shell: bash

# 5. Gradle 빌드 권한 부여
- name: Grant execute permission for gradlew
run: chmod +x gradlew
shell: bash

# 6. Gradle 빌드 (테스트 제외)
- name: Build with Gradle
run: ./gradlew clean build -x test
shell: bash

# 7. Docker 이미지 빌드 (DEVELOPMENT 태그)
- name: Build Docker image
run: |
docker build -t ${{ secrets.DOCKER_USERNAME }}/ono:dev-latest .
docker tag ${{ secrets.DOCKER_USERNAME }}/ono:dev-latest ${{ secrets.DOCKER_USERNAME }}/ono:dev-${{ github.sha }}
shell: bash

# 8. Docker Hub에 푸시
- name: Push to Docker Hub
run: |
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
docker push ${{ secrets.DOCKER_USERNAME }}/ono:dev-latest
docker push ${{ secrets.DOCKER_USERNAME }}/ono:dev-${{ github.sha }}
shell: bash

# 9. .env 파일 생성 (DEVELOPMENT)
- name: Create .env file
run: |
cat > .env.dev << EOF
DOCKER_USERNAME=${{ secrets.DOCKER_USERNAME }}
MYSQL_ROOT_PASSWORD=${{ secrets.MYSQL_ROOT_PASSWORD }}
SPRING_CRYPTO_SECRET_KEY=${{ secrets.SPRING_CRYPTO_SECRET_KEY }}
JWT_ACCESS_TOKEN_SECRET=${{ secrets.JWT_ACCESS_TOKEN_SECRET }}
JWT_ACCESS_TOKEN_EXPIRATION=1800000
JWT_REFRESH_TOKEN_SECRET=${{ secrets.JWT_REFRESH_TOKEN_SECRET }}
JWT_REFRESH_TOKEN_EXPIRATION=604800000
CLOUD_AWS_S3_BUCKET=${{ secrets.CLOUD_AWS_S3_BUCKET }}
CLOUD_AWS_CREDENTIALS_ACCESS_KEY=${{ secrets.CLOUD_AWS_CREDENTIALS_ACCESS_KEY }}
CLOUD_AWS_CREDENTIALS_SECRET_KEY=${{ secrets.CLOUD_AWS_CREDENTIALS_SECRET_KEY }}
DISCORD_WEBHOOK_URL_DEV=${{ secrets.DISCORD_WEBHOOK_URL }}
OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}
SENTRY_DSN_DEV=${{ secrets.SENTRY_DSN }}
ADMIN_IDENTIFIER=${{ secrets.ADMIN_IDENTIFIER }}
ADMIN_PASSWORD=${{ secrets.ADMIN_PASSWORD }}
EOF
shell: bash

# 10. Docker Compose로 배포 (DEVELOPMENT)
- name: Deploy with Docker Compose
run: |
# MySQL과 Redis 시작 (개발 환경 전용)
docker-compose -f docker-compose.dev.yml --env-file .env.dev up -d mysql-dev redis-dev

# Spring Boot 개발 앱만 재시작
docker-compose -f docker-compose.dev.yml --env-file .env.dev stop app-dev || true
docker-compose -f docker-compose.dev.yml --env-file .env.dev rm -f app-dev || true
docker-compose -f docker-compose.dev.yml --env-file .env.dev up -d app-dev

# 헬스체크 대기 (최대 2분)
echo "Waiting for development application to be healthy..."
timeout=60
elapsed=0
while [ $elapsed -lt $timeout ]; do
if docker-compose -f docker-compose.dev.yml --env-file .env.dev ps app-dev | grep -q "healthy"; then
echo "Development application is healthy!"
break
fi
echo "Waiting... ($elapsed seconds)"
sleep 5
elapsed=$((elapsed + 5))
done

# 컨테이너 상태 확인
docker-compose -f docker-compose.dev.yml --env-file .env.dev ps

# 로그 출력 (마지막 50줄)
echo "=== Development Application Logs ==="
docker-compose -f docker-compose.dev.yml --env-file .env.dev logs --tail=50 app-dev
shell: bash

# 12. 오래된 Docker 이미지 정리
- name: Clean up old Docker images
run: |
docker image prune -f --filter "until=24h"
shell: bash
continue-on-error: true

# 13. Discord 배포 알림
- name: Send deployment notification
if: always()
run: |
STATUS="${{ job.status }}"
COLOR="3066993" # 파란색 (성공)
if [ "$STATUS" != "success" ]; then
COLOR="15158332" # 빨간색 (실패)
fi

curl -X POST "${{ secrets.DISCORD_WEBHOOK_URL }}" \
-H "Content-Type: application/json" \
-d "{
\"embeds\": [{
\"title\": \"🔧 Development Deployment $STATUS\",
\"description\": \"Environment: **DEVELOPMENT**\nURL: https://ono-dev.seungminki.shop\nBranch: \`${{ github.ref_name }}\`\nCommit: \`${{ github.sha }}\`\nAuthor: ${{ github.actor }}\",
\"color\": $COLOR,
\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\"
}]
}"
shell: bash
continue-on-error: true
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
name: Deploy to Home Server (Mac Mini)
name: Deploy to Production Server (Mac Mini)

on:
push:
branches: ["main"] # main 브랜치에 푸시될 때 워크플로우 실행
branches: ["main"] # main 브랜치에 푸시될 때 운영 배포
workflow_dispatch: # 수동 실행 가능

permissions:
contents: read

jobs:
build-and-deploy:
build-and-deploy-prod:
runs-on: self-hosted # Mac Mini의 Self-hosted Runner 사용

steps:
Expand All @@ -23,19 +23,19 @@ jobs:
with:
java-version: '17'
distribution: 'temurin'
cache: 'gradle' # Gradle 캐시 활성화
cache: 'gradle'

# 3. application-prod.yml 생성 (GitHub Secrets에서 주입)
# 3. application-prod.yml 생성
- name: Create application-prod.yml
run: |
mkdir -p ./src/main/resources
echo "${{ secrets.APPLICATION_PROD }}" > ./src/main/resources/application-prod.yml
echo "${{ secrets.APPLICATION_PROD }}" > ./src/main/resources/application.yml
shell: bash

# 4. Firebase Admin Key 생성 (GitHub Secrets에서 주입)
# 4. Firebase Admin Key 생성
- name: Create FirebaseAdminKey.json
run: |
rm -rf ./FirebaseAdminKey.json # 기존 파일/디렉토리 삭제
rm -rf ./FirebaseAdminKey.json
cat > ./FirebaseAdminKey.json << 'EOF'
${{ secrets.FIREBASE_ADMIN_KEY }}
EOF
Expand All @@ -51,25 +51,25 @@ jobs:
run: ./gradlew clean build -x test
shell: bash

# 7. Docker 이미지 빌드
# 7. Docker 이미지 빌드 (PRODUCTION 태그)
- name: Build Docker image
run: |
docker build -t ${{ secrets.DOCKER_USERNAME }}/ono:latest .
docker tag ${{ secrets.DOCKER_USERNAME }}/ono:latest ${{ secrets.DOCKER_USERNAME }}/ono:${{ github.sha }}
docker build -t ${{ secrets.DOCKER_USERNAME }}/ono:prod-latest .
docker tag ${{ secrets.DOCKER_USERNAME }}/ono:prod-latest ${{ secrets.DOCKER_USERNAME }}/ono:prod-${{ github.sha }}
shell: bash

# 8. Docker Hub에 로그인 및 푸시 (선택사항: 다른 서버에도 배포하려면)
# 8. Docker Hub에 푸시
- name: Push to Docker Hub
run: |
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
docker push ${{ secrets.DOCKER_USERNAME }}/ono:latest
docker push ${{ secrets.DOCKER_USERNAME }}/ono:${{ github.sha }}
docker push ${{ secrets.DOCKER_USERNAME }}/ono:prod-latest
docker push ${{ secrets.DOCKER_USERNAME }}/ono:prod-${{ github.sha }}
shell: bash

# 9. .env 파일 생성
# 9. .env 파일 생성 (PRODUCTION)
- name: Create .env file
run: |
cat > .env << EOF
cat > .env.prod << EOF
DOCKER_USERNAME=${{ secrets.DOCKER_USERNAME }}
MYSQL_ROOT_PASSWORD=${{ secrets.MYSQL_ROOT_PASSWORD }}
SPRING_CRYPTO_SECRET_KEY=${{ secrets.SPRING_CRYPTO_SECRET_KEY }}
Expand All @@ -80,7 +80,6 @@ jobs:
CLOUD_AWS_S3_BUCKET=${{ secrets.CLOUD_AWS_S3_BUCKET }}
CLOUD_AWS_CREDENTIALS_ACCESS_KEY=${{ secrets.CLOUD_AWS_CREDENTIALS_ACCESS_KEY }}
CLOUD_AWS_CREDENTIALS_SECRET_KEY=${{ secrets.CLOUD_AWS_CREDENTIALS_SECRET_KEY }}
EXTERNAL_API_FAST_API_URL=${{ secrets.EXTERNAL_API_FAST_API_URL }}
DISCORD_WEBHOOK_URL=${{ secrets.DISCORD_WEBHOOK_URL }}
OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}
SENTRY_DSN=${{ secrets.SENTRY_DSN }}
Expand All @@ -89,24 +88,24 @@ jobs:
EOF
shell: bash

# 10. Docker Compose로 배포
# 10. Docker Compose로 배포 (PRODUCTION)
- name: Deploy with Docker Compose
run: |
# MySQL과 Redis가 없으면 시작 (최초 배포 시)
docker-compose up -d mysql redis
# MySQL과 Redis 시작 (이미 실행 중이면 스킵)
docker-compose -f docker-compose.prod.yml --env-file .env.prod up -d mysql-prod redis-prod

# Spring Boot 앱만 재시작 (기존 컨테이너 중지 후 새로 시작)
docker-compose stop app || true
docker-compose rm -f app || true
docker-compose up -d app
# Spring Boot 운영 앱만 재시작
docker-compose -f docker-compose.prod.yml --env-file .env.prod stop app-prod || true
docker-compose -f docker-compose.prod.yml --env-file .env.prod rm -f app-prod || true
docker-compose -f docker-compose.prod.yml --env-file .env.prod up -d app-prod

# 헬스체크 대기 (최대 2분)
echo "Waiting for application to be healthy..."
timeout=120
# 헬스체크 대기 (최대 1분)
echo "Waiting for production application to be healthy..."
timeout=60
elapsed=0
while [ $elapsed -lt $timeout ]; do
if docker-compose ps app | grep -q "healthy"; then
echo "Application is healthy!"
if docker-compose -f docker-compose.prod.yml ps app-prod | grep -q "healthy"; then
echo "Production application is healthy!"
break
fi
echo "Waiting... ($elapsed seconds)"
Expand All @@ -115,27 +114,23 @@ jobs:
done

# 컨테이너 상태 확인
docker-compose ps
docker-compose -f docker-compose.prod.yml ps

# 로그 출력 (마지막 50줄)
echo "=== Application Logs ==="
docker-compose logs --tail=50 app
echo "=== Production Application Logs ==="
docker-compose -f docker-compose.prod.yml logs --tail=50 app-prod
shell: bash

# 11. 오래된 Docker 이미지 정리
# 12. 오래된 Docker 이미지 정리
- name: Clean up old Docker images
run: |
# 24시간 이상 된 dangling 이미지 삭제
docker image prune -f --filter "until=24h"

# 사용하지 않는 볼륨 정리 (선택사항)
# docker volume prune -f
shell: bash
continue-on-error: true # 정리 실패해도 워크플로우는 성공으로 처리
continue-on-error: true

# 12. Slack/Discord 알림 (선택사항)
# 13. Discord 배포 알림
- name: Send deployment notification
if: always() # 성공/실패 관계없이 항상 실행
if: always()
run: |
STATUS="${{ job.status }}"
COLOR="3066993" # 파란색 (성공)
Expand All @@ -147,8 +142,8 @@ jobs:
-H "Content-Type: application/json" \
-d "{
\"embeds\": [{
\"title\": \"🚀 Deployment $STATUS\",
\"description\": \"Branch: \`${{ github.ref_name }}\`\nCommit: \`${{ github.sha }}\`\nAuthor: ${{ github.actor }}\",
\"title\": \"🚀 Production Deployment $STATUS\",
\"description\": \"Environment: **PRODUCTION**\nURL: https://ono-prod.seungminki.shop\nBranch: \`${{ github.ref_name }}\`\nCommit: \`${{ github.sha }}\`\nAuthor: ${{ github.actor }}\",
\"color\": $COLOR,
\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\"
}]
Expand Down
76 changes: 0 additions & 76 deletions .github/workflows/prod_docker_gradle.yml

This file was deleted.

Loading