Skip to content

Merge pull request #141 from intel-fit/fix/kakaopay-url #271

Merge pull request #141 from intel-fit/fix/kakaopay-url

Merge pull request #141 from intel-fit/fix/kakaopay-url #271

Workflow file for this run

name: CI/CD Pipeline
on:
push:
branches:
- dev
env:
DOCKER_IMAGE: intelfit-backend
EC2_HOST: 43.200.40.140
EC2_USER: ubuntu
jobs:
test:
if : false
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: Admin123!
MYSQL_DATABASE: intelfit
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
redis:
image: redis:7.2-alpine
ports:
- 6379:6379
options: >-
--health-cmd="redis-cli ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: gradle
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Clean and refresh dependencies
run: ./gradlew clean --refresh-dependencies --no-daemon
- name: Run tests
run: ./gradlew test --no-daemon
env:
SPRING_PROFILES_ACTIVE: test
SPRING_DATASOURCE_URL: jdbc:mysql://localhost:3306/intelfit?serverTimezone=Asia/Seoul&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: Admin123!
SPRING_DATA_REDIS_HOST: localhost
SPRING_DATA_REDIS_PORT: 6379
# JWT
JWT_SECRET: intelfit-jwt-secret-key-for-production-use-minimum-32-characters-long-2024
# 🔥 이메일 SMTP
SPRING_MAIL_HOST: smtp.naver.com
SPRING_MAIL_PORT: 587
SPRING_MAIL_USERNAME: ${{ secrets.SPRING_MAIL_USERNAME }}
SPRING_MAIL_PASSWORD: ${{ secrets.SPRING_MAIL_PASSWORD }}
# 🔥 AI 서버 더미
AI_SERVER_URL: http://localhost:8000
AI_SERVER_API_KEY: dummy
# OCR Mock
AWS_S3_BUCKET: dummy
AWS_S3_BASE_URL: http://localhost
GEMINI_API_KEY: dummy
# Stripe Dummy (Fully Mock)
STRIPE_PUBLISHABLE_KEY: dummy
STRIPE_SECRET_KEY: dummy
STRIPE_PRICE_ID_MONTHLY: dummy
STRIPE_PRICE_ID_ANNUAL: dummy
STRIPE_WEBHOOK_SECRET: dummy
STRIPE_SUCCESS_URL: http://localhost
STRIPE_CANCEL_URL: http://localhost
# KakaoPay Dummy (Fully Mock)
KAKAOPAY_ADMIN_KEY: dummy
KAKAOPAY_REST_API_KEY: dummy
KAKAOPAY_CID: TC0ONETIME
KAKAOPAY_PLAN_MONTHLY_AMOUNT: 5900
KAKAOPAY_PLAN_ANNUAL_AMOUNT: 59000
KAKAOPAY_PLAN_MONTHLY_ITEM_NAME: "test-monthly"
KAKAOPAY_PLAN_ANNUAL_ITEM_NAME: "test-annual"
KAKAOPAY_APPROVAL_URL: http://localhost
KAKAOPAY_CANCEL_URL: http://localhost
KAKAOPAY_FAIL_URL: http://localhost
KAKAOPAY_PLAN_MONTHLY_TAX_FREE: 0
KAKAOPAY_PLAN_ANNUAL_TAX_FREE: 0
# 카카오 로그인 Dummy
KAKAO_CLIENT_ID: dummy
KAKAO_CLIENT_SECRET: dummy
build-and-deploy:
# needs: test // 필요시, 주석 제거하기
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: false
fetch-depth: 0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image
run: |
echo "🔧 Building Docker image..."
docker build --no-cache --pull -t ${{ env.DOCKER_IMAGE }}:${{ github.sha }} .
docker tag ${{ env.DOCKER_IMAGE }}:${{ github.sha }} ${{ env.DOCKER_IMAGE }}:latest
echo "Docker image built successfully"
- name: Save Docker image
run: |
docker save ${{ env.DOCKER_IMAGE }}:latest | gzip > image.tar.gz
ls -lh image.tar.gz
- name: Create Nginx config file
run: |
mkdir -p docker/nginx
cat > docker/nginx/nginx.conf << 'EOF'
server {
listen 80;
server_name _;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
client_max_body_size 10M;
location ^~ /.well-known/acme-challenge/ {
root /var/www/certbot;
default_type "text/plain";
try_files $uri =404;
}
location /health {
access_log off;
return 200 "OK";
add_header Content-Type text/plain;
}
# AI 서버 프록시 (HTTP)
location /ai/ {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' '*';
add_header 'Access-Control-Max-Age' 1728000;
return 204;
}
proxy_pass http://43.200.40.140:8000/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
}
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' '*';
add_header 'Access-Control-Max-Age' 1728000;
return 204;
}
proxy_pass http://spring-app:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
# HTTPS 서버 (도메인 접근용)
server {
listen 443 ssl;
server_name intelfits.com www.intelfits.com;
ssl_certificate /etc/letsencrypt/live/intelfits.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/intelfits.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
client_max_body_size 10M;
location /health {
access_log off;
return 200 "OK";
add_header Content-Type text/plain;
}
# AI 서버 프록시 (HTTPS)
location /ai/ {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' '*';
add_header 'Access-Control-Max-Age' 1728000;
return 204;
}
proxy_pass http://43.200.40.140:8000/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
}
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' '*';
add_header 'Access-Control-Max-Age' 1728000;
return 204;
}
proxy_pass http://spring-app:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
EOF
- name: Create deploy.sh
run: |
cat > deploy.sh << 'EOF'
#!/bin/bash
set -e
echo "IntelFit 백엔드 배포 시작..."
docker compose down --remove-orphans || true
docker image prune -f || true
docker compose up -d
echo "서비스 초기화 중..."
sleep 20
echo "Spring Boot 헬스체크..."
if docker exec spring-app curl -sf http://localhost:8080/actuator/health > /dev/null 2>&1; then
echo "Spring Boot 정상 작동"
else
echo "Spring Boot 헬스체크 실패"
docker logs --tail 50 spring-app
exit 1
fi
echo "🌐 Nginx 리버스 프록시 확인..."
if curl -sf http://localhost/health > /dev/null 2>&1; then
echo "Nginx 프록시 정상 작동"
else
echo "Nginx 프록시 응답 없음"
docker logs --tail 50 nginx
exit 1
fi
echo "배포 완료!"
docker compose ps
EOF
chmod +x deploy.sh
- name: Create .env file
run: |
cat > .env << EOF
KAKAO_CLIENT_ID=${{ secrets.KAKAO_CLIENT_ID }}
KAKAO_CLIENT_SECRET=${{ secrets.KAKAO_CLIENT_SECRET }}
EOF
- name: Copy files to EC2
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ env.EC2_HOST }}
username: ${{ env.EC2_USER }}
key: ${{ secrets.EC2_PRIVATE_KEY }}
source: "image.tar.gz,compose.yaml,deploy.sh,docker/nginx/nginx.conf,docker/mysql/exerciseCategoryDataBase.sql,.env"
target: "/home/ubuntu/back-end"
overwrite: true
- name: Deploy to EC2
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ env.EC2_HOST }}
username: ${{ env.EC2_USER }}
key: ${{ secrets.EC2_PRIVATE_KEY }}
script: |
cd /home/ubuntu/back-end
echo "📦 Docker 이미지 로드..."
docker load < image.tar.gz
rm image.tar.gz
./deploy.sh