diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml new file mode 100644 index 00000000..1e2b1372 --- /dev/null +++ b/.github/workflows/deploy-dev.yml @@ -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 diff --git a/.github/workflows/deploy-home-server.yml b/.github/workflows/deploy-prod.yml similarity index 60% rename from .github/workflows/deploy-home-server.yml rename to .github/workflows/deploy-prod.yml index 8557c325..3b967fd3 100644 --- a/.github/workflows/deploy-home-server.yml +++ b/.github/workflows/deploy-prod.yml @@ -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: @@ -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 @@ -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 }} @@ -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 }} @@ -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)" @@ -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" # 파란색 (성공) @@ -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)\" }] diff --git a/.github/workflows/prod_docker_gradle.yml b/.github/workflows/prod_docker_gradle.yml deleted file mode 100644 index 9887d64b..00000000 --- a/.github/workflows/prod_docker_gradle.yml +++ /dev/null @@ -1,76 +0,0 @@ -#name: DOCKER CI IN PRODUCTION SERVER # 워크플로우의 이름 설정 -# -#on: -# push: -# branches: ["main"] # main 브랜치에 푸시될 때 워크플로우 실행 -# -#permissions: -# contents: read # 워크플로우의 권한 설정 (read-only) -# -#jobs: -# build: -# -# runs-on: ubuntu-latest # 워크플로우가 실행될 환경 설정 (Ubuntu 최신 버전) -# -# steps: -# # 코드 체크아웃: GitHub Actions가 현재 리포지토리의 코드를 가져옵니다. -# - uses: actions/checkout@v3 -# -# # JDK 17 설정: Java 17 버전을 사용하기 위해 JDK를 설치합니다. -# - name: Set up JDK 17 -# uses: actions/setup-java@v3 -# with: -# java-version: '17' # JDK 버전 17 설정 -# distribution: 'temurin' # Temurin JDK 배포판 사용 -# -# # application.properties 파일 생성: dev 환경의 설정 파일을 생성하고 secrets에서 값을 가져와 입력합니다. -# - name: make application.properties -# run: | -# cd ./src/main/resources # resources 디렉토리로 이동 -# touch ./application.yml # application.properties 파일 생성 -# echo "${{ secrets.APPLICATION_PROD }}" > ./application.yml # GitHub Secrets에서 설정값을 가져와 파일에 저장 -# -# # Gradle Wrapper 실행 권한 부여: gradlew에 실행 권한을 부여합니다. -# - name: Grant execute permission for gradlew -# run: chmod +x gradlew -# -# # Gradle 빌드: Gradle을 사용해 프로젝트를 빌드하되, 테스트는 생략합니다. -# - name: Build with Gradle -# run: ./gradlew build -x test # 테스트는 실행하지 않고 빌드만 수행 -# -# # Docker 이미지 빌드 및 푸시 -# - name: Docker build -# run: | -# docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} # Docker Hub에 로그인 -# docker build -t app . # Docker 이미지를 'app'이라는 이름으로 빌드 -# docker tag app ${{ secrets.DOCKER_USERNAME }}/ono:latest # 이미지를 Docker Hub 저장소로 태깅 -# docker push ${{ secrets.DOCKER_USERNAME }}/ono:latest # 이미지를 Docker Hub에 푸시 -# -# # Bastion 서버를 통해 스프링 서버로 배포 -# - name: Deploy via Bastion Server -# uses: appleboy/ssh-action@master -# with: -# host: ${{ secrets.PROD_BASTION_HOST }} # Bastion 서버의 퍼블릭 IP 또는 DNS 주소 -# username: ubuntu # Bastion 서버의 사용자 이름 -# key: ${{ secrets.PROD_BASTION_PRIVATE_KEY }} # Bastion 서버의 SSH 개인 키 (GitHub Secrets에서 가져옴) -# port: 22 # SSH 연결에 사용할 포트 (기본값 22) -# script: | -# sudo usermod -aG docker ubuntu -# sudo chmod 666 /var/run/docker.sock -# # Bastion 서버에서 스프링 서버로 SSH 연결 및 도커 작업 수행 -# ssh -o StrictHostKeyChecking=no -i /home/ubuntu/dev-an2-ono-test-spring-key.pem ubuntu@${{ secrets.PROD_SPRING_SERVER_IP }} << "EOSSH" -# # 스프링 서버에서 Docker Hub에 로그인 -# docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} -# # 최신 Docker 이미지를 Docker Hub에서 Pull -# docker pull ${{ secrets.DOCKER_USERNAME }}/ono:latest -# # 실행 중인 모든 컨테이너를 중지 (실행 중인 컨테이너가 없을 경우 오류 무시) -# docker stop $(docker ps -a -q) || true -# # 중지된 모든 컨테이너를 제거 (없을 경우 오류 무시) -# docker rm $(docker ps -a -q) || true -# # 새로 받은 이미지를 기반으로 컨테이너 실행 (8080 포트 사용) -# docker run -d --restart unless-stopped --log-driver=syslog -p 8080:8080 ${{ secrets.DOCKER_USERNAME }}/ono:latest -# # 24시간 동안 사용되지 않은 모든 Docker 이미지를 삭제하여 공간 확보 -# docker image prune -a -f --filter "until=24h" -# EOSSH -#TODO -#FirebaseAdminKey.json 파일 생성하기 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 64cff529..385f2b4d 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ application.properties application-aws.properties application-dev.properties application-prod.properties +application-dev.yml application-prod.yml FirebaseAdminKey.json diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 00000000..9592915a --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,103 @@ +version: '3.8' + +services: + # MySQL Database - DEVELOPMENT + mysql-dev: + image: mysql:8.0 + container_name: ono-mysql-dev + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: ono_db_dev + TZ: Asia/Seoul + ports: + - "3307:3306" # 외부 포트 3307로 노출 (prod와 충돌 방지) + volumes: + - mysql_dev_data:/var/lib/mysql + command: + - --character-set-server=utf8mb4 + - --collation-server=utf8mb4_unicode_ci + - --default-time-zone=+09:00 + networks: + - ono-network + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"] + interval: 10s + timeout: 5s + retries: 5 + + # Redis Cache - DEVELOPMENT (리소스 제한 적용) + redis-dev: + image: redis:7.2-alpine + container_name: ono-redis-dev + restart: unless-stopped + ports: + - "6380:6379" # 외부 포트 6380으로 노출 (prod와 충돌 방지) + volumes: + - redis_dev_data:/data + networks: + - ono-network + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + # 개발 환경 Redis는 메모리 제한을 더 작게 설정 + command: redis-server --appendonly yes --maxmemory 128mb --maxmemory-policy allkeys-lru + + # Spring Boot Application - DEVELOPMENT + app-dev: + image: ${DOCKER_USERNAME}/ono:dev-latest + container_name: ono-app-dev + restart: unless-stopped + ports: + - "8082:8080" # 개발 서버는 8082 포트로 외부 노출 + environment: + SPRING_PROFILES_ACTIVE: dev + SPRING_DATASOURCE_URL: jdbc:mysql://mysql-dev:3306/ono_db_dev?serverTimezone=Asia/Seoul&characterEncoding=UTF-8 + SPRING_DATASOURCE_USERNAME: root + SPRING_DATASOURCE_PASSWORD: ${MYSQL_ROOT_PASSWORD} + SPRING_DATA_REDIS_HOST: redis-dev + SPRING_DATA_REDIS_PORT: 6379 + # 환경 변수 + SPRING_CRYPTO_SECRET_KEY: ${SPRING_CRYPTO_SECRET_KEY} + JWT_ACCESS_TOKEN_SECRET: ${JWT_ACCESS_TOKEN_SECRET} + JWT_ACCESS_TOKEN_EXPIRATION: ${JWT_ACCESS_TOKEN_EXPIRATION} + JWT_REFRESH_TOKEN_SECRET: ${JWT_REFRESH_TOKEN_SECRET} + JWT_REFRESH_TOKEN_EXPIRATION: ${JWT_REFRESH_TOKEN_EXPIRATION} + CLOUD_AWS_S3_BUCKET: ${CLOUD_AWS_S3_BUCKET} + CLOUD_AWS_CREDENTIALS_ACCESS_KEY: ${CLOUD_AWS_CREDENTIALS_ACCESS_KEY} + CLOUD_AWS_CREDENTIALS_SECRET_KEY: ${CLOUD_AWS_CREDENTIALS_SECRET_KEY} + DISCORD_WEBHOOK_URL: ${DISCORD_WEBHOOK_URL} + OPENAI_API_KEY: ${OPENAI_API_KEY} + SENTRY_DSN: ${SENTRY_DSN} + ADMIN_IDENTIFIER: ${ADMIN_IDENTIFIER} + ADMIN_PASSWORD: ${ADMIN_PASSWORD} + volumes: + - app_dev_logs:/app/logs + depends_on: + mysql-dev: + condition: service_healthy + redis-dev: + condition: service_healthy + networks: + - ono-network + healthcheck: + test: ["CMD-SHELL", "wget --quiet --tries=1 --spider http://localhost:8080/actuator/health || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + +networks: + ono-network: + driver: bridge + name: ono-network # prod와 같은 네트워크 이름 사용 + +volumes: + mysql_dev_data: + driver: local + redis_dev_data: + driver: local + app_dev_logs: + driver: local \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 00000000..e67e8a77 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,101 @@ +version: '3.8' + +services: + # MySQL Database - PRODUCTION + mysql-prod: + image: mysql:8.0 + container_name: ono-mysql-prod + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} + MYSQL_DATABASE: ono_db_prod + TZ: Asia/Seoul + ports: + - "3306:3306" + volumes: + - mysql_prod_data:/var/lib/mysql + command: + - --character-set-server=utf8mb4 + - --collation-server=utf8mb4_unicode_ci + - --default-time-zone=+09:00 + networks: + - ono-network + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"] + interval: 10s + timeout: 5s + retries: 5 + + # Redis Cache - PRODUCTION + redis-prod: + image: redis:7.2-alpine + container_name: ono-redis-prod + restart: unless-stopped + ports: + - "6379:6379" + volumes: + - redis_prod_data:/data + networks: + - ono-network + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + command: redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru + + # Spring Boot Application - PRODUCTION + app-prod: + image: ${DOCKER_USERNAME}/ono:prod-latest + container_name: ono-app-prod + restart: unless-stopped + ports: + - "8080:8080" # 운영 서버 포트 + environment: + SPRING_PROFILES_ACTIVE: prod + SPRING_DATASOURCE_URL: jdbc:mysql://mysql-prod:3306/ono_db_prod?serverTimezone=Asia/Seoul&characterEncoding=UTF-8 + SPRING_DATASOURCE_USERNAME: root + SPRING_DATASOURCE_PASSWORD: ${MYSQL_ROOT_PASSWORD} + SPRING_DATA_REDIS_HOST: redis-prod + SPRING_DATA_REDIS_PORT: 6379 + # 환경 변수 + SPRING_CRYPTO_SECRET_KEY: ${SPRING_CRYPTO_SECRET_KEY} + JWT_ACCESS_TOKEN_SECRET: ${JWT_ACCESS_TOKEN_SECRET} + JWT_ACCESS_TOKEN_EXPIRATION: ${JWT_ACCESS_TOKEN_EXPIRATION} + JWT_REFRESH_TOKEN_SECRET: ${JWT_REFRESH_TOKEN_SECRET} + JWT_REFRESH_TOKEN_EXPIRATION: ${JWT_REFRESH_TOKEN_EXPIRATION} + CLOUD_AWS_S3_BUCKET: ${CLOUD_AWS_S3_BUCKET} + CLOUD_AWS_CREDENTIALS_ACCESS_KEY: ${CLOUD_AWS_CREDENTIALS_ACCESS_KEY} + CLOUD_AWS_CREDENTIALS_SECRET_KEY: ${CLOUD_AWS_CREDENTIALS_SECRET_KEY} + DISCORD_WEBHOOK_URL: ${DISCORD_WEBHOOK_URL} + OPENAI_API_KEY: ${OPENAI_API_KEY} + SENTRY_DSN: ${SENTRY_DSN} + ADMIN_IDENTIFIER: ${ADMIN_IDENTIFIER} + ADMIN_PASSWORD: ${ADMIN_PASSWORD} + volumes: + - app_prod_logs:/app/logs + depends_on: + mysql-prod: + condition: service_healthy + redis-prod: + condition: service_healthy + networks: + - ono-network + healthcheck: + test: ["CMD-SHELL", "wget --quiet --tries=1 --spider http://localhost:8080/actuator/health || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + +networks: + ono-network: + driver: bridge + +volumes: + mysql_prod_data: + driver: local + redis_prod_data: + driver: local + app_prod_logs: + driver: local \ No newline at end of file diff --git a/src/main/java/com/aisip/OnO/backend/auth/config/SecurityConfig.java b/src/main/java/com/aisip/OnO/backend/auth/config/SecurityConfig.java index f2014754..09ba1ecc 100644 --- a/src/main/java/com/aisip/OnO/backend/auth/config/SecurityConfig.java +++ b/src/main/java/com/aisip/OnO/backend/auth/config/SecurityConfig.java @@ -33,10 +33,7 @@ @RequiredArgsConstructor public class SecurityConfig { - @Value("${spring.jwt.secret}") - private String secret; - - @Value("${spring.site.url}") // 애플리케이션의 HTTPS 기본 URL을 환경 변수로 받아옴 + @Value("${spring.site.url}") private String siteUrl; public final JwtTokenFilter jwtTokenFilter;