Skip to content

Commit d06f529

Browse files
committed
feat(CICD) : CICD 설정
1 parent 6e306cc commit d06f529

6 files changed

Lines changed: 165 additions & 8 deletions

File tree

.dockerignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.git
2+
.github
3+
.idea
4+
*.iml
5+
.gradle
6+
build
7+
out
8+
*.log
9+
.env
10+
.env.*
11+
!.env.example
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# -----------------------------------------------------------------------------
2+
# EC2 배포는 아래 워크플로만 사용합니다 (별도 shell 스크립트 없음).
3+
#
4+
# GitHub → Settings → Secrets and variables → Actions → Repository secrets
5+
#
6+
# Docker Hub
7+
# DOCKERHUB_USERNAME
8+
# DOCKERHUB_TOKEN
9+
#
10+
# EC2 SSH
11+
# EC2_HOST
12+
# EC2_USER
13+
# EC2_SSH_KEY
14+
# EC2_SSH_PASSPHRASE (선택)
15+
#
16+
# 앱 (application.yml / GitHub Secrets)
17+
# SPRING_DATASOURCE_URL
18+
# SPRING_DATASOURCE_USERNAME
19+
# SPRING_DATASOURCE_PASSWORD
20+
# GOOGLE_CLIENT_ID
21+
# JWT_SECRET_BASE64
22+
#
23+
# EC2 준비: Docker 설치, 보안 그룹 SSH(22)·앱(9090) 허용
24+
#
25+
# Deploy 단계 (SSH 원격에서 실행):
26+
# 1) docker pull <이미지:커밋SHA>
27+
# 2) 기존 컨테이너 stop/rm
28+
# 3) docker run (아래 Secrets 값을 컨테이너에 전달 — EC2 에 .env 파일 없음)
29+
# 4) docker image prune -f
30+
# -----------------------------------------------------------------------------
31+
32+
name: Build, push Docker Hub, deploy EC2
33+
34+
on:
35+
push:
36+
branches: [main]
37+
workflow_dispatch:
38+
39+
jobs:
40+
build-push-deploy:
41+
runs-on: ubuntu-latest
42+
permissions:
43+
contents: read
44+
45+
steps:
46+
- name: Checkout
47+
uses: actions/checkout@v4
48+
49+
- name: Set up Docker Buildx
50+
uses: docker/setup-buildx-action@v3
51+
52+
- name: Login to Docker Hub
53+
uses: docker/login-action@v3
54+
with:
55+
username: ${{ secrets.DOCKERHUB_USERNAME }}
56+
password: ${{ secrets.DOCKERHUB_TOKEN }}
57+
58+
- name: Build and push
59+
uses: docker/build-push-action@v6
60+
with:
61+
context: .
62+
push: true
63+
tags: |
64+
${{ secrets.DOCKERHUB_USERNAME }}/your_voice_back:latest
65+
${{ secrets.DOCKERHUB_USERNAME }}/your_voice_back:${{ github.sha }}
66+
cache-from: type=gha
67+
cache-to: type=gha,mode=max
68+
69+
- name: Deploy to EC2
70+
uses: appleboy/ssh-action@v1.2.0
71+
with:
72+
host: ${{ secrets.EC2_HOST }}
73+
username: ${{ secrets.EC2_USER }}
74+
key: ${{ secrets.EC2_SSH_KEY }}
75+
passphrase: ${{ secrets.EC2_SSH_PASSPHRASE }}
76+
port: 22
77+
script_stop: true
78+
script: |
79+
set -e
80+
IMAGE="${{ secrets.DOCKERHUB_USERNAME }}/your_voice_back:${{ github.sha }}"
81+
NAME="your-voice-back"
82+
docker pull "$IMAGE"
83+
docker stop "$NAME" 2>/dev/null || true
84+
docker rm "$NAME" 2>/dev/null || true
85+
docker run -d \
86+
--name "$NAME" \
87+
--restart unless-stopped \
88+
-p 9090:9090 \
89+
-e SPRING_DATASOURCE_URL="${{ secrets.SPRING_DATASOURCE_URL }}" \
90+
-e SPRING_DATASOURCE_USERNAME="${{ secrets.SPRING_DATASOURCE_USERNAME }}" \
91+
-e SPRING_DATASOURCE_PASSWORD="${{ secrets.SPRING_DATASOURCE_PASSWORD }}" \
92+
-e GOOGLE_CLIENT_ID="${{ secrets.GOOGLE_CLIENT_ID }}" \
93+
-e JWT_SECRET_BASE64="${{ secrets.JWT_SECRET_BASE64 }}" \
94+
"$IMAGE"
95+
docker image prune -f

Dockerfile

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Build
2+
FROM eclipse-temurin:21-jdk-alpine AS build
3+
WORKDIR /app
4+
5+
COPY gradle gradle
6+
COPY gradlew build.gradle settings.gradle ./
7+
RUN chmod +x ./gradlew
8+
COPY src src
9+
RUN ./gradlew bootJar --no-daemon -x test -Pdeploy
10+
11+
# Run
12+
FROM eclipse-temurin:21-jre-alpine
13+
WORKDIR /app
14+
15+
RUN addgroup -S spring && adduser -S spring -G spring
16+
17+
# bootJar는 *-plain.jar 와 실행용 JAR를 함께 만듈 수 있음
18+
COPY --from=build /app/build/libs/ /app/libs-tmp/
19+
RUN f="$(find /app/libs-tmp -name '*.jar' ! -name '*-plain.jar' | head -n1)" && \
20+
test -n "$f" && mv "$f" /app/app.jar && rm -rf /app/libs-tmp && \
21+
chown spring:spring /app/app.jar
22+
23+
USER spring:spring
24+
25+
EXPOSE 9090
26+
ENTRYPOINT ["java", "-jar", "/app/app.jar"]

build.gradle

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ dependencies {
3737
testAnnotationProcessor 'org.projectlombok:lombok'
3838
}
3939

40+
tasks.named('processResources') {
41+
// Docker/배포 JAR: application-local.yml 제외 → application.yml 만 런타임 설정으로 포함
42+
if (project.hasProperty('deploy')) {
43+
exclude '**/application-local.yml'
44+
}
45+
}
46+
4047
tasks.named('test') {
4148
useJUnitPlatform()
4249
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# 로컬 전용 (Gradle -Pdeploy / Docker 빌드 시 JAR 에 포함되지 않음)
2+
3+
spring:
4+
datasource:
5+
url: jdbc:mysql://localhost:3306/yourvoice?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Seoul&characterEncoding=UTF-8
6+
username: root
7+
password: root
8+
driver-class-name: com.mysql.cj.jdbc.Driver
9+
jpa:
10+
hibernate:
11+
ddl-auto: update
12+
properties:
13+
hibernate:
14+
format_sql: true
15+
show-sql: true

src/main/resources/application.yml

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1+
# 배포(JAR/Docker) 시에는 application-local.yml 을 넣지 않으므로 이 파일만 적용됩니다.
2+
# 로컬: spring.profiles.default=local + application-local.yml 이 아래 설정을 덮어씁니다.
3+
14
spring:
5+
profiles:
6+
default: local
7+
28
datasource:
3-
url: jdbc:mysql://localhost:3306/yourvoice?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Seoul&characterEncoding=UTF-8
4-
username: root
5-
password: root
9+
url: ${SPRING_DATASOURCE_URL}
10+
username: ${SPRING_DATASOURCE_USERNAME}
11+
password: ${SPRING_DATASOURCE_PASSWORD}
612
driver-class-name: com.mysql.cj.jdbc.Driver
13+
714
jpa:
815
hibernate:
916
ddl-auto: update
10-
properties:
11-
hibernate:
12-
format_sql: true
13-
show-sql: true
17+
show-sql: false
1418

1519
server:
1620
port: 9090
@@ -25,7 +29,6 @@ auth:
2529
google:
2630
client-id: ${GOOGLE_CLIENT_ID:498950596222-tncs8b6ce662tiuga816ai9pda03e3fr.apps.googleusercontent.com}
2731
jwt:
28-
# Base64 encoded secret. Change in production.
2932
secret: ${JWT_SECRET_BASE64:ZGV2LXNlY3JldC1rZXktZm9yLWp3dC1zaG91bGQtYmUtbG9uZy1lbm91Z2gtMTIzNDU2}
3033
access-expiration-seconds: 1800
3134
refresh-expiration-seconds: 1209600

0 commit comments

Comments
 (0)