Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
a0fe53b
fix: μŠ΅λ“ μ‹ κ³ μž 정보 ν•„μˆ˜ -> 선택
randirao Jan 6, 2026
aa0f5f1
style: λΆˆν•„μš”ν•œ importλ¬Έ 제거
randirao Jan 6, 2026
08f5c54
Merge pull request #164 from BSSMEOD/fix/#163/μŠ΅λ“-μ‹ κ³ μž-ν•„μˆ˜-μ•„λ‹˜
randirao Jan 6, 2026
13f16ca
fix :: CD νŒŒμ΄ν”„λΌμΈ μ„€μ • 였λ₯˜ μˆ˜μ •
jmj732 Jan 6, 2026
435289b
Merge pull request #165 from BSSMEOD/feat/162-split-dev-serv
jmj732 Jan 6, 2026
405a8e9
fix(#162) : ssh λ³€κ²½
jmj732 Jan 6, 2026
adcf980
Merge pull request #167 from BSSMEOD/feat/162-split-dev-serv
jmj732 Jan 6, 2026
465e38c
fix :: CD νŒŒμ΄ν”„λΌμΈ μ»¨ν…Œμ΄λ„ˆ μ‹€ν–‰ 검증 κ°•ν™”
jmj732 Jan 6, 2026
7e8023c
Merge pull request #168 from BSSMEOD/feat/162-split-dev-serv
jmj732 Jan 6, 2026
88fb4be
fix :: CD νŒŒμ΄ν”„λΌμΈμ— MySQL λ³Όλ₯¨ μžλ™ 생성 둜직 μΆ”κ°€
jmj732 Jan 6, 2026
f01c906
Merge pull request #169 from BSSMEOD/feat/162-split-dev-serv
jmj732 Jan 6, 2026
bfb8c1a
fix(#162) : ssh λ³€κ²½
jmj732 Jan 6, 2026
1eb339f
Merge pull request #170 from BSSMEOD/feat/162-split-dev-serv
jmj732 Jan 6, 2026
cc5dfac
fix(#162) : ssh λ³€κ²½
jmj732 Jan 6, 2026
57696e2
Merge pull request #171 from BSSMEOD/feat/162-split-dev-serv
jmj732 Jan 6, 2026
b6626ca
fix(#162) : env μˆ˜μ •
jmj732 Jan 6, 2026
84f3168
Merge pull request #172 from BSSMEOD/feat/162-split-dev-serv
jmj732 Jan 6, 2026
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
225 changes: 225 additions & 0 deletions .github/workflows/cd-prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
name: CD - Production Deploy

on:
push:
branches: [production]
workflow_dispatch:
inputs:
confirm:
description: '운영 배포λ₯Ό μ§„ν–‰ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?'
required: true
type: boolean
default: false

env:
REGISTRY: ghcr.io

jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: μ½”λ“œ 체크아웃
uses: actions/checkout@v4

- name: Java 17 μ„€μ •
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: 'gradle'

- name: Gradle μ‹€ν–‰ κΆŒν•œ λΆ€μ—¬
run: chmod +x ./gradlew

- name: μ• ν”Œλ¦¬μΌ€μ΄μ…˜ JAR λΉŒλ“œ
run: ./gradlew bootJar --no-daemon -x test

- name: QEMU μ„€μ • (ARM64 크둜슀 컴파일)
uses: docker/setup-qemu-action@v3
with:
platforms: arm64

- name: 이미지 이름 μ†Œλ¬Έμž λ³€ν™˜
id: image_name
run: |
IMAGE_NAME=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')
echo "name=${IMAGE_NAME}" >> $GITHUB_OUTPUT
echo "μ†Œλ¬Έμž λ³€ν™˜λœ 이미지 이름: ${IMAGE_NAME}"

- name: Docker Buildx μ„€μ •
uses: docker/setup-buildx-action@v3

- name: GitHub Container Registry 둜그인
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Docker 메타데이터 μΆ”μΆœ
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ steps.image_name.outputs.name }}
tags: |
type=raw,value=production
type=sha,prefix=prod-

- name: Docker 이미지 λΉŒλ“œ 및 ν‘Έμ‹œ
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
platforms: linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=eod-prod-build
cache-to: type=gha,scope=eod-prod-build,mode=max
build-args: |
BUILDKIT_INLINE_CACHE=1

- name: 이미지 λ‹€μ΄μ œμŠ€νŠΈ 좜λ ₯
run: echo ${{ steps.meta.outputs.digest }}

deploy:
needs: build-and-push
runs-on: ubuntu-latest

steps:
- name: μ„œλ²„μ— SSH 접속 및 Docker Compose 배포
uses: appleboy/[email protected]
with:
host: ${{ secrets.PROD_SSH_HOST }}
username: ${{ secrets.PROD_SSH_USERNAME }}
key: ${{ secrets.SSH_KEY }}
port: ${{ secrets.PROD_SSH_PORT || 22 }}
script: |
# 배포 디렉토리 생성 및 이동
mkdir -p ~/eod/prod
cd ~/eod/prod

# GitHub Repository Clone λ˜λŠ” Pull
if [ -d ".git" ]; then
echo "κΈ°μ‘΄ μ €μž₯μ†Œ μ—…λ°μ΄νŠΈ 쀑..."
git fetch origin production
git reset --hard origin/production
else
echo "μ €μž₯μ†Œ 볡제 쀑..."
rm -rf * .* 2>/dev/null || true
git clone -b production https://github.com/${{ github.repository }}.git .
fi

# .env 파일 생성 (GitHub Secrets μ‚¬μš©)
REPO_LOWERCASE=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')
cat > .env << EOF
GITHUB_REPOSITORY=${REPO_LOWERCASE}
DOCKER_IMAGE_TAG=production
SPRING_DATASOURCE_URL=${{ secrets.PROD_DB_URL }}
SPRING_DATASOURCE_USERNAME=${{ secrets.PROD_DB_USERNAME }}
SPRING_DATASOURCE_PASSWORD=${{ secrets.PROD_DB_PASSWORD }}
MYSQL_ROOT_PASSWORD=${{ secrets.PROD_MYSQL_ROOT_PASSWORD }}
MYSQL_DATABASE=${{ secrets.PROD_MYSQL_DATABASE }}
MYSQL_USER=${{ secrets.PROD_DB_USERNAME }}
MYSQL_PASSWORD=${{ secrets.PROD_DB_PASSWORD }}
JWT_SECRET=${{ secrets.JWT_SECRET }}
BASE_URL=${{ secrets.PROD_BASE_URL }}
FRONTEND_BASE_URL=${{ secrets.PROD_FRONTEND_BASE_URL }}
LOG_DIR=${{ secrets.PROD_LOG_DIR }}
BSM_CLIENT_ID=${{ secrets.BSM_CLIENT_ID }}
BSM_CLIENT_SECRET=${{ secrets.BSM_CLIENT_SECRET }}
BSM_OAUTH_BASE_URL=${{ secrets.BSM_OAUTH_BASE_URL }}
BSM_REDIRECT_URI=${{ secrets.BSM_REDIRECT_URI }}
FILE_UPLOAD_BASE_URL=${{ secrets.FILE_UPLOAD_BASE_URL }}
EOF

# GitHub Container Registry 둜그인
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin

# ν™˜κ²½ λ³€μˆ˜ 확인 (디버깅)
echo "GITHUB_REPOSITORY (μ†Œλ¬Έμž): ${REPO_LOWERCASE}"

# MySQL λ³Όλ₯¨ 쑴재 μ—¬λΆ€ 확인 및 생성
echo "========================================="
echo "πŸ“¦ MySQL λ³Όλ₯¨ 확인 쀑..."
if ! docker volume inspect eod_mysql-data-prod >/dev/null 2>&1; then
echo "⚠️ λ³Όλ₯¨μ΄ μ‘΄μž¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. μƒˆλ‘œ μƒμ„±ν•©λ‹ˆλ‹€..."
docker volume create eod_mysql-data-prod
echo "βœ… λ³Όλ₯¨ 생성 μ™„λ£Œ: eod_mysql-data-prod"
else
echo "βœ… λ³Όλ₯¨μ΄ 이미 μ‘΄μž¬ν•©λ‹ˆλ‹€: eod_mysql-data-prod"
fi
echo "========================================="

# Docker Compose둜 배포 (production νƒœκ·Έ μ‚¬μš©)
echo "========================================="
echo "πŸš€ PRODUCTION Docker Compose 배포 μ‹œμž‘"
echo "========================================="
export DOCKER_IMAGE_TAG=production
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml down
docker compose -f docker-compose.prod.yml up -d --remove-orphans

# 배포 직후 μƒνƒœ 확인
echo "========================================="
echo "πŸ“Š 배포 직후 μ»¨ν…Œμ΄λ„ˆ μƒνƒœ:"
docker compose -f docker-compose.prod.yml ps
echo "========================================="

# μ»¨ν…Œμ΄λ„ˆ μ‹œμž‘ λŒ€κΈ°
echo "μ»¨ν…Œμ΄λ„ˆ μ‹œμž‘ λŒ€κΈ° 쀑..."
sleep 10

# μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” 이미지 정리
docker image prune -af

- name: 배포 μƒνƒœ 확인
uses: appleboy/[email protected]
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USERNAME }}
key: ${{ secrets.SSH_KEY }}
port: ${{ secrets.SSH_PORT || 22 }}
script: |
cd ~/eod/prod
echo "========================================="
echo "πŸš€ PRODUCTION ν™˜κ²½ 배포 μ™„λ£Œ"
echo "========================================="
echo "πŸ“‹ ν™˜κ²½ λ³€μˆ˜ 확인 (민감 정보 λ§ˆμŠ€ν‚Ή):"
cat .env | sed 's/PASSWORD=.*/PASSWORD=***/g' | sed 's/SECRET=.*/SECRET=***/g'
echo "========================================="
echo "🐳 μ»¨ν…Œμ΄λ„ˆ μ‹€ν–‰ μƒνƒœ:"
docker compose -f docker-compose.prod.yml ps

# μ»¨ν…Œμ΄λ„ˆ μ‹€ν–‰ 검증 (app μ»¨ν…Œμ΄λ„ˆκ°€ λ°˜λ“œμ‹œ λ– μžˆμ–΄μ•Ό 함)
if ! docker compose -f docker-compose.prod.yml ps | grep -q "eod-app.*Up"; then
echo "❌ ERROR: app μ»¨ν…Œμ΄λ„ˆκ°€ μ‹€ν–‰λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€!"
docker compose -f docker-compose.prod.yml logs --tail=100 app
exit 1
fi
echo "βœ… app μ»¨ν…Œμ΄λ„ˆ 정상 μ‹€ν–‰ 확인"

echo "========================================="
echo "πŸ“Š Docker 이미지 확인:"
docker images | grep eod || true
echo "========================================="
echo "πŸ“ MySQL 둜그 (졜근 20쀄):"
docker compose -f docker-compose.prod.yml logs --tail=20 mysql || true
echo "========================================="
echo "πŸ“ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 둜그 (졜근 50쀄):"
docker compose -f docker-compose.prod.yml logs --tail=50 app || true
echo "========================================="
echo "πŸ” 포트 μ‚¬μš© 확인:"
netstat -tuln | grep -E ':(8020|3307)' || true

- name: 배포 μ™„λ£Œ μ•Œλ¦Ό
if: success()
run: |
IMAGE_NAME=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')
echo "βœ… PRODUCTION Docker Compose 배포 μ™„λ£Œ!"
echo "μ„œλ²„: ${{ secrets.SSH_HOST }}"
echo "이미지: ${{ env.REGISTRY }}/${IMAGE_NAME}:production"
48 changes: 39 additions & 9 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,31 @@ jobs:
# ν™˜κ²½ λ³€μˆ˜ 확인 (디버깅)
echo "GITHUB_REPOSITORY (μ†Œλ¬Έμž): ${REPO_LOWERCASE}"

# MySQL λ³Όλ₯¨ 쑴재 μ—¬λΆ€ 확인 및 생성
echo "========================================="
echo "πŸ“¦ MySQL λ³Όλ₯¨ 확인 쀑..."
if ! docker volume inspect eod_mysql-data-dev >/dev/null 2>&1; then
echo "⚠️ λ³Όλ₯¨μ΄ μ‘΄μž¬ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. μƒˆλ‘œ μƒμ„±ν•©λ‹ˆλ‹€..."
docker volume create eod_mysql-data-dev
echo "βœ… λ³Όλ₯¨ 생성 μ™„λ£Œ: eod_mysql-data-dev"
else
echo "βœ… λ³Όλ₯¨μ΄ 이미 μ‘΄μž¬ν•©λ‹ˆλ‹€: eod_mysql-data-dev"
fi
echo "========================================="

# Docker Compose둜 배포
docker compose -f docker-compose.prod.yml pull
docker compose -f docker-compose.prod.yml down
docker compose -f docker-compose.prod.yml up -d
echo "========================================="
echo "πŸš€ Docker Compose 배포 μ‹œμž‘"
echo "========================================="
docker compose -f docker-compose.yml pull
docker compose -f docker-compose.yml down
docker compose -f docker-compose.yml up -d --remove-orphans

# 배포 직후 μƒνƒœ 확인
echo "========================================="
echo "πŸ“Š 배포 직후 μ»¨ν…Œμ΄λ„ˆ μƒνƒœ:"
docker compose -f docker-compose.yml ps
echo "========================================="

# μ»¨ν…Œμ΄λ„ˆ μ‹œμž‘ λŒ€κΈ°
echo "μ»¨ν…Œμ΄λ„ˆ μ‹œμž‘ λŒ€κΈ° 쀑..."
Expand All @@ -163,22 +184,31 @@ jobs:
cd ~/eod
echo "========================================="
echo "πŸ“‹ ν™˜κ²½ λ³€μˆ˜ 확인 (민감 정보 λ§ˆμŠ€ν‚Ή):"
cat .env | sed 's/PASSWORD=.*/PASSWORD=***/g'
cat .env | sed 's/PASSWORD=.*/PASSWORD=***/g' | sed 's/SECRET=.*/SECRET=***/g'
echo "========================================="
echo "🐳 μ»¨ν…Œμ΄λ„ˆ μ‹€ν–‰ μƒνƒœ:"
docker compose -f docker-compose.prod.yml ps
docker compose -f docker-compose.yml ps

# μ»¨ν…Œμ΄λ„ˆ μ‹€ν–‰ 검증 (app μ»¨ν…Œμ΄λ„ˆκ°€ λ°˜λ“œμ‹œ λ– μžˆμ–΄μ•Ό 함)
if ! docker compose -f docker-compose.yml ps | grep -q "eod-app-dev.*Up"; then
echo "❌ ERROR: app μ»¨ν…Œμ΄λ„ˆκ°€ μ‹€ν–‰λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€!"
docker compose -f docker-compose.yml logs --tail=100 app
exit 1
fi
echo "βœ… app μ»¨ν…Œμ΄λ„ˆ 정상 μ‹€ν–‰ 확인"

echo "========================================="
echo "πŸ“Š Docker 이미지 확인:"
docker images | grep eod
docker images | grep eod || true
echo "========================================="
echo "πŸ“ MySQL 둜그 (졜근 20쀄):"
docker compose -f docker-compose.prod.yml logs --tail=20 mysql
docker compose -f docker-compose.yml logs --tail=20 mysql || true
echo "========================================="
echo "πŸ“ μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 둜그 (졜근 50쀄):"
docker compose -f docker-compose.prod.yml logs --tail=50 app
docker compose -f docker-compose.yml logs --tail=50 app || true
echo "========================================="
echo "πŸ” 포트 μ‚¬μš© 확인:"
netstat -tuln | grep -E ':(8000|3306)'
netstat -tuln | grep -E ':(8000|3306)' || true

- name: 배포 μ™„λ£Œ μ•Œλ¦Ό
if: success()
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: CI

on:
push:
branches: [ "main", "master", "develop" ]
branches: [ "main", "master", "develop", "production" ]
pull_request:
branches: [ "main", "master", "develop" ]
branches: [ "main", "master", "develop", "production" ]

jobs:
build:
Expand Down
14 changes: 6 additions & 8 deletions docker-compose.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,15 @@ services:
image: ghcr.io/${GITHUB_REPOSITORY}:latest
container_name: eod-app
volumes:
- /eod/logs:/logs
- /eod/prod/logs:/logs
- /eod/uploads:/eod/uploads
restart: always
ports:
- "8000:8080"
- "8020:8080"
environment:
- SPRING_DATASOURCE_URL=${SPRING_DATASOURCE_URL}
- SPRING_DATASOURCE_USERNAME=${SPRING_DATASOURCE_USERNAME}
- SPRING_DATASOURCE_PASSWORD=${SPRING_DATASOURCE_PASSWORD}
- GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
- GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
- JWT_SECRET=${JWT_SECRET}
- BASE_URL=${BASE_URL}
- FRONTEND_BASE_URL=${FRONTEND_BASE_URL}
Expand All @@ -40,9 +38,9 @@ services:
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
ports:
- "3306:3306"
- "3307:3306"
volumes:
- mysql-data:/var/lib/mysql
- mysql-data-prod:/var/lib/mysql
networks:
- eod-network
healthcheck:
Expand All @@ -52,9 +50,9 @@ services:
retries: 10

volumes:
mysql-data:
mysql-data-prod:
external: true
name: eod_mysql-data
name: eod_mysql-data-prod

networks:
eod-network:
Expand Down
Loading