1. Github Secrets ์ค์

Github Actions๋ฅผ ์ ์ฉํ Repo์ Setting์์ Secrets and variables์ ๋ค์ด๊ฐ์ฃผ๋ฉด
New repository secret๋ก ํ๊ฒฝ ๋ณ์๋ฅผ ์ค์ ํ ์ ์์
- DOCKERHUB_USERNAME
=> Docker Hub ์ฌ์ฉ์ ์ด๋ฆ - DOCKERHUB_PASSWORD
=> Docker Hub ๋น๋ฐ๋ฒํธ - EC2_HOST
=> EC2 ํผ๋ธ๋ฆญ IP - EC2_USER
=> EC2์ SSH๋ก ์ ์ํ ๋ ์ฌ์ฉํ ์ฌ์ฉ์ ์ด๋ฆ์
(์ฐ๋ถํฌ EC2๋ฅผ ์ฌ์ฉ์ค์ด๊ธฐ์ ubuntu๋ก ์ง์ ํ์์) - EC2_SSH_KEY
https://hanjungyo.tistory.com/127
Jenkins์ ์ด์ฉํ Spring Boot ๋ฐฐํฌ ์๋ํ
๋ก์ปฌ์์ Spring Boot ๋น๋ํ๊ณ docker image ๋ง๋ค์ด์ docker hub์ ์ฌ๋ฆฌ๊ณ ..EC2 ๋ค์ด๊ฐ์ docker hub์ ์๋ ์ด๋ฏธ์ง pull ๋ฐ์์ run ์ํค๊ธฐ... ๊ฐ๋ฐ์ ํ๋ฉด ํ ์๋ก CI/CD์ ์ค์์ฑ์ด ๊น๊ฒ ๋๊ปด์ง๋ ๊ฒ ๊ฐ๋ค...
hanjungyo.tistory.com
EC2_SSH_KEY์ ๋ํด์๋ ์ ํฌ์คํ
์ SSHํค ์ค์ ๋ถ๋ถ์ ์ฐธ๊ณ ํ๋ฉด ์ ์ ์์
์ฃผ์ํด์ผํ ๋ถ๋ถ์
1. ์ ๊ทผํ๋์ชฝ์ธ GitHub์ ๊ณต๊ฐํค๊ฐ ์๋ ๋น๋ฐํค๋ฅผ ๋ฃ์ด์ค์ผํ๋ค๋์
2. EC2 ์ธ์คํด์ค์ authorized_keys์๋ GitHub Actions์์ ์ฌ์ฉ๋๋ ๋น๋ฐํค์ ํด๋นํ๋ ๊ณต๊ฐํค (id_rsa.pub) ๊ฐ ๋ฑ๋ก๋์ด์ผ SSH ์ฐ๊ฒฐ์ด ๊ฐ๋ฅํ๋ค๋์
- APPLICATION
application.yml ํ์ผ๋ ์ค์ ์ ํด์ผํ๋๋ฐ (๋ณด์์์ ์ด์๋ก gitignore ์ฒ๋ฆฌ ๋์ด ์์)
์ฝ๋๋ฅผ ๊ทธ๋ฅ ๋ฃ์ด๋ ๋์ง๋ง ๊ณต๋ฐฑ์ด๋ ํน์๋ฌธ์ ๋ฑ์์ ์ค๋ฅ๊ฐ ๋ ์ ์๋ค๊ณ ํด์ Base64๋ก ์ธ์ฝ๋ฉํ ๊ฐ์ APPLICATION์ผ๋ก ์ ์ฅํ์์
https://www.convertstring.com/ko/EncodeDecode/Base64Encode
Base64๋ก ์ธ์ฝ๋ฉ - ์จ๋ผ์ธ Base64๋ก ์ธ์ฝ๋
www.convertstring.com
์ ์ฌ์ดํธ์์ application.yaml ํ์ผ์ ๋ฃ๊ณ Base64๋ก ์ธ์ฝ๋ฉํ ์ ์์
2. CI ํ ์คํธ (Java with Gradle)

CI / CD๋ฅผ ์ ์ฉํ Github Repo์ ์๋จ๋ฐ์์ Actions๋ฅผ ํด๋ฆญํ๊ณ Java with Gradle์ Configureํด์ผํจ
name: Java CI with Gradle
on:
push:
branches: [ "backend/main", "backend/feature/cd-setup" ]
pull_request:
branches: [ "backend/main", "backend/feature/cd-setup" ]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
- name: Grant execute permission for gradlew
run: chmod +x backend/gradlew
- name: Create application.yml from GitHub Secrets
run: |
mkdir -p backend/src/main/resources
echo "${{ secrets.APPLICATION }}" | base64 --decode > backend/src/main/resources/application.yml
- name: Build with Gradle Wrapper
working-directory: backend
run: ./gradlew build
ํ์์๋ ๋ถ๋ถ๋ ์๊ธฐ์ ์์ ๊ฐ์ด ์์ ํด์คฌ์
์ฝ๋๋ฅผ ์ดํด๋ณด๋ฉด
- Java CI with Gradle ์ด๋ผ๋ ์ด๋ฆ์ GitHub Actions ์ํฌํ๋ก์ฐ๋ฅผ ๋ง๋ฌ
- backend/main ๊ณผ backend/feature/cd-setup ๋ก ์์ํ๋ ๋ธ๋์น์ push ๋๋ pull request ๊ฐ ๋ฐ์ํ๋ฉด ์ด ์ํฌํ๋ก์ฐ๊ฐ ์คํ๋๋๋ก ํธ๋ฆฌ๊ฑฐ ์กฐ๊ฑด์ ๊ฑธ์์
=> workflow_dispatch๋ฅผ ํตํด ์ด workflow ํ์ผ์ ๊ฐ์ง ๋ธ๋์น์์ ํธ๋ฆฌ๊ฑฐ ์ธ์ ์๋์ผ๋ก ์์ ์ ์ํํ ์ ์์

workflow_dispatch ์ค์ ์ Actions์ ๋ค์ด๊ฐ์ ์ข์ธก์ ์คํํ workflow ์ด๋ฆ์ ์ ํํ๊ฒ๋๋ฉด ์ค๋ฅธ์ชฝ ๋นจ๊ฐ ๋ฐ์ค์ ์๋ Run workflow๋ฅผ ๋ณผ ์ ์์
์ด ๋ ์คํํ branch๋ฅผ ์ ํํ๊ฒ ๋๋ฉด ์๋์ผ๋ก workflow ์ํ์ด ๊ฐ๋ฅํ๋ฐ ์ด๋ฅผ ์ํด์ ํด๋น branch์ .github/workflows์ ํด๋น workflow ๊ด๋ จ yml ํ์ผ์ด ์กด์ฌํด์ผํจ
- build๋ผ๋ job์ ์ ์ํ๊ณ ์คํ ํ๊ฒฝ์ ubuntu ์ต์ ํ๊ฒฝ์ผ๋ก ์ง์ ํ์์
(์ ์ฅ์์ ๋ด์ฉ์ ์ฝ์ ์ ์๋๋ก ์ต์ํ์ ๊ถํ์ ์ค์ )
- ํ์ฌ ์ ์ฅ์์ ์ฝ๋๋ฅผ checkoutํ์ฌ runner ํ๊ฒฝ์ ๋ค์ด๋ก๋
- JDK 17์ ์ค์น (Adoptium์์ ์ ๊ณตํ๋ OpenJDK ๋ฐฐํฌํ์ธ temurin)
- ๊ณต์ Gradle GitHub Action์ ์ฌ์ฉํ์ฌ Gradle์ ์ค์
- gradlew(Gradle Wrapper) ํ์ผ์ด ์คํ๋ ์ ์๋๋ก ๊ถํ ๋ถ์ฌ
=> ํ์ฌ github repo์ ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ๊ฐ fornt ์ backend ํด๋๊ฐ ํจ๊ป ์กด์ฌํ๊ธฐ์ gradlew์ ์์น๋ฅผ backend/gradelw๋ก ํด์ค์ผ backend ํด๋ ์์ ์๋ gradlew๋ฅผ ์ ๋๋ก ์ฐพ์ ์ ์์ - github secrets์ ์ ์ฅํ base64 ์ธ์ฝ๋ฉ๋ application.yml ํ์ผ์ (๋ณ์๋ช APPLICATION) ๋์ฝ๋ฉ ํ์ฌ ์ง์ ํด๋ ์์น์ ์ ์ฅ
- Gradle์ ์ฌ์ฉํ์ฌ ํ๋ก์ ํธ ๋น๋
=> working directory๋ฅผ backend๋ก ์ค์ ํ์์
(์ด์ ์ ๋งํ๋๋ก ํ์ฌ ๋ฃจํธ์๋ front ์ backend ๋๊ฐ์ ๋๋ ํ ๋ฆฌ๊ฐ ๋ชจ๋ ์กด์ฌํ๊ธฐ์)
- ํ์ฌ ์ ์ฅ์์ ์ฝ๋๋ฅผ checkoutํ์ฌ runner ํ๊ฒฝ์ ๋ค์ด๋ก๋

ํ๋ก์ ํธ ํด๋ ๊ตฌ์กฐ๊ฐ ์์ ๊ฐ์ผ๋ฏ๋ก ๋น๋ํ ๋ working-directory๋ฅผ backend๋ก ์ง์ ํด์ฃผ์ด์ผํจ
์์ ๊ฐ์ CI ์ํฌํ๋ก์ฐ๋ฅผ ํตํด PR์ด๋ ์ฝ๋ ๋ณ๊ฒฝ ์ ์๋์ผ๋ก ๋น๋ ์ค๋ฅ๋ฅผ ํ์ธํ ์ ์์

์์ ๊ฐ์ด ์ฑ๊ณตํ๋ ๊ฒ์ ํ์ธํ ์ ์์
์ฃผ์์ฌํญ
.github/worflows/gradle.yml ์ ์์์ ์์ฑํ yml ํ์ผ์ด ๋ค์ด๊ฐ๊ฒ๋ ํ ๋ฐ
์ด๋ฏธ ์กด์ฌํ๋ ํน์ branch๋ฅผ ํธ๋ฆฌ๊ฑฐํด๋จ๋๋ผ๋ ํด๋น ๋ธ๋ ์น์ gradle.yml ํ์ผ ์ฆ, Github Action ํ์ผ์ด ์๋ค๋ฉด ์คํ๋์ง ์์
๋ฐ๋ผ์, master ๋ธ๋์น์ yml ํ์ผ์ ๋ง๋ค๊ณ ํธ๋ฆฌ๊ฑฐ ์ค์ ์ ํด๋์ ๋ธ๋์น์ merge ํ๊ฑฐ๋ ์ฒ์๋ถํฐ Action์ ์ฌ์ฉํ ๋ธ๋์น์ gradle.yml์ ์์ฑํ๋ฉด ๋จ
3. CD ์์ฑ
name: Java CI/CD with Gradle & Docker
on:
push:
branches: [ "backend/main", "backend/feature/cd-setup" ]
pull_request:
branches: [ "backend/main", "backend/feature/cd-setup" ]
workflow_dispatch:
jobs:
build-docker-image:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
# GitHub ์ ์ฅ์์์ ์ฝ๋๋ฅผ ๊ฐ์ ธ์ด
- uses: actions/checkout@v4
# JDK 17 ์ค์
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
# Gradle ์ค์
- name: Setup Gradle
uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
# Gradle wrapper ์คํ ๊ถํ ๋ถ์ฌ
- name: Grant execute permission for gradlew
run: chmod +x backend/gradlew
# GitHub Secrets์์ application.yml ํ์ผ ์์ฑ
- name: Create application.yml from GitHub Secrets
run: |
mkdir -p backend/src/main/resources
echo "${{ secrets.APPLICATION }}" | base64 --decode > backend/src/main/resources/application.yml
# Gradle ๋น๋ ์คํ
- name: Build with Gradle Wrapper
working-directory: backend
run: ./gradlew build
# Docker ์ด๋ฏธ์ง ๋น๋
- name: Build Docker Image
working-directory: backend
run: |
docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/2025capstone:latest .
# Docker Hub ๋ก๊ทธ์ธ
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
# Docker Hub์ ์ด๋ฏธ์ง ํธ์
- name: Push Docker Image to Docker Hub
run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/2025capstone:latest
run-docker-image-on-ec2:
needs: build-docker-image
runs-on: ubuntu-latest
steps:
# Known Hosts ์
๋ฐ์ดํธ: EC2์ SSH ์ ์ํ๊ธฐ ์ํด known_hosts ํ์ผ์ EC2 ํธ์คํธ ์ถ๊ฐ
- name: Update Known Hosts
run: |
mkdir -p ~/.ssh
ssh-keyscan -H ${{ secrets.EC2_HOST }} >> ~/.ssh/known_hosts
# EC2์ Docker ์ด๋ฏธ์ง ๋ฐฐํฌ
- name: Deploy to EC2
uses: appleboy/ssh-action@v0.1.6
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_SSH_KEY }}
script: |
# ๊ธฐ์กด ์ปจํ
์ด๋๊ฐ ์์ผ๋ฉด ์ค์งํ๊ณ ์ญ์
sudo docker stop $(sudo docker ps -q --filter ancestor=${{ secrets.DOCKERHUB_USERNAME }}/2025capstone)
sudo docker rm $(sudo docker ps -aq --filter ancestor=${{ secrets.DOCKERHUB_USERNAME }}/2025capstone)
# ์ต์ Docker ์ด๋ฏธ์ง ๊ฐ์ ธ์ค๊ธฐ
sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/2025capstone:latest
# ์ ์ปจํ
์ด๋ ์คํ
sudo docker run --rm -it -d -p 8080:8080 --name 2025capstone ${{ secrets.DOCKERHUB_USERNAME }}/2025capstone:latest
# ์ฌ์ฉํ์ง ์๋ Docker ์ด๋ฏธ์ง ๋ฐ ์บ์ ์ ๋ฆฌ
sudo docker system prune -f
๊ธฐ์กด์ CI ํ ์คํธ ์ฝ๋์์ ๋ณ๊ฒฝ๋ ๋ถ๋ถ์ ์ดํด๋ณด๋ฉด
- ./gradlew build ๋ช
๋ น์ด๋ก Gradle์ ์ฌ์ฉํด ์ดํ๋ฆฌ์ผ์ด์
์ ๋น๋ํ ํ docker build ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํ์ฌ backend ๋๋ ํ ๋ฆฌ์์ Docker ์ด๋ฏธ์ง๋ฅผ ๋น๋ํ๊ณ USERNAME ๊ฐ์ ๋ฐ๋ผ 2025capstone:latest ๋ก ํ๊น
ํจ
=> USERNAME/2025capstone:latest ์ด๋ฏธ์ง ์์ฑ - docekr/login-action@v2 ๋ฅผ ์ฌ์ฉํด Docker Hub์ ๋ก๊ทธ์ธํจ
=> github secrets์ ์ง์ ํด๋ ์ ์ ์์ด๋์ ๋น๋ฐ๋ฒํธ ์ฌ์ฉ - ๋น๋ํ Docker ์ด๋ฏธ์ง๋ฅผ docker push ๋ช
๋ น์ด๋ก Docker Hub์ ํธ์ํจ
- run-docker-image-oon-ec2 ์์
- ssh-keyscan์ ์ฌ์ฉํด EC2 ํธ์คํธ์ SSH ๊ณต๊ฐ ํค๋ฅผ ~/.ssh/known_hosts์ ์ถ๊ฐํจ
(์์ ์ฒจ๋ถํ ssh ์ค์ ํฌ์คํ ์ฐธ๊ณ ) - appleboy/ssh-action@v0.1.6์ ์ฌ์ฉํด EC2์ SSH๋ก ์ฐ๊ฒฐํ๊ณ ์์
์คํ
- docker stop ๋ฐ docker rm ๋ช
๋ น์ด๋ก ์ด์ ์ ์คํ์ค์ธ ์ปจํ
์ด๋๋ฅผ ์ค์งํ๊ณ ์ญ์
- docker pull ๋ก Docker Hub์์ ์ต์ ์ด๋ฏธ์ง๋ฅผ ๊ฐ์ ธ์ด
- docker run ๋ช
๋ น์ด๋ก ์๋ก์ด ์ปจํ
์ด๋๋ฅผ 8080 ํฌํธ์ ๋ฐฐํฌํจ
- docker system prune์ ์ฌ์ฉํด ๋ถํ์ํ Docker ์ด๋ฏธ์ง์ ์บ์๋ฅผ ์ ๋ฆฌํจ
- docker stop ๋ฐ docker rm ๋ช
๋ น์ด๋ก ์ด์ ์ ์คํ์ค์ธ ์ปจํ
์ด๋๋ฅผ ์ค์งํ๊ณ ์ญ์
- ssh-keyscan์ ์ฌ์ฉํด EC2 ํธ์คํธ์ SSH ๊ณต๊ฐ ํค๋ฅผ ~/.ssh/known_hosts์ ์ถ๊ฐํจ
4. ํ ์คํธ

workflow์ ์ง์ ํด๋ backend/feature/cd-setup ๋ธ๋์น์ test.md ๋ฅผ push ํ์ ์ ์ฌ์ง๊ณผ ๊ฐ์ด ์ฒดํฌํ์๋ก CD ์์ ์ด ์ฑ๊ณต์ ์ผ๋ก ์ด๋ฃจ์ด์ง ๊ฒ์ ํ์ธํ ์ ์์

EC2์ ๋ค์ด๊ฐ sudo docker ps ๋ช ๋ น์ด๋ก ํ์ฌ ์คํ์ค์ธ ์ปจํ ์ด๋ ํ์ธ์ ์ ๋์ํ๋ ๊ฒ์ ํ์ธํ ์ ์์
'๐ Infra > CI ยท CD' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Jenkins์ ์ด์ฉํ Spring Boot ๋ฐฐํฌ ์๋ํ (0) | 2025.03.03 |
---|---|
Github Actions ํบ์๋ณด๊ธฐ (0) | 2025.02.26 |
GitHub Actions ์๊ฐ (0) | 2024.05.01 |
1. Github Secrets ์ค์

Github Actions๋ฅผ ์ ์ฉํ Repo์ Setting์์ Secrets and variables์ ๋ค์ด๊ฐ์ฃผ๋ฉด
New repository secret๋ก ํ๊ฒฝ ๋ณ์๋ฅผ ์ค์ ํ ์ ์์
- DOCKERHUB_USERNAME
=> Docker Hub ์ฌ์ฉ์ ์ด๋ฆ - DOCKERHUB_PASSWORD
=> Docker Hub ๋น๋ฐ๋ฒํธ - EC2_HOST
=> EC2 ํผ๋ธ๋ฆญ IP - EC2_USER
=> EC2์ SSH๋ก ์ ์ํ ๋ ์ฌ์ฉํ ์ฌ์ฉ์ ์ด๋ฆ์
(์ฐ๋ถํฌ EC2๋ฅผ ์ฌ์ฉ์ค์ด๊ธฐ์ ubuntu๋ก ์ง์ ํ์์) - EC2_SSH_KEY
https://hanjungyo.tistory.com/127
Jenkins์ ์ด์ฉํ Spring Boot ๋ฐฐํฌ ์๋ํ
๋ก์ปฌ์์ Spring Boot ๋น๋ํ๊ณ docker image ๋ง๋ค์ด์ docker hub์ ์ฌ๋ฆฌ๊ณ ..EC2 ๋ค์ด๊ฐ์ docker hub์ ์๋ ์ด๋ฏธ์ง pull ๋ฐ์์ run ์ํค๊ธฐ... ๊ฐ๋ฐ์ ํ๋ฉด ํ ์๋ก CI/CD์ ์ค์์ฑ์ด ๊น๊ฒ ๋๊ปด์ง๋ ๊ฒ ๊ฐ๋ค...
hanjungyo.tistory.com
EC2_SSH_KEY์ ๋ํด์๋ ์ ํฌ์คํ
์ SSHํค ์ค์ ๋ถ๋ถ์ ์ฐธ๊ณ ํ๋ฉด ์ ์ ์์
์ฃผ์ํด์ผํ ๋ถ๋ถ์
1. ์ ๊ทผํ๋์ชฝ์ธ GitHub์ ๊ณต๊ฐํค๊ฐ ์๋ ๋น๋ฐํค๋ฅผ ๋ฃ์ด์ค์ผํ๋ค๋์
2. EC2 ์ธ์คํด์ค์ authorized_keys์๋ GitHub Actions์์ ์ฌ์ฉ๋๋ ๋น๋ฐํค์ ํด๋นํ๋ ๊ณต๊ฐํค (id_rsa.pub) ๊ฐ ๋ฑ๋ก๋์ด์ผ SSH ์ฐ๊ฒฐ์ด ๊ฐ๋ฅํ๋ค๋์
- APPLICATION
application.yml ํ์ผ๋ ์ค์ ์ ํด์ผํ๋๋ฐ (๋ณด์์์ ์ด์๋ก gitignore ์ฒ๋ฆฌ ๋์ด ์์)
์ฝ๋๋ฅผ ๊ทธ๋ฅ ๋ฃ์ด๋ ๋์ง๋ง ๊ณต๋ฐฑ์ด๋ ํน์๋ฌธ์ ๋ฑ์์ ์ค๋ฅ๊ฐ ๋ ์ ์๋ค๊ณ ํด์ Base64๋ก ์ธ์ฝ๋ฉํ ๊ฐ์ APPLICATION์ผ๋ก ์ ์ฅํ์์
https://www.convertstring.com/ko/EncodeDecode/Base64Encode
Base64๋ก ์ธ์ฝ๋ฉ - ์จ๋ผ์ธ Base64๋ก ์ธ์ฝ๋
www.convertstring.com
์ ์ฌ์ดํธ์์ application.yaml ํ์ผ์ ๋ฃ๊ณ Base64๋ก ์ธ์ฝ๋ฉํ ์ ์์
2. CI ํ ์คํธ (Java with Gradle)

CI / CD๋ฅผ ์ ์ฉํ Github Repo์ ์๋จ๋ฐ์์ Actions๋ฅผ ํด๋ฆญํ๊ณ Java with Gradle์ Configureํด์ผํจ
name: Java CI with Gradle
on:
push:
branches: [ "backend/main", "backend/feature/cd-setup" ]
pull_request:
branches: [ "backend/main", "backend/feature/cd-setup" ]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Setup Gradle
uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
- name: Grant execute permission for gradlew
run: chmod +x backend/gradlew
- name: Create application.yml from GitHub Secrets
run: |
mkdir -p backend/src/main/resources
echo "${{ secrets.APPLICATION }}" | base64 --decode > backend/src/main/resources/application.yml
- name: Build with Gradle Wrapper
working-directory: backend
run: ./gradlew build
ํ์์๋ ๋ถ๋ถ๋ ์๊ธฐ์ ์์ ๊ฐ์ด ์์ ํด์คฌ์
์ฝ๋๋ฅผ ์ดํด๋ณด๋ฉด
- Java CI with Gradle ์ด๋ผ๋ ์ด๋ฆ์ GitHub Actions ์ํฌํ๋ก์ฐ๋ฅผ ๋ง๋ฌ
- backend/main ๊ณผ backend/feature/cd-setup ๋ก ์์ํ๋ ๋ธ๋์น์ push ๋๋ pull request ๊ฐ ๋ฐ์ํ๋ฉด ์ด ์ํฌํ๋ก์ฐ๊ฐ ์คํ๋๋๋ก ํธ๋ฆฌ๊ฑฐ ์กฐ๊ฑด์ ๊ฑธ์์
=> workflow_dispatch๋ฅผ ํตํด ์ด workflow ํ์ผ์ ๊ฐ์ง ๋ธ๋์น์์ ํธ๋ฆฌ๊ฑฐ ์ธ์ ์๋์ผ๋ก ์์ ์ ์ํํ ์ ์์

workflow_dispatch ์ค์ ์ Actions์ ๋ค์ด๊ฐ์ ์ข์ธก์ ์คํํ workflow ์ด๋ฆ์ ์ ํํ๊ฒ๋๋ฉด ์ค๋ฅธ์ชฝ ๋นจ๊ฐ ๋ฐ์ค์ ์๋ Run workflow๋ฅผ ๋ณผ ์ ์์
์ด ๋ ์คํํ branch๋ฅผ ์ ํํ๊ฒ ๋๋ฉด ์๋์ผ๋ก workflow ์ํ์ด ๊ฐ๋ฅํ๋ฐ ์ด๋ฅผ ์ํด์ ํด๋น branch์ .github/workflows์ ํด๋น workflow ๊ด๋ จ yml ํ์ผ์ด ์กด์ฌํด์ผํจ
- build๋ผ๋ job์ ์ ์ํ๊ณ ์คํ ํ๊ฒฝ์ ubuntu ์ต์ ํ๊ฒฝ์ผ๋ก ์ง์ ํ์์
(์ ์ฅ์์ ๋ด์ฉ์ ์ฝ์ ์ ์๋๋ก ์ต์ํ์ ๊ถํ์ ์ค์ )
- ํ์ฌ ์ ์ฅ์์ ์ฝ๋๋ฅผ checkoutํ์ฌ runner ํ๊ฒฝ์ ๋ค์ด๋ก๋
- JDK 17์ ์ค์น (Adoptium์์ ์ ๊ณตํ๋ OpenJDK ๋ฐฐํฌํ์ธ temurin)
- ๊ณต์ Gradle GitHub Action์ ์ฌ์ฉํ์ฌ Gradle์ ์ค์
- gradlew(Gradle Wrapper) ํ์ผ์ด ์คํ๋ ์ ์๋๋ก ๊ถํ ๋ถ์ฌ
=> ํ์ฌ github repo์ ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ๊ฐ fornt ์ backend ํด๋๊ฐ ํจ๊ป ์กด์ฌํ๊ธฐ์ gradlew์ ์์น๋ฅผ backend/gradelw๋ก ํด์ค์ผ backend ํด๋ ์์ ์๋ gradlew๋ฅผ ์ ๋๋ก ์ฐพ์ ์ ์์ - github secrets์ ์ ์ฅํ base64 ์ธ์ฝ๋ฉ๋ application.yml ํ์ผ์ (๋ณ์๋ช APPLICATION) ๋์ฝ๋ฉ ํ์ฌ ์ง์ ํด๋ ์์น์ ์ ์ฅ
- Gradle์ ์ฌ์ฉํ์ฌ ํ๋ก์ ํธ ๋น๋
=> working directory๋ฅผ backend๋ก ์ค์ ํ์์
(์ด์ ์ ๋งํ๋๋ก ํ์ฌ ๋ฃจํธ์๋ front ์ backend ๋๊ฐ์ ๋๋ ํ ๋ฆฌ๊ฐ ๋ชจ๋ ์กด์ฌํ๊ธฐ์)
- ํ์ฌ ์ ์ฅ์์ ์ฝ๋๋ฅผ checkoutํ์ฌ runner ํ๊ฒฝ์ ๋ค์ด๋ก๋

ํ๋ก์ ํธ ํด๋ ๊ตฌ์กฐ๊ฐ ์์ ๊ฐ์ผ๋ฏ๋ก ๋น๋ํ ๋ working-directory๋ฅผ backend๋ก ์ง์ ํด์ฃผ์ด์ผํจ
์์ ๊ฐ์ CI ์ํฌํ๋ก์ฐ๋ฅผ ํตํด PR์ด๋ ์ฝ๋ ๋ณ๊ฒฝ ์ ์๋์ผ๋ก ๋น๋ ์ค๋ฅ๋ฅผ ํ์ธํ ์ ์์

์์ ๊ฐ์ด ์ฑ๊ณตํ๋ ๊ฒ์ ํ์ธํ ์ ์์
์ฃผ์์ฌํญ
.github/worflows/gradle.yml ์ ์์์ ์์ฑํ yml ํ์ผ์ด ๋ค์ด๊ฐ๊ฒ๋ ํ ๋ฐ
์ด๋ฏธ ์กด์ฌํ๋ ํน์ branch๋ฅผ ํธ๋ฆฌ๊ฑฐํด๋จ๋๋ผ๋ ํด๋น ๋ธ๋ ์น์ gradle.yml ํ์ผ ์ฆ, Github Action ํ์ผ์ด ์๋ค๋ฉด ์คํ๋์ง ์์
๋ฐ๋ผ์, master ๋ธ๋์น์ yml ํ์ผ์ ๋ง๋ค๊ณ ํธ๋ฆฌ๊ฑฐ ์ค์ ์ ํด๋์ ๋ธ๋์น์ merge ํ๊ฑฐ๋ ์ฒ์๋ถํฐ Action์ ์ฌ์ฉํ ๋ธ๋์น์ gradle.yml์ ์์ฑํ๋ฉด ๋จ
3. CD ์์ฑ
name: Java CI/CD with Gradle & Docker
on:
push:
branches: [ "backend/main", "backend/feature/cd-setup" ]
pull_request:
branches: [ "backend/main", "backend/feature/cd-setup" ]
workflow_dispatch:
jobs:
build-docker-image:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
# GitHub ์ ์ฅ์์์ ์ฝ๋๋ฅผ ๊ฐ์ ธ์ด
- uses: actions/checkout@v4
# JDK 17 ์ค์
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
# Gradle ์ค์
- name: Setup Gradle
uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
# Gradle wrapper ์คํ ๊ถํ ๋ถ์ฌ
- name: Grant execute permission for gradlew
run: chmod +x backend/gradlew
# GitHub Secrets์์ application.yml ํ์ผ ์์ฑ
- name: Create application.yml from GitHub Secrets
run: |
mkdir -p backend/src/main/resources
echo "${{ secrets.APPLICATION }}" | base64 --decode > backend/src/main/resources/application.yml
# Gradle ๋น๋ ์คํ
- name: Build with Gradle Wrapper
working-directory: backend
run: ./gradlew build
# Docker ์ด๋ฏธ์ง ๋น๋
- name: Build Docker Image
working-directory: backend
run: |
docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/2025capstone:latest .
# Docker Hub ๋ก๊ทธ์ธ
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
# Docker Hub์ ์ด๋ฏธ์ง ํธ์
- name: Push Docker Image to Docker Hub
run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/2025capstone:latest
run-docker-image-on-ec2:
needs: build-docker-image
runs-on: ubuntu-latest
steps:
# Known Hosts ์
๋ฐ์ดํธ: EC2์ SSH ์ ์ํ๊ธฐ ์ํด known_hosts ํ์ผ์ EC2 ํธ์คํธ ์ถ๊ฐ
- name: Update Known Hosts
run: |
mkdir -p ~/.ssh
ssh-keyscan -H ${{ secrets.EC2_HOST }} >> ~/.ssh/known_hosts
# EC2์ Docker ์ด๋ฏธ์ง ๋ฐฐํฌ
- name: Deploy to EC2
uses: appleboy/ssh-action@v0.1.6
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_SSH_KEY }}
script: |
# ๊ธฐ์กด ์ปจํ
์ด๋๊ฐ ์์ผ๋ฉด ์ค์งํ๊ณ ์ญ์
sudo docker stop $(sudo docker ps -q --filter ancestor=${{ secrets.DOCKERHUB_USERNAME }}/2025capstone)
sudo docker rm $(sudo docker ps -aq --filter ancestor=${{ secrets.DOCKERHUB_USERNAME }}/2025capstone)
# ์ต์ Docker ์ด๋ฏธ์ง ๊ฐ์ ธ์ค๊ธฐ
sudo docker pull ${{ secrets.DOCKERHUB_USERNAME }}/2025capstone:latest
# ์ ์ปจํ
์ด๋ ์คํ
sudo docker run --rm -it -d -p 8080:8080 --name 2025capstone ${{ secrets.DOCKERHUB_USERNAME }}/2025capstone:latest
# ์ฌ์ฉํ์ง ์๋ Docker ์ด๋ฏธ์ง ๋ฐ ์บ์ ์ ๋ฆฌ
sudo docker system prune -f
๊ธฐ์กด์ CI ํ ์คํธ ์ฝ๋์์ ๋ณ๊ฒฝ๋ ๋ถ๋ถ์ ์ดํด๋ณด๋ฉด
- ./gradlew build ๋ช
๋ น์ด๋ก Gradle์ ์ฌ์ฉํด ์ดํ๋ฆฌ์ผ์ด์
์ ๋น๋ํ ํ docker build ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํ์ฌ backend ๋๋ ํ ๋ฆฌ์์ Docker ์ด๋ฏธ์ง๋ฅผ ๋น๋ํ๊ณ USERNAME ๊ฐ์ ๋ฐ๋ผ 2025capstone:latest ๋ก ํ๊น
ํจ
=> USERNAME/2025capstone:latest ์ด๋ฏธ์ง ์์ฑ - docekr/login-action@v2 ๋ฅผ ์ฌ์ฉํด Docker Hub์ ๋ก๊ทธ์ธํจ
=> github secrets์ ์ง์ ํด๋ ์ ์ ์์ด๋์ ๋น๋ฐ๋ฒํธ ์ฌ์ฉ - ๋น๋ํ Docker ์ด๋ฏธ์ง๋ฅผ docker push ๋ช
๋ น์ด๋ก Docker Hub์ ํธ์ํจ
- run-docker-image-oon-ec2 ์์
- ssh-keyscan์ ์ฌ์ฉํด EC2 ํธ์คํธ์ SSH ๊ณต๊ฐ ํค๋ฅผ ~/.ssh/known_hosts์ ์ถ๊ฐํจ
(์์ ์ฒจ๋ถํ ssh ์ค์ ํฌ์คํ ์ฐธ๊ณ ) - appleboy/ssh-action@v0.1.6์ ์ฌ์ฉํด EC2์ SSH๋ก ์ฐ๊ฒฐํ๊ณ ์์
์คํ
- docker stop ๋ฐ docker rm ๋ช
๋ น์ด๋ก ์ด์ ์ ์คํ์ค์ธ ์ปจํ
์ด๋๋ฅผ ์ค์งํ๊ณ ์ญ์
- docker pull ๋ก Docker Hub์์ ์ต์ ์ด๋ฏธ์ง๋ฅผ ๊ฐ์ ธ์ด
- docker run ๋ช
๋ น์ด๋ก ์๋ก์ด ์ปจํ
์ด๋๋ฅผ 8080 ํฌํธ์ ๋ฐฐํฌํจ
- docker system prune์ ์ฌ์ฉํด ๋ถํ์ํ Docker ์ด๋ฏธ์ง์ ์บ์๋ฅผ ์ ๋ฆฌํจ
- docker stop ๋ฐ docker rm ๋ช
๋ น์ด๋ก ์ด์ ์ ์คํ์ค์ธ ์ปจํ
์ด๋๋ฅผ ์ค์งํ๊ณ ์ญ์
- ssh-keyscan์ ์ฌ์ฉํด EC2 ํธ์คํธ์ SSH ๊ณต๊ฐ ํค๋ฅผ ~/.ssh/known_hosts์ ์ถ๊ฐํจ
4. ํ ์คํธ

workflow์ ์ง์ ํด๋ backend/feature/cd-setup ๋ธ๋์น์ test.md ๋ฅผ push ํ์ ์ ์ฌ์ง๊ณผ ๊ฐ์ด ์ฒดํฌํ์๋ก CD ์์ ์ด ์ฑ๊ณต์ ์ผ๋ก ์ด๋ฃจ์ด์ง ๊ฒ์ ํ์ธํ ์ ์์

EC2์ ๋ค์ด๊ฐ sudo docker ps ๋ช ๋ น์ด๋ก ํ์ฌ ์คํ์ค์ธ ์ปจํ ์ด๋ ํ์ธ์ ์ ๋์ํ๋ ๊ฒ์ ํ์ธํ ์ ์์
'๐ Infra > CI ยท CD' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Jenkins์ ์ด์ฉํ Spring Boot ๋ฐฐํฌ ์๋ํ (0) | 2025.03.03 |
---|---|
Github Actions ํบ์๋ณด๊ธฐ (0) | 2025.02.26 |
GitHub Actions ์๊ฐ (0) | 2024.05.01 |