본문 바로가기

CICD

Docker Compose와 GitHub Actions를 활용한 CI/CD 파이프라인 구축

기존 CI/CD 문제점

처음 CI/CD 파이프라인을 구성했을 때는 Docker Compose를 사용하지 않고, Redis도 EC2 인스턴스에 직접 설치해서 운영했습니다. Spring Boot 애플리케이션만 Docker로 빌드해 EC2에 배포했고, CI/CD 자체는 동작했지만 인프라 관리 측면에서 아쉬운 점이 있었습니다.

특히 Redis는 컨테이너 외부에 설치되어 있어 관리가 분리되어 있었고, 컨테이너화된 애플리케이션과의 연동이 유연하지 않았습니다. 또한 Redis 데이터의 영속성을 따로 관리하지 않아 재시작 시 데이터가 유지되지 않는 구조도 문제였습니다.

이러한 점들을 보완하기 위해 이번에는 Docker Compose를 도입해 Spring Boot와 Redis를 함께 컨테이너로 실행하고, Redis에는 --appendonly yes와 volume 설정을 적용해 데이터가 유지되도록 구성했습니다. 이 구조를 기반으로 CI/CD 파이프라인을 다시 구축했으며 아래 글은 이 전체 과정을 단계별로 정리한 내용입니다.

CI/CD

CI/CD는 지속적 통합(Continuous Integration)과 지속적 배포(Continuous Deployment)의 줄임말입니다. 코드를 수정하고 GitHub 등에 push하면 자동으로 빌드하고 테스트한 뒤 서버에 배포까지 이어지는 일련의 과정을 자동화해주는 개발 방식입니다.

기존에는 코드를 수정할 때마다 개발자가 직접 서버에 접속해서 빌드하고 배포하는 작업을 반복해야 했습니다. 하지만 CI/CD를 도입하면 이러한 작업을 자동화할 수 있어 작업 속도와 안정성이 크게 향상됩니다.

1. 사전 준비

  • AWS EC2 인스턴스 생성 및 접속 가능 상태
  • Spring Boot 프로젝트가 GitHub에 업로드된 상태

2. Docker 설치

우선 EC2 인스턴스에 접속하여 Docker를 설치해야 합니다. 아래 명령어를 입력하여 Docker를 설치합니다.

# HTTP 패키지 설치
sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common -y

# gpg 키 및 저장소 추가
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository --yes \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

# Docker 엔진 설치
sudo apt-get install docker-ce docker-ce-cli containerd.io -y

 

위의 명령어로 설치가 완료되면 아래 명령어를 통해 설치를 확인합니다.

# Docker 버전 확인
docker -v

# Docker Compose 버전 확인
docker compose version

3. Dockerfile, docker-compose.yml 파일 생성

(1) Dockerfile

 

Spring Boot 프로젝트를 빌드하면 build/libs/ 디렉토리에 *.jar 파일이 생성됩니다. 이 JAR 파일을 기반으로 도커 이미지를 만들기 위해 아래와 같이 Dockerfile을 작성합니다.

FROM amazoncorretto:17

WORKDIR /app

COPY ./build/libs/*SNAPSHOT.jar app.jar

ENTRYPOINT ["java", "-jar", "app.jar"]

 

  • amazoncorretto:17: AWS에서 관리하는 OpenJDK 17 이미지입니다.
  • WORKDIR /app: 컨테이너 내부 작업 디렉토리 설정
  • COPY: 로컬에서 빌드된 JAR 파일을 컨테이너 내부로 복사
  • ENTRYPOINT: 컨테이너가 시작되면 JAR 파일을 실행하도록 설정

(2) docker-compose.yml

아래 파일은 Spring Boot 앱과 Redis를 컨테이너로 동시에 띄우는 도커 컴포즈 파일입니다. 

services:
  app:
    image: "${DOCKERHUB_USERNAME}/${PROJECT_NAME}:latest"
    ports:
      - "${PORT}:${PORT}"
    volumes:
      - ./application.yml:/app/config/application.yml
    environment:
      - SPRING_CONFIG_LOCATION=file:/app/config/application.yml
    depends_on:
      - redis
  redis:
    image: redis:7
    container_name: redis
    ports:
      - "6379:6379"
    restart: always
    command: redis-server --appendonly yes
    volumes:
      - ./redis-data:/data

 

  • image: 빌드된 도커 이미지를 사용합니다.
  • ports: 외부와 통신할 수 있도록 포트를 바인딩합니다.
  • volumes: EC2에 생성된 application.yml 파일을 컨테이너 내부로 마운트하여 Spring Boot가 외부 설정 파일을 참조할 수 있도록 합니다.
  • environment: 컨테이너 실행 시 환경 변수를 설정하는 블록입니다.
    • SPRING_CONFIG_LOCATION=file:/app/config/application.yml: Spring Boot가 기본 경로(classpath:/application.yml)가 아닌 외부 마운트된 파일 경로에서 설정 파일을 읽도록 지정합니다.
      해당 경로는 위 volumes 설정에서 컨테이너 내부에 연결한 경로와 동일해야 합니다.
  • depends_on: Redis 컨테이너가 먼저 실행되어야 함을 지정합니다.
  • redis
    • redis:7: 공식 Redis 이미지 사용
    • --appendonly yes: 영속적인 데이터 저장을 위한 설정
    • volumes: Redis 데이터를 호스트에 영구 저장

3. GitHub Repository secrets 입력

CI/CD 과정에서 민감한 정보를 외부에 노출하지 않기 위해 GitHub의 Secrets 기능을 활용합니다.
GitHub Repository에서 Settings > Secrets and variables > Actions로 이동한 후 아래 항목들을 하나씩 New repository secret 버튼을 눌러 등록합니다.

APPLICATION Spring Boot의 application.yml 전체 내용을 복사하여 입력
DB_URL 데이터베이스 접속 URL (jdbc:mysql://RDS주소:DB포트/DB명)
DB_USERNAME DB 사용자 이름
DB_PASSWORD DB 비밀번호
DOCKERHUB_USERNAME DockerHub 사용자 이름
DOCKERHUB_PASSWORD DockerHub 비밀번호
EC2_HOST AWS EC2 인스턴스의 퍼블릭 IPv4 DNS
EC2_USERNAME EC2 접속 계정명 (예: ubuntu)
EC2_KEY EC2 접속용 SSH 개인 키 (.pem 파일을 메모장으로 열어 전부 복사하여 붙여 넣습니다.)
PORT Spring Boot 애플리케이션 실행 포트 (예시: 8080)
PROJECT_NAME 배포할 도커 이미지 이름
REDIS_PORT Redis 컨테이너 포트 (예시: 6379)

 

4.Gradle 파일 생성

이제 GitHub Actions 워크플로우 파일을 생성합니다. GitHub Repository에 진입 후 Actions를 클릭합니다. gradle를 검색하고 Configure를 클릭하여 파일을 생성하면 됩니다.

 

 

아래 워크플로우는 GitHub main 브랜치에 push 또는 pull request가 발생했을 때 자동으로 다음 작업을 수행합니다.

  1. GitHub Actions 환경 구성 (JDK 설치, 빌드 권한 부여 등)
  2. Spring Boot 애플리케이션 빌드 (Gradle)
  3. Docker 이미지 생성 및 DockerHub에 업로드
  4. EC2에 docker-compose.yml, .env, application.yml 복사
  5. EC2에서 Docker Compose를 실행하여 서비스 재시작

gradle.yml

# Workflow 이름
name: realtimetrip

# 어떤 이벤트가 발생하면 workflow 실행할 지 명시
on:
  # main 브랜치에 push나 pull request 발생 시
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

# 위 이벤트 발생 시 실행될 작업들
jobs:
  build:
    # VM의실행 환경 지정 => 우분투 최신 버전
    runs-on: ubuntu-latest

    # 실행될 jobs를 순서대로 명시
    steps:
    - name: Checkout
      uses: actions/checkout@v3

    # JDK 17 설치
    - name: Set up JDK 17
      uses: actions/setup-java@v3
      with:
        java-version: '17'
        distribution: 'corretto'
        
    # Gradle Build를 위한 권한 부여
    - name: Grant execute permission for gradlew
      run: chmod +x gradlew

    # Gradle Build (test 제외)
    - name: Build with Gradle
      run: ./gradlew clean build --exclude-task test

    # DockerHub 로그인
    - name: DockerHub Login
      uses: docker/login-action@v2
      with:
        username: ${{ secrets.DOCKERHUB_USERNAME }}
        password: ${{ secrets.DOCKERHUB_PASSWORD }}

    # Docker 이미지 빌드
    - name: Docker Image Build
      run: docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.PROJECT_NAME }} .

    # DockerHub Push
    - name: DockerHub Push
      run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.PROJECT_NAME }}

    # EC2에 docker-compose.yml 파일 복사
    - name: Copy docker-compose.yml to EC2
      uses: appleboy/scp-action@v0.1.5
      with:
        host: ${{ secrets.EC2_HOST }}
        username: ${{ secrets.EC2_USERNAME }}
        key: ${{ secrets.EC2_KEY }}
        source: "docker-compose.yml"
        target: "~/realtimetrip"

    # application.yml 생성 (마운트용)
    - name: Create application.yml on EC2
      uses: appleboy/ssh-action@v0.1.6
      with:
        host: ${{ secrets.EC2_HOST }}
        username: ${{ secrets.EC2_USERNAME }}
        key: ${{ secrets.EC2_KEY }}
        script: |
          echo "${{ secrets.APPLICATION }}" > ~/realtimetrip/application.yml

    # EC2에 .env 파일 생성 (.env는 docker-compose에서 참조하는 환경 변수 파일)
    - name: Create .env file on EC2
      uses: appleboy/ssh-action@v0.1.6
      with:
        host: ${{ secrets.EC2_HOST }}
        username: ${{ secrets.EC2_USERNAME }}
        key: ${{ secrets.EC2_KEY }}
        script: |
          echo "PORT=${{ secrets.PORT }}" > ~/realtimetrip/.env
          echo "DOCKERHUB_USERNAME=${{ secrets.DOCKERHUB_USERNAME }}" >> ~/realtimetrip/.env
          echo "PROJECT_NAME=${{ secrets.PROJECT_NAME }}" >> ~/realtimetrip/.env

    # EC2에서 Docker Compose로 애플리케이션 실행
    - name: Application Run
      uses: appleboy/ssh-action@v0.1.6
      with:
        host: ${{ secrets.EC2_HOST }}
        username: ${{ secrets.EC2_USERNAME }}
        key: ${{ secrets.EC2_KEY }}
        script: |
          cd ~/realtimetrip
          sudo docker compose pull
          sudo docker compose down
          sudo docker compose up -d

 

5. CI/CD 동작 확인

아래 사진 처럼 workflow가 제대로 동작하면 CI/CD 파이프라인 구축이 완료된 것입니다.

 

또한, EC2에 접속해서 docker ps 명령어를 입력하면 Spring Boot 애플리케이션과 Redis가 각각 컨테이너 형태로 실행 중인 것을 확인할 수 있습니다.


이번 과정을 통해 CI/CD 파이프라인을 직접 구축해보면서 코드 수정 → 빌드 → 배포까지의 흐름을 자동화할 수 있다는 점이 정말 편리하다는 걸 체감할 수 있었습니다.

또한, Docker Compose를 활용해 Spring Boot 애플리케이션과 Redis를 각각 독립된 컨테이너로 동시에 실행하는 구조를 제대로 이해할 수 있었습니다. 특히 application.yml과 .env 파일을 EC2에 외부 마운트 방식으로 연결해 컨테이너에 주입하는 과정이 처음에는 헷갈렸지만 직접 작성하고 테스트하면서 이해할 수 있었던 것 같습니다.

 

참고 링크

https://chb2005.tistory.com/191

'CICD' 카테고리의 다른 글

도커 개념 정리: 이미지, 컨테이너, 볼륨, 컴포즈  (0) 2025.06.22