Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 9 additions & 12 deletions .github/workflows/DEV-CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,22 @@ on:
branches: [ "develop" ]

jobs:
build:
build-and-test:
runs-on: ubuntu-24.04
env:
working-directory: .

# Checkout - 가상 머신에 체크아웃
steps:
- name: 체크아웃
# 1. 코드 체크아웃
- name: Checkout
uses: actions/checkout@v3

# JDK setting - JDK 21 설정
# 2. JDK 21 설정
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21'

# Gradle caching - 빌드 시간 향상
# 3. Gradle 캐싱 (빌드 속도 향상)
- name: Gradle Caching
uses: actions/cache@v3
with:
Expand All @@ -33,10 +31,9 @@ jobs:
restore-keys: |
${{ runner.os }}-gradle-

# Gradle build - 테스트 없이 gradle 빌드
- name: 빌드
# 4. Gradle 빌드 및 테스트 실행
- name: Build and Test with Gradle
run: |
chmod +x gradlew
./gradlew build -x test
working-directory: ${{ env.working-directory }}
shell: bash
./gradlew build
shell: bash
132 changes: 132 additions & 0 deletions .github/workflows/DOCKER-CD-PRODUCTION.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
name: DOCKER-CD-PRODUCTION

on:
push:
branches: [ "main" ]

jobs:
ci:
runs-on: ubuntu-24.04

steps:
- name: Checkout
uses: actions/checkout@v3

- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21'

- name: Gradle Caching
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-

- name: Build and Test with Gradle
run: |
chmod +x gradlew
./gradlew build
shell: bash

- name: Login to Docker Hub
uses: docker/login-action@v2.2.0
with:
username: ${{ secrets.DOCKER_LOGIN_USERNAME }}
password: ${{ secrets.DOCKER_LOGIN_ACCESSTOKEN }}

- name: Build and push Docker image for Production
run: |
docker build --platform linux/amd64 -t terningpoint/terning2025 .
docker push terningpoint/terning2025

cd:
needs: ci
runs-on: ubuntu-24.04
environment: production

steps:
- name: Deploy to Production Server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_IP }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_KEY }}
script: |
# -- 변수 설정 --
APP_NAME="terning2025-prod"
IMAGE_NAME="terningpoint/terning2025"
NGINX_CONFIG_PATH="/etc/nginx"
SERVICE_URL_INC_PATH="${NGINX_CONFIG_PATH}/conf.d/service-url.inc"

Comment on lines +60 to +66
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 스크립트가 긴데, 중간에 하나의 명령어가 실패하더라도 다음 명령어를 계속 실행할 수 있는 상태라 set -e를 스크립트 상단에 추가하는 것은 어떨까요? 명령어가 하나라도 실패하면 스크립트 전체를 중단시키도록 설정하는게 좋을 것 같아서 제안드려봅니다 ㅎㅎ

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

꼼꼼하게 리뷰해주셔서 감사합니다! 👍

말씀해주신 대로 set -e 옵션을 추가해서 스크립트의 안정성을 높이는 것, 정말 좋은 제안이라고 생각합니다. 중간에 명령어가 실패했을 때 즉시 파이프라인을 중단시키는 것이 더 안전한 방법이겠네요.

다만 현재 PR에서는 핵심 기능 구현에 더 집중하고 싶습니다. 제안해주신 내용은 파이프라인 안정성 강화라는 좋은 개선 과제이니, 별도 이슈로 등록해서 다음 단계에서 꼭 반영하도록 하겠습니다.

다시 한번 좋은 의견 감사드립니다! 😊

echo "### 1. 최신 Docker 이미지를 pull합니다."
docker pull ${IMAGE_NAME}:latest

echo "### 2. 현재 실행 중인 포트(Blue)와 새로 실행할 포트(Green)를 결정합니다."
RUNNING_PORT=$(docker ps --filter "name=${APP_NAME}" --format "{{.Ports}}" | grep -o '[0-9]\{4\}->8080' | awk -F'->' '{print $1}')

if [ "${RUNNING_PORT}" == "8080" ]; then
NEW_PORT=8081
else
NEW_PORT=8080
fi

echo " > 현재 서비스 포트(Blue): ${RUNNING_PORT:-없음}"
echo " > 새로 실행할 포트(Green): ${NEW_PORT}"

echo "### 3. 새로운 버전의 애플리케이션(Green)을 실행합니다."
docker run -d --name ${APP_NAME}-${NEW_PORT} --restart always \
-p ${NEW_PORT}:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
-e SPRING_DATASOURCE_URL='${{ secrets.DB_URL }}' \
-e SPRING_DATASOURCE_USERNAME=${{ secrets.DB_USERNAME }} \
-e SPRING_DATASOURCE_PASSWORD=${{ secrets.DB_PASSWORD }} \
-e SPRING_JPA_DEFAULT_SCHEMA=${{ secrets.SPRING_JPA_DEFAULT_SCHEMA }} \
-e JWT_SECRET_KEY='${{ secrets.JWT_SECRET_KEY }}' \
-e JWT_ACCESS_TOKEN_EXPIRED=${{ secrets.JWT_ACCESS_TOKEN_EXPIRED }} \
-e JWT_REFRESH_TOKEN_EXPIRED=${{ secrets.JWT_REFRESH_TOKEN_EXPIRED }} \
-e OPERATION_BASE_URL='${{ secrets.OPERATION_BASE_URL }}' \
-e DISCORD_WEBHOOK_URL='${{ secrets.DISCORD_WEBHOOK_URL }}' \
-e FIREBASE_SERVICE_KEY_JSON='${{ secrets.FIREBASE_SERVICE_KEY_JSON }}' \
-e LOGGING_LOCATION=${{ secrets.LOGGING_LOCATION }} \
-e TZ=Asia/Seoul \
-v /home/ubuntu:/home/ubuntu/prod-logs \
${IMAGE_NAME}:latest

echo "### 4. 헬스 체크를 시작합니다."
sleep 10
for retry_count in {1..10}; do
echo " > [${retry_count}/10] 서버 상태 체크 중..."
response=$(curl -s http://localhost:${NEW_PORT}/actuator/health)
up_count=$(echo "$response" | grep -c 'UP')

if [ $up_count -ge 1 ]; then
echo " > ✅ 서버 실행 성공 (포트: ${NEW_PORT})"
break
fi
if [ $retry_count -eq 10 ]; then
echo " > ❌ 서버 헬스체크 실패. 배포를 중단하고 새 컨테이너를 종료합니다."
docker rm -f ${APP_NAME}-${NEW_PORT}
exit 1
fi
sleep 5
done

echo "### 5. Nginx 설정을 변경하여 트래픽을 새 포트(Green)로 전환합니다."
echo "set \$service_url http://127.0.0.1:${NEW_PORT};" | sudo tee ${SERVICE_URL_INC_PATH}
sudo nginx -s reload

echo "### 6. 이전 버전의 컨테이너(Blue)를 종료 및 삭제합니다."
if [ -n "${RUNNING_PORT}" ]; then
docker rm -f ${APP_NAME}-${RUNNING_PORT}
fi

echo "### 7. 사용하지 않는 Docker 이미지를 정리합니다."
docker image prune -af

echo "✅ Production 배포가 성공적으로 완료되었습니다. 현재 서비스 포트: ${NEW_PORT}"
125 changes: 85 additions & 40 deletions .github/workflows/DOCKER-CD-STAGING.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,21 @@ on:

jobs:
ci:
# Using Environment - Staging 환경 사용
# environment: staging..
runs-on: ubuntu-24.04
env:
working-directory: .

# Checkout - 가상 머신에 체크아웃
steps:
- name: 체크아웃
# 1. 소스 코드 체크아웃
- name: Checkout
uses: actions/checkout@v3

# JDK setting - JDK 21 설정
# 2. JDK 21 설정
- name: Set up JDK 21
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '21'

# Gradle caching - 빌드 시간 향상
# 3. Gradle 캐싱 (빌드 속도 향상)
- name: Gradle Caching
uses: actions/cache@v3
with:
Expand All @@ -35,59 +31,108 @@ jobs:
restore-keys: |
${{ runner.os }}-gradle-

# create .yml - yml 파일 생성
- name: application.yml 생성
run: |
mkdir -p ./src/main/resources && cd $_
touch ./application.yml
echo "${{ secrets.YML }}" > ./application.yml
cat ./application.yml
working-directory: ${{ env.working-directory }}

- name: application-staging.yml 생성
run: |
cd ./src/main/resources
touch ./application-staging.yml
echo "${{ secrets.YML_STAGING }}" > ./application-staging.yml
working-directory: ${{ env.working-directory }}

# Gradle build - 테스트 없이 gradle 빌드
- name: 빌드
# 4. Gradle 빌드 및 테스트 실행
- name: Build and Test with Gradle
run: |
chmod +x gradlew
./gradlew build -x test
working-directory: ${{ env.working-directory }}
./gradlew build
shell: bash

- name: docker 로그인
uses: docker/setup-buildx-action@v2.9.1

- name: login docker hub
# 5. Docker Hub 로그인 (Repository Secrets 사용)
- name: Login to Docker Hub
uses: docker/login-action@v2.2.0
with:
username: ${{ secrets.DOCKER_LOGIN_USERNAME }}
password: ${{ secrets.DOCKER_LOGIN_ACCESSTOKEN }}

- name: docker image 빌드 및 푸시
# 6. Docker 이미지 빌드 및 푸시
- name: Build and push Docker image for Staging
run: |
docker build -f Dockerfile-staging --platform linux/amd64 -t terningpoint/terning2025-staging .
docker push terningpoint/terning2025-staging
working-directory: ${{ env.working-directory }}

cd:
needs: ci
runs-on: ubuntu-24.04
environment: staging

steps:
- name: Debugging - Echo Host
run: echo "${{ secrets.STAGING_SERVER_IP }}"

- name: docker 컨테이너 실행
- name: Deploy to Staging Server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.STAGING_SERVER_IP }}
username: ${{ secrets.STAGING_SERVER_USER }}
key: ${{ secrets.STAGING_SERVER_KEY }}
script: |
cd ~
./deploy-staging.sh
# -- 변수 설정 --
APP_NAME="terning2025-staging"
IMAGE_NAME="terningpoint/terning2025-staging"
NGINX_CONFIG_PATH="/etc/nginx"
SERVICE_URL_INC_PATH="${NGINX_CONFIG_PATH}/conf.d/service-url-staging.inc"

echo "### 1. 최신 Docker 이미지를 pull합니다."
docker pull ${IMAGE_NAME}:latest

echo "### 2. 현재 실행 중인 포트(Blue)와 새로 실행할 포트(Green)를 결정합니다."
RUNNING_PORT=$(docker ps --filter "name=${APP_NAME}" --format "{{.Ports}}" | grep -o '[0-9]\{4\}->8080' | awk -F'->' '{print $1}')

if [ "${RUNNING_PORT}" == "8080" ]; then
NEW_PORT=8081
else
NEW_PORT=8080
fi

echo " > 현재 서비스 포트(Blue): ${RUNNING_PORT:-없음}"
echo " > 새로 실행할 포트(Green): ${NEW_PORT}"

echo "### 3. 새로운 버전의 애플리케이션(Green)을 실행합니다."
docker run -d --name ${APP_NAME}-${NEW_PORT} --restart always \
-p ${NEW_PORT}:8080 \
-e SPRING_PROFILES_ACTIVE=staging \
-e SPRING_DATASOURCE_URL='${{ secrets.DB_URL }}' \
-e SPRING_DATASOURCE_USERNAME=${{ secrets.DB_USERNAME }} \
-e SPRING_DATASOURCE_PASSWORD=${{ secrets.DB_PASSWORD }} \
-e SPRING_JPA_DEFAULT_SCHEMA=${{ secrets.SPRING_JPA_DEFAULT_SCHEMA }} \
-e JWT_SECRET_KEY='${{ secrets.JWT_SECRET_KEY }}' \
-e JWT_ACCESS_TOKEN_EXPIRED=${{ secrets.JWT_ACCESS_TOKEN_EXPIRED }} \
-e JWT_REFRESH_TOKEN_EXPIRED=${{ secrets.JWT_REFRESH_TOKEN_EXPIRED }} \
-e OPERATION_BASE_URL='${{ secrets.OPERATION_BASE_URL }}' \
-e DISCORD_WEBHOOK_URL='${{ secrets.DISCORD_WEBHOOK_URL }}' \
-e FIREBASE_SERVICE_KEY_JSON='${{ secrets.FIREBASE_SERVICE_KEY_JSON }}' \
-e LOGGING_LOCATION=${{ secrets.LOGGING_LOCATION }} \
-e TZ=Asia/Seoul \
-v /home/ubuntu:/home/ubuntu/dev-logs \
${IMAGE_NAME}:latest

echo "### 4. 헬스 체크를 시작합니다."
sleep 10
for retry_count in {1..10}; do
echo " > [${retry_count}/10] 서버 상태 체크 중..."
response=$(curl -s http://localhost:${NEW_PORT}/actuator/health)
up_count=$(echo "$response" | grep -c 'UP')

if [ $up_count -ge 1 ]; then
echo " > ✅ 서버 실행 성공 (포트: ${NEW_PORT})"
break
fi
if [ $retry_count -eq 10 ]; then
echo " > ❌ 서버 헬스체크 실패. 배포를 중단하고 새 컨테이너를 종료합니다."
docker rm -f ${APP_NAME}-${NEW_PORT}
exit 1
fi
sleep 5
done

echo "### 5. Nginx 설정을 변경하여 트래픽을 새 포트(Green)로 전환합니다."
echo "set \$service_url http://127.0.0.1:${NEW_PORT};" | sudo tee ${SERVICE_URL_INC_PATH}
sudo nginx -s reload

echo "### 6. 이전 버전의 컨테이너(Blue)를 종료 및 삭제합니다."
if [ -n "${RUNNING_PORT}" ]; then
docker rm -f ${APP_NAME}-${RUNNING_PORT}
fi

echo "### 7. 사용하지 않는 Docker 이미지를 정리합니다."
docker image prune -af

echo "✅ Staging 배포가 성공적으로 완료되었습니다. 현재 서비스 포트: ${NEW_PORT}"
5 changes: 0 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,5 @@ Network Trash Folder
Temporary Items
.apdisk

# application.yml
src/main/resources/application.yml
src/main/resources/application-dev.yml
src/test/resources/application-test.yml

# Q-Class
src/main/generated
Loading
Loading