Skip to content

Commit dd42e15

Browse files
authored
feat: 멀티 애플리케이션 배포 기능 구현 (#124)
* [BOOK-350] refactor: apis, admin, batch - 모듈 추가가 되어도 component 스캔 대응할 수 있도록 리팩토링 * [BOOK-350] feat: observability 모듈 생성 및 MDC, Actuator 설정을 gateway에서 observability 모듈로 마이그레이션 * [BOOK-350] chore: observability 모듈 등록 및 각 모듈 의존성 추가 * [BOOK-350] chore: observability - 관련 의존성 추가 * [BOOK-350] refactor: admin, batch 모듈 지원을 위한 Dockerfile 및 ci 로직 변경 * [BOOK-350] refactor: Matrix Strategy를 통한 병렬 배포 기능 도입 * [BOOK-350] refactor: batch 모듈 배포 지원을 위해 스크립트 리팩토링 * [BOOK-350] fix: batch - batch 모듈 포트 수정 * [BOOK-350] chore: Server port 정의는 docker compose에서 관리하도록 변경 * [BOOK-350] chore: shell 프로세스를 java 프로세스로 대체하여 graceful shutdown 지원 * [BOOK-350] chore: prod도 Server port 정의를 docker compose에서 관리하도록 변경 * [BOOK-350] chore: prod 환경 deploy 스크립트 마이그레이션을 위한 파일 이름 변경
1 parent 68085fa commit dd42e15

File tree

26 files changed

+499
-314
lines changed

26 files changed

+499
-314
lines changed
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
name: 'Deploy Module'
2+
description: 'Build, push and deploy a module to server (dev/prod)'
3+
4+
inputs:
5+
environment:
6+
description: 'Environment (dev or prod)'
7+
required: true
8+
module:
9+
description: 'Module name (apis, admin, batch)'
10+
required: true
11+
port:
12+
description: 'Server port (for logging only - actual port defined in docker-compose.yml)'
13+
required: false
14+
default: 'N/A'
15+
dockerhub-username:
16+
description: 'Docker Hub username'
17+
required: true
18+
dockerhub-token:
19+
description: 'Docker Hub token'
20+
required: true
21+
secret-properties:
22+
description: 'Secret properties (dev or prod)'
23+
required: true
24+
apple-auth-key:
25+
description: 'Apple Auth Key'
26+
required: true
27+
host:
28+
description: 'Server host'
29+
required: true
30+
username:
31+
description: 'Server username'
32+
required: true
33+
ssh-key:
34+
description: 'Server SSH key'
35+
required: true
36+
ssh-port:
37+
description: 'Server SSH port'
38+
required: true
39+
discord-webhook-url:
40+
description: 'Discord webhook URL'
41+
required: true
42+
image-prefix:
43+
description: 'Docker image prefix'
44+
required: true
45+
image-tag-type:
46+
description: 'Image tag type (development-latest or semver)'
47+
required: true
48+
deploy-script:
49+
description: 'Deploy script name (deploy-dev.sh or deploy-prod.sh)'
50+
required: true
51+
default: 'deploy-dev.sh'
52+
53+
runs:
54+
using: 'composite'
55+
steps:
56+
- name: Inject application-secret.properties from Secrets
57+
shell: bash
58+
run: |
59+
mkdir -p ./secret
60+
echo "$SECRET_CONTENT" > ./secret/application-${{ inputs.environment }}-secret.properties
61+
echo "$APPLE_KEY_CONTENT" > ./secret/AuthKey.p8
62+
chmod 600 ./secret/*
63+
env:
64+
SECRET_CONTENT: ${{ inputs.secret-properties }}
65+
APPLE_KEY_CONTENT: ${{ inputs.apple-auth-key }}
66+
67+
- name: Set up Docker Buildx
68+
uses: docker/setup-buildx-action@v3
69+
70+
- name: Log in to Docker Hub
71+
uses: docker/login-action@v3
72+
with:
73+
username: ${{ inputs.dockerhub-username }}
74+
password: ${{ inputs.dockerhub-token }}
75+
76+
- name: Extract metadata for Docker
77+
id: meta
78+
uses: docker/metadata-action@v5
79+
with:
80+
images: docker.io/${{ inputs.image-prefix }}-${{ inputs.module }}
81+
tags: ${{ inputs.image-tag-type }}
82+
83+
- name: Build and push Docker image
84+
id: build-and-push
85+
uses: docker/build-push-action@v6
86+
with:
87+
context: .
88+
file: ./Dockerfile
89+
platforms: linux/amd64
90+
push: true
91+
tags: ${{ steps.meta.outputs.tags }}
92+
cache-from: type=gha,scope=${{ inputs.module }}
93+
cache-to: type=gha,mode=max,scope=${{ inputs.module }}
94+
build-args: |
95+
MODULE=${{ inputs.module }}
96+
97+
- name: Deploy to Server
98+
uses: appleboy/[email protected]
99+
with:
100+
host: ${{ inputs.host }}
101+
username: ${{ inputs.username }}
102+
key: ${{ inputs.ssh-key }}
103+
port: ${{ inputs.ssh-port }}
104+
script: |
105+
export DOCKERHUB_USERNAME="${{ inputs.dockerhub-username }}"
106+
export DOCKERHUB_TOKEN="${{ inputs.dockerhub-token }}"
107+
export MODULE="${{ inputs.module }}"
108+
export SPRING_PROFILE="${{ inputs.environment }}"
109+
export IMAGE_TAG="$(echo "${{ steps.meta.outputs.tags }}" | head -n1)"
110+
cd ~/deploy
111+
chmod +x ./${{ inputs.deploy-script }}
112+
./${{ inputs.deploy-script }}
113+
114+
- name: Send Discord notification on success (Development)
115+
uses: tsickert/discord-webhook@b217a69502f52803de774ded2b1ab7c282e99645
116+
if: success() && inputs.environment == 'dev'
117+
continue-on-error: true
118+
shell: bash
119+
with:
120+
webhook-url: ${{ inputs.discord-webhook-url }}
121+
embed-title: "✅ [${{ github.repository }}] Development Deploy Succeeded - ${{ inputs.module }}"
122+
embed-description: |
123+
**Module**: `${{ inputs.module }}`
124+
**Commit**: `${{ github.sha }}`
125+
**Author**: `${{ github.actor }}`
126+
**Message**: `${{ github.event.head_commit.message }}`
127+
[View Committed Changes](https://github.com/${{ github.repository }}/commit/${{ github.sha }})
128+
embed-color: 65280
129+
130+
- name: Send Discord notification on success (Production)
131+
uses: tsickert/discord-webhook@b217a69502f52803de774ded2b1ab7c282e99645
132+
if: success() && inputs.environment == 'prod'
133+
continue-on-error: true
134+
shell: bash
135+
with:
136+
webhook-url: ${{ inputs.discord-webhook-url }}
137+
content: "🚀 **Production Deploy Succeeded!**"
138+
embed-title: "✅ [${{ github.repository }}] Production Deploy Succeeded - ${{ inputs.module }}"
139+
embed-description: |
140+
**Module**: `${{ inputs.module }}`
141+
**Deployed by**: `${{ github.actor }}`
142+
The new version has been successfully deployed to production.
143+
[View Workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
144+
embed-color: 65280
145+
146+
- name: Send Discord notification on failure (Development)
147+
uses: tsickert/discord-webhook@b217a69502f52803de774ded2b1ab7c282e99645
148+
if: failure() && inputs.environment == 'dev'
149+
continue-on-error: true
150+
shell: bash
151+
with:
152+
webhook-url: ${{ inputs.discord-webhook-url }}
153+
embed-title: "❌ [${{ github.repository }}] Development Deploy Failed - ${{ inputs.module }}"
154+
embed-description: |
155+
**Module**: `${{ inputs.module }}`
156+
**Commit**: `${{ github.sha }}`
157+
**Author**: `${{ github.actor }}`
158+
An error occurred during the workflow execution.
159+
[View Failed Workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
160+
embed-color: 16711680
161+
162+
- name: Send Discord notification on failure (Production)
163+
uses: tsickert/discord-webhook@b217a69502f52803de774ded2b1ab7c282e99645
164+
if: failure() && inputs.environment == 'prod'
165+
continue-on-error: true
166+
shell: bash
167+
with:
168+
webhook-url: ${{ inputs.discord-webhook-url }}
169+
content: "🚨 **Production Deploy Failed!**"
170+
embed-title: "❌ [${{ github.repository }}] Production Deploy Failed - ${{ inputs.module }}"
171+
embed-description: |
172+
**Module**: `${{ inputs.module }}`
173+
**Deployed by**: `${{ github.actor }}`
174+
An error occurred during the production deployment workflow.
175+
[View Failed Workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
176+
embed-color: 16711680

.github/workflows/ci-pr.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,8 @@ jobs:
5858
env:
5959
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
6060
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
61-
run: ./gradlew fullCheck --parallel --build-cache --info --stacktrace
61+
run: |
62+
# fullCheck: 모든 모듈 (apis, admin, batch, gateway 등)의 빌드, 테스트, 정적분석 수행
63+
# --parallel: 모듈별 병렬 빌드로 시간 단축
64+
# --build-cache: Gradle 빌드 캐시 사용
65+
./gradlew fullCheck --parallel --build-cache --info --stacktrace

.github/workflows/dev-ci-cd.yml

Lines changed: 72 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -11,96 +11,89 @@ concurrency:
1111

1212
env:
1313
REGISTRY: docker.io
14-
IMAGE_NAME: ninecraft0523/ninecraft-server
15-
MODULE: apis
14+
IMAGE_PREFIX: ninecraft0523/ninecraft
1615

1716
jobs:
18-
build-push-and-deploy:
17+
detect-changes:
1918
runs-on: ubuntu-24.04
20-
timeout-minutes: 20
21-
environment: development
22-
19+
outputs:
20+
apis: ${{ steps.filter.outputs.apis }}
21+
# admin: ${{ steps.filter.outputs.admin }} # TODO: Uncomment when admin module is ready
22+
batch: ${{ steps.filter.outputs.batch }}
23+
any: ${{ steps.filter.outputs.any }}
2324
steps:
2425
- name: Checkout code
2526
uses: actions/checkout@v4
2627

27-
- name: Inject application-secret.properties from Secrets
28-
run: |
29-
mkdir ./secret
30-
echo "${{ secrets.DEV_SECRET_PROPERTIES }}" > ./secret/application-dev-secret.properties
31-
echo "${{ secrets.APPLE_AUTH_KEY }}" > ./secret/AuthKey.p8
32-
chmod 600 ./secret/*
33-
34-
- name: Set up Docker Buildx
35-
uses: docker/setup-buildx-action@v3
36-
37-
- name: Log in to Docker Hub
38-
uses: docker/login-action@v3
28+
- name: Check changed files
29+
uses: dorny/paths-filter@v3
30+
id: filter
3931
with:
40-
username: ${{ secrets.DOCKERHUB_USERNAME }}
41-
password: ${{ secrets.DOCKERHUB_TOKEN }}
32+
filters: |
33+
apis:
34+
- 'apis/**'
35+
- 'domain/**'
36+
- 'infra/**'
37+
- 'global-utils/**'
38+
- 'observability/**'
39+
# admin: # TODO: Uncomment when admin module is ready
40+
# - 'admin/**'
41+
# - 'domain/**'
42+
# - 'infra/**'
43+
# - 'global-utils/**'
44+
# - 'observability/**'
45+
batch:
46+
- 'batch/**'
47+
- 'domain/**'
48+
- 'infra/**'
49+
- 'global-utils/**'
50+
- 'observability/**'
51+
any:
52+
- 'apis/**'
53+
# - 'admin/**' # TODO: Uncomment when admin module is ready
54+
- 'batch/**'
55+
- 'domain/**'
56+
- 'infra/**'
57+
- 'global-utils/**'
58+
- 'observability/**'
4259
43-
- name: Extract metadata for Docker
44-
id: meta
45-
uses: docker/metadata-action@v5
46-
with:
47-
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
48-
tags: |
49-
type=raw,value=development-latest
60+
build-push-and-deploy:
61+
needs: detect-changes
62+
if: needs.detect-changes.outputs.any == 'true'
63+
runs-on: ubuntu-24.04
64+
timeout-minutes: 20
65+
environment: development
66+
strategy:
67+
fail-fast: false
68+
matrix:
69+
include:
70+
- module: apis
71+
changed: ${{ needs.detect-changes.outputs.apis }}
72+
# - module: admin # TODO: Uncomment when admin module is ready
73+
# changed: ${{ needs.detect-changes.outputs.admin }}
74+
- module: batch
75+
changed: ${{ needs.detect-changes.outputs.batch }}
5076

51-
- name: Build and push Docker image
52-
id: build-and-push
53-
uses: docker/build-push-action@v6
54-
with:
55-
context: .
56-
file: ./Dockerfile
57-
platforms: linux/amd64
58-
push: true
59-
tags: ${{ steps.meta.outputs.tags }}
60-
cache-from: type=gha
61-
cache-to: type=gha,mode=max
62-
build-args: |
63-
MODULE=${{ env.MODULE }}
77+
steps:
78+
- name: Checkout code
79+
if: matrix.changed == 'true'
80+
uses: actions/checkout@v4
6481

65-
- name: Deploy to Development Server
66-
uses: appleboy/[email protected]
82+
- name: Deploy module
83+
if: matrix.changed == 'true'
84+
uses: ./.github/actions/deploy-module
6785
with:
86+
environment: dev
87+
module: ${{ matrix.module }}
88+
dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }}
89+
dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }}
90+
secret-properties: ${{ secrets.DEV_SECRET_PROPERTIES }}
91+
apple-auth-key: ${{ secrets.APPLE_AUTH_KEY }}
6892
host: ${{ secrets.DEV_HOST }}
6993
username: ${{ secrets.DEV_USERNAME }}
70-
key: ${{ secrets.DEV_SSH_KEY }}
71-
port: ${{ secrets.DEV_PORT }}
72-
script: |
73-
export DOCKERHUB_USERNAME="${{ secrets.DOCKERHUB_USERNAME }}"
74-
export DOCKERHUB_TOKEN="${{ secrets.DOCKERHUB_TOKEN }}"
75-
export IMAGE_TAG="$(echo "${{ steps.meta.outputs.tags }}" | head -n1)"
76-
cd ~/deploy
77-
chmod +x ./deploy.sh
78-
./deploy.sh
79-
80-
- name: Send Discord notification on success
81-
uses: tsickert/discord-webhook@b217a69502f52803de774ded2b1ab7c282e99645
82-
if: success()
83-
continue-on-error: true
84-
with:
85-
webhook-url: ${{ secrets.DEV_DEPLOY_DISCORD_WEBHOOK_URL }}
86-
embed-title: "✅ [${{ github.repository }}] Development Deploy Succeeded"
87-
embed-description: |
88-
**Commit**: `${{ github.sha }}`
89-
**Author**: `${{ github.actor }}`
90-
**Message**: `${{ github.event.head_commit.message }}`
91-
[View Committed Changes](https://github.com/${{ github.repository }}/commit/${{ github.sha }})
92-
embed-color: 65280
93-
94-
- name: Send Discord notification on failure
95-
uses: tsickert/discord-webhook@b217a69502f52803de774ded2b1ab7c282e99645
96-
if: failure()
97-
continue-on-error: true
98-
with:
99-
webhook-url: ${{ secrets.DEV_DEPLOY_DISCORD_WEBHOOK_URL }}
100-
embed-title: "❌ [${{ github.repository }}] Development Deploy Failed"
101-
embed-description: |
102-
**Commit**: `${{ github.sha }}`
103-
**Author**: `${{ github.actor }}`
104-
An error occurred during the workflow execution.
105-
[View Failed Workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
106-
embed-color: 16711680
94+
ssh-key: ${{ secrets.DEV_SSH_KEY }}
95+
ssh-port: ${{ secrets.DEV_PORT }}
96+
discord-webhook-url: ${{ secrets.DEV_DEPLOY_DISCORD_WEBHOOK_URL }}
97+
image-prefix: ${{ env.IMAGE_PREFIX }}
98+
image-tag-type: type=raw,value=development-latest
99+
deploy-script: deploy-dev.sh

0 commit comments

Comments
 (0)