Github Actions과 Docker을 활용한 CI/CD 구축

2025. 9. 23. 12:14·Web/배포

이전 프로젝트에서는 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
'Web/배포' 카테고리의 다른 글
  • Spring Boot HTTPS로 배포하기 - CertBot, NginX
  • [AWS] Spring Boot 프로젝트 배포하기 - EC2, RDS, S3
  • [AWS] EC2 배포 - 스프링부트 + 리액트 프로젝트 배포하기
도탱
도탱
ღ 성장하는 백엔드 개발자 ღ
  • 도탱
    도탱이의 코딩흔적
    도탱
  • 전체
    오늘
    어제
    • 분류 전체보기 (43)
      • Programing Language (4)
        • Python (3)
        • JAVA (1)
      • Web (28)
        • Spring (24)
        • 배포 (4)
      • 알고리즘 (2)
      • 자격증 (2)
      • 회고 (2)
      • 트러블슈팅 (5)
  • 인기 글

  • hELLO· Designed By정상우.v4.10.3
도탱
Github Actions과 Docker을 활용한 CI/CD 구축
상단으로

티스토리툴바