이전 프로젝트에서는 Spring boot 프로젝트의 빌드 파일을 이미지로 만들어 도커에 올리
EC2에서 pull해서 서버를 배포를 하는 방식으로 진행했었다.
코드가 변경될때마다 이 과정을 반복했기 때문에 상당히 귀찮고 불편했어서
이번 프로젝트에서는 Github Actions를 사용해서 CI/CD를 구축해보려 한다.
Github Actions 선택 이유
Jenkins, Travis CI 등 다양한 CI/CD 도구가 있지만
가장 접근성이 좋고 무료라는 장점이 있어 github actions를 선택하게 되었다.
도커
- Dockerfile
FROM openjdk:21-jdk-slim
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-Duser.timezone=Asia/Seoul", "-jar", "/app.jar"]
JDK 21 slim 이미지 기반으로 Spring Boot 애플리케이션 JAR을 컨테이너에 넣고
실행 시 한국 시간대로 구동되도록 설정했다.
- EC2에 도커 설치
// 기존 패키지 업데이트
sudo apt update
// 필요한 패키지 설치
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common
// Docker GPG 키 추가
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
// Docker 저장소 추가
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
// Docker 설치
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io
// 설치 확인
docker --version
github-actions.yml
name: CI/CD using github actions & docker
on:
push:
branches:
- main
permissions:
contents: read
jobs:
CI-CD:
runs-on: ubuntu-latest
name: Build
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Cache Gradle packages
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Create application.properties
run: |
echo "${{ secrets.APPLICATION }}" > src/main/resources/application.properties
- name: Create application-prod.properties
run: |
echo "${{ secrets.APPLICATION_PROD }}" > src/main/resources/application-prod.properties
- name: Build with Gradle
run: ./gradlew clean build
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Docker image
run: |
docker build -f Dockerfile -t ${{ secrets.DOCKER_USERNAME }}/spring-app:${{ github.sha }} .
docker push ${{ secrets.DOCKER_USERNAME }}/spring-app:${{ github.sha }}
- name: Deploy to EC2
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_PRIVATE_KEY }}
script: |
sudo docker pull ${{ secrets.DOCKER_USERNAME }}/spring-app:${{ github.sha }}
docker stop spring-app || true
docker rm spring-app || true
docker run -d -p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
--name spring-app \
${{ secrets.DOCKER_USERNAME }}/spring-app:${{ github.sha }}
전체 코드는 이렇다 !!
너무 기니까 단락별로 나눠서 설명하도록 하겠다.
- Workflow 기본 설정
# 워크플로우 이름
name: CI/CD using github actions & docker
# main 브랜치에 push될 때 자동으로 실행
on:
push:
branches:
- main
# 코드 읽기 권한만 부여
permissions:
contents: read
main 브랜치에 코드가 올라가면 CI/CD가 자동으로 동작한다.
- Job 정의
jobs:
CI-CD:
runs-on: ubuntu-latest
name: Build
Job의 이름은 CI-CD, 우분투 최신 환경에서 실행하며 Job 표시 이름은 Build이다.
- 코드 체크아웃 & JDK 세팅
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
GitHub 저장소 코드를 runner에 가져오고 JDK 21을 설치한다.
- 캐싱 & gradlew 실행 준비
- name: Cache Gradle packages
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Grant execute permission for gradlew
run: chmod +x gradlew
Gradle 의존성 캐시를 해줌으로써 빌드 속도를 최적화하고 gradlew 실행 권한을 부여한다.
- application.properties 생성
- name: Create application.properties
run: |
echo "${{ secrets.APPLICATION }}" > src/main/resources/application.properties
- name: Create application-prod.properties
run: |
echo "${{ secrets.APPLICATION_PROD }}" > src/main/resources/application-prod.properties
GitHub Secrets에서 가져와 소스 리소스 폴더에 application.properties, application-prod.properties를 생성한다.
민감한 정보이기 때문에 깃허브에 올리지 않고 Secrets로 안전하게 관리해야한다.
- Gradle 빌드
- name: Build with Gradle
run: ./gradlew clean build
이전 빌드 결과 삭제 후 새로 빌드하며 빌드 성공 시 JAR이 파일 생성된다.
- Docker Hub 로그인 & 이미지 빌드/푸시
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Docker image
run: |
docker build -f Dockerfile -t ${{ secrets.DOCKER_USERNAME }}/spring-app:${{ github.sha }} .
docker push ${{ secrets.DOCKER_USERNAME }}/spring-app:${{ github.sha }}
Docker Hub에 로그인을 하고 이미지 빌드/푸시를 한다.
커밋 해시를 태그로 붙여 push하여 관리한다.
- EC2 배포
- name: Deploy to EC2
uses: appleboy/ssh-action@v1.2.0
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_PRIVATE_KEY }}
script: |
sudo docker pull ${{ secrets.DOCKER_USERNAME }}/spring-app:${{ github.sha }}
docker stop spring-app || true
docker rm spring-app || true
docker run -d -p 8080:8080 \
-e SPRING_PROFILES_ACTIVE=prod \
--name spring-app \
${{ secrets.DOCKER_USERNAME }}/spring-app:${{ github.sha }}
EC2 SSH에 접속해 Docker 이미지를 pull하고 기존 컨테이너는 삭제한 후
새로운 컨테이너를 실행한다.
Github secrets

깃허브 레포지토리의 Settings > Secrets > Actions > New repository secret 로 따라 들어가면
secrets를 등록할 수 있다.

Secret Key의 내용은 저장하면 볼 수 없고 편집과 삭제만 가능하다.
성공적으로 잘 돌아가는걸 보니 너무나도 뿌듯했다 !!!!!

PR 올라갈때마다 빌드 & 배포를 하지 않아도 되서 훨씬 간편해졌다 🎉 🎉
'Web > 배포' 카테고리의 다른 글
| Spring Boot HTTPS로 배포하기 - CertBot, NginX (0) | 2025.09.22 |
|---|---|
| [AWS] Spring Boot 프로젝트 배포하기 - EC2, RDS, S3 (0) | 2025.09.16 |
| [AWS] EC2 배포 - 스프링부트 + 리액트 프로젝트 배포하기 (1) | 2024.10.18 |