Skip to content

Commit 92bf05e

Browse files
RookieANDclaude
andauthored
ci: Production 환경 배포 파이프라인 구축 (#69)
* refactor: Docker Compose 파일 환경별 분리 - docker-compose.yml 삭제 - docker-compose.dev.yml 생성 (Development 환경) - 포트: 8080:80, 8443:443 - 컨테이너: yogieat-*-dev - 네트워크: yogieat-dev-network - 볼륨: certbot_conf_dev, certbot_www_dev - docker-compose.prod.yml 생성 (Production 환경) - 포트: 80:80, 443:443 - 컨테이너: yogieat-*-prod - 네트워크: yogieat-prod-network - 볼륨: certbot_conf_prod, certbot_www_prod - Health check 추가 Co-Authored-By: Claude (us.anthropic.claude-sonnet-4-5-20250929-v1:0) <noreply@anthropic.com> * refactor: Nginx 설정 파일 환경별 분리 - client.conf 삭제 - client-dev.conf 생성 (Development) - 도메인: dev.yogieat.com - SSL 경로: /etc/letsencrypt-dev - Certbot webroot: /var/www/certbot-dev - Proxy: http://client-dev:3000 - 로그: /var/log/nginx/client-dev/ - client-prod.conf 생성 (Production) - 도메인: yogieat.com, www.yogieat.com - SSL 경로: /etc/letsencrypt-prod - Certbot webroot: /var/www/certbot-prod - Proxy: http://client-prod:3000 - 로그: /var/log/nginx/client-prod/ - Dockerfile 수정 - 두 환경의 설정 파일 모두 복사 - 환경별 로그 디렉토리 생성 - 환경별 Certbot webroot 디렉토리 생성 Co-Authored-By: Claude (us.anthropic.claude-sonnet-4-5-20250929-v1:0) <noreply@anthropic.com> * chore: Development 배포 워크플로우 docker-compose.dev.yml 사용 - docker-compose.yml -> docker-compose.dev.yml 참조로 변경 - 모든 docker compose 명령어에 -f docker-compose.dev.yml 추가 Co-Authored-By: Claude (us.anthropic.claude-sonnet-4-5-20250929-v1:0) <noreply@anthropic.com> * feat: Production 자동 배포 파이프라인 구축 - main 브랜치 푸시 시 자동 배포 - Production 환경 변수 사용 (NEXT_PUBLIC_API_URL_PROD) - docker-compose.prod.yml 사용 - Health Check 추가 - Discord 알림 (Production 전용 웹훅 지원) 배포 프로세스: 1. 변경 파일 감지 (client/nginx) 2. .env.production 생성 (Prod 환경 변수) 3. Docker 이미지 빌드 및 푸시 4. 서버 배포 5. Health Check 확인 6. Discord 알림 Co-Authored-By: Claude (us.anthropic.claude-sonnet-4-5-20250929-v1:0) <noreply@anthropic.com> --------- Co-authored-by: Claude (us.anthropic.claude-sonnet-4-5-20250929-v1:0) <noreply@anthropic.com>
1 parent bf07d68 commit 92bf05e

File tree

8 files changed

+332
-64
lines changed

8 files changed

+332
-64
lines changed

.github/workflows/development-deploy.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,13 @@ jobs:
7676
- name: Checkout Repository
7777
uses: actions/checkout@v5
7878

79-
- name: Copy docker-compose.yml to Server
79+
- name: Copy docker-compose.dev.yml to Server
8080
uses: appleboy/scp-action@v0.1.7
8181
with:
8282
host: ${{ secrets.SERVER_HOST }}
8383
username: ${{ secrets.SERVER_USERNAME }}
8484
key: ${{ secrets.SERVER_PRIVATE_KEY }}
85-
source: "docker-compose.yml"
85+
source: "docker-compose.dev.yml"
8686
target: "~/yogieat"
8787

8888
- name: Deploy Project to Gabia Server
@@ -94,13 +94,13 @@ jobs:
9494
script: |
9595
cd ~/yogieat
9696
97-
sudo docker compose -f ./docker-compose.yml down
97+
sudo docker compose -f ./docker-compose.dev.yml down
9898
# Clean up unused images only (preserve volumes with SSL certificates)
9999
sudo docker image prune -a -f
100100
101-
sudo docker compose -f ./docker-compose.yml pull
102-
sudo docker compose -f ./docker-compose.yml up -d
103-
sudo docker compose -f ./docker-compose.yml ps
101+
sudo docker compose -f ./docker-compose.dev.yml pull
102+
sudo docker compose -f ./docker-compose.dev.yml up -d
103+
sudo docker compose -f ./docker-compose.dev.yml ps
104104
105105
- name: Extract commit info
106106
id: commit
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
name: Production Deployment
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
jobs:
9+
build-image:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- name: Checkout
14+
uses: actions/checkout@v5
15+
16+
- name: Detect changed files
17+
uses: dorny/paths-filter@v3
18+
id: changes
19+
with:
20+
filters: |
21+
client:
22+
- 'app/**'
23+
- 'src/**'
24+
- 'public/**'
25+
- 'package.json'
26+
- 'pnpm-lock.yaml'
27+
- 'docker/client/**'
28+
- 'next.config.js'
29+
nginx:
30+
- 'docker/nginx/**'
31+
32+
- name: Login to DockerHub
33+
if: steps.changes.outputs.client == 'true' || steps.changes.outputs.nginx == 'true'
34+
uses: docker/login-action@v3
35+
with:
36+
username: ${{ secrets.DOCKER_HUB_USERNAME }}
37+
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
38+
39+
- name: Set up Docker Buildx
40+
if: steps.changes.outputs.client == 'true' || steps.changes.outputs.nginx == 'true'
41+
uses: docker/setup-buildx-action@v3
42+
43+
- name: Create .env.production
44+
if: steps.changes.outputs.client == 'true'
45+
run: |
46+
echo "NEXT_PUBLIC_API_URL=${{ secrets.NEXT_PUBLIC_API_URL_PROD }}" > .env.production
47+
echo "NEXT_PUBLIC_AWS_S3=${{ secrets.NEXT_PUBLIC_AWS_S3 }}" >> .env.production
48+
echo "NEXT_PUBLIC_GTM_ID=${{ secrets.NEXT_PUBLIC_GTM_ID_PROD || secrets.NEXT_PUBLIC_GTM_ID }}" >> .env.production
49+
50+
- name: Build and Push Client image
51+
if: steps.changes.outputs.client == 'true'
52+
uses: docker/build-push-action@v5
53+
with:
54+
push: true
55+
context: .
56+
file: ./docker/client/Dockerfile
57+
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/yogieat-client:latest
58+
cache-from: type=gha
59+
cache-to: type=gha,mode=max
60+
61+
- name: Build and Push Nginx image
62+
if: steps.changes.outputs.nginx == 'true'
63+
uses: docker/build-push-action@v5
64+
with:
65+
push: true
66+
file: ./docker/nginx/Dockerfile
67+
context: ./docker/nginx
68+
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/yogieat-nginx:latest
69+
cache-from: type=gha
70+
cache-to: type=gha,mode=max
71+
72+
deploy-application:
73+
needs: build-image
74+
runs-on: ubuntu-latest
75+
steps:
76+
- name: Checkout Repository
77+
uses: actions/checkout@v5
78+
79+
- name: Copy docker-compose.prod.yml to Server
80+
uses: appleboy/scp-action@v0.1.7
81+
with:
82+
host: ${{ secrets.SERVER_HOST }}
83+
username: ${{ secrets.SERVER_USERNAME }}
84+
key: ${{ secrets.SERVER_PRIVATE_KEY }}
85+
source: "docker-compose.prod.yml"
86+
target: "~/yogieat"
87+
88+
- name: Deploy Project to Gabia Server
89+
uses: appleboy/ssh-action@master
90+
with:
91+
host: ${{ secrets.SERVER_HOST }}
92+
username: ${{ secrets.SERVER_USERNAME }}
93+
key: ${{ secrets.SERVER_PRIVATE_KEY }}
94+
script: |
95+
cd ~/yogieat
96+
97+
sudo docker compose -f ./docker-compose.prod.yml down
98+
# Clean up unused images only (preserve volumes with SSL certificates)
99+
sudo docker image prune -a -f
100+
101+
sudo docker compose -f ./docker-compose.prod.yml pull
102+
sudo docker compose -f ./docker-compose.prod.yml up -d
103+
sudo docker compose -f ./docker-compose.prod.yml ps
104+
105+
- name: Health Check
106+
uses: appleboy/ssh-action@master
107+
with:
108+
host: ${{ secrets.SERVER_HOST }}
109+
username: ${{ secrets.SERVER_USERNAME }}
110+
key: ${{ secrets.SERVER_PRIVATE_KEY }}
111+
script: |
112+
cd ~/yogieat
113+
114+
echo "Waiting for services to be healthy..."
115+
sleep 10
116+
117+
# Check container status
118+
sudo docker compose -f ./docker-compose.prod.yml ps
119+
120+
# Check if client is healthy
121+
CLIENT_STATUS=$(sudo docker inspect --format='{{.State.Health.Status}}' yogieat-client-prod 2>/dev/null || echo "no-healthcheck")
122+
echo "Client health status: $CLIENT_STATUS"
123+
124+
if [ "$CLIENT_STATUS" != "healthy" ] && [ "$CLIENT_STATUS" != "no-healthcheck" ]; then
125+
echo "Client container is not healthy"
126+
sudo docker compose -f ./docker-compose.prod.yml logs client-prod
127+
exit 1
128+
fi
129+
130+
- name: Extract commit info
131+
id: commit
132+
run: |
133+
COMMIT_MSG=$(echo "${{ github.event.head_commit.message }}" | head -n 1)
134+
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
135+
echo "message=$COMMIT_MSG" >> $GITHUB_OUTPUT
136+
echo "sha=$SHORT_SHA" >> $GITHUB_OUTPUT
137+
138+
- name: Discord notification
139+
if: always()
140+
uses: sarisia/actions-status-discord@v1
141+
with:
142+
webhook: ${{ secrets.DISCORD_WEBHOOK_PROD || secrets.DISCORD_WEBHOOK }}
143+
title: "🚀 Production 배포"
144+
description: |
145+
**상태**: ${{ job.status }}
146+
**커밋**: [`${{ steps.commit.outputs.sha }}`](https://github.com/${{ github.repository }}/commit/${{ github.sha }})
147+
**메시지**: ${{ steps.commit.outputs.message }}
148+
**URL**: https://yogieat.com
149+
color: ${{ job.status == 'success' && '0x57F287' || '0xED4245' }}
150+
username: GitHub Actions

docker-compose.dev.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
services:
2+
client-dev:
3+
image: rookieanddocker/yogieat-client:latest
4+
container_name: yogieat-client-dev
5+
expose:
6+
- "3000"
7+
environment:
8+
- NODE_ENV=production
9+
- PORT=3000
10+
- HOSTNAME=0.0.0.0
11+
networks:
12+
- yogieat-dev-network
13+
labels:
14+
- "name=client-dev"
15+
- "mode=development"
16+
17+
nginx-dev:
18+
image: rookieanddocker/yogieat-nginx:latest
19+
container_name: yogieat-nginx-dev
20+
ports:
21+
- 8080:80
22+
- 8443:443
23+
volumes:
24+
- certbot_conf_dev:/etc/letsencrypt-dev
25+
- certbot_www_dev:/var/www/certbot-dev
26+
depends_on:
27+
- client-dev
28+
networks:
29+
- yogieat-dev-network
30+
labels:
31+
- "name=nginx-dev"
32+
- "mode=development"
33+
restart: on-failure
34+
35+
certbot-dev:
36+
image: certbot/certbot:latest
37+
container_name: yogieat-certbot-dev
38+
volumes:
39+
- certbot_conf_dev:/etc/letsencrypt
40+
- certbot_www_dev:/var/www/certbot
41+
networks:
42+
- yogieat-dev-network
43+
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
44+
labels:
45+
- "name=certbot-dev"
46+
- "mode=development"
47+
48+
networks:
49+
yogieat-dev-network:
50+
driver: bridge
51+
52+
volumes:
53+
certbot_conf_dev:
54+
certbot_www_dev:

docker-compose.prod.yml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
services:
2+
client-prod:
3+
image: rookieanddocker/yogieat-client:latest
4+
container_name: yogieat-client-prod
5+
expose:
6+
- "3000"
7+
environment:
8+
- NODE_ENV=production
9+
- PORT=3000
10+
- HOSTNAME=0.0.0.0
11+
networks:
12+
- yogieat-prod-network
13+
labels:
14+
- "name=client-prod"
15+
- "mode=production"
16+
healthcheck:
17+
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000"]
18+
interval: 30s
19+
timeout: 10s
20+
retries: 3
21+
start_period: 40s
22+
23+
nginx-prod:
24+
image: rookieanddocker/yogieat-nginx:latest
25+
container_name: yogieat-nginx-prod
26+
ports:
27+
- 80:80
28+
- 443:443
29+
volumes:
30+
- certbot_conf_prod:/etc/letsencrypt-prod
31+
- certbot_www_prod:/var/www/certbot-prod
32+
depends_on:
33+
- client-prod
34+
networks:
35+
- yogieat-prod-network
36+
labels:
37+
- "name=nginx-prod"
38+
- "mode=production"
39+
restart: on-failure
40+
41+
certbot-prod:
42+
image: certbot/certbot:latest
43+
container_name: yogieat-certbot-prod
44+
volumes:
45+
- certbot_conf_prod:/etc/letsencrypt
46+
- certbot_www_prod:/var/www/certbot
47+
networks:
48+
- yogieat-prod-network
49+
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
50+
labels:
51+
- "name=certbot-prod"
52+
- "mode=production"
53+
54+
networks:
55+
yogieat-prod-network:
56+
driver: bridge
57+
58+
volumes:
59+
certbot_conf_prod:
60+
certbot_www_prod:

docker-compose.yml

Lines changed: 0 additions & 44 deletions
This file was deleted.

docker/nginx/Dockerfile

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ FROM nginx:1.29.4-alpine
33
# Default Configuration 파일 삭제
44
RUN rm /etc/nginx/conf.d/default.conf
55

6-
# Configuration 파일 복사
7-
COPY ./config/client.conf /etc/nginx/conf.d/client.conf
6+
# Configuration 파일 복사 (Dev와 Prod 모두)
7+
COPY ./config/client-dev.conf /etc/nginx/conf.d/client-dev.conf
8+
COPY ./config/client-prod.conf /etc/nginx/conf.d/client-prod.conf
89
COPY ./config/nginx.conf /etc/nginx/nginx.conf
910

10-
# 로그 디렉토리 생성
11-
RUN mkdir -p /var/log/nginx/client
11+
# 로그 디렉토리 생성 (환경별 분리)
12+
RUN mkdir -p /var/log/nginx/client-dev /var/log/nginx/client-prod
1213

13-
# Certbot webroot 디렉토리 생성
14-
RUN mkdir -p /var/www/certbot
14+
# Certbot webroot 디렉토리 생성 (환경별 분리)
15+
RUN mkdir -p /var/www/certbot-dev /var/www/certbot-prod
1516

16-
CMD ["nginx", "-g", "daemon off;"]
17+
CMD ["nginx", "-g", "daemon off;"]

0 commit comments

Comments
 (0)