본문 바로가기
Server/🐳 Docker

[Docker] GitHub Actions와 Docker Hub를 이용해 CI/CD 환경 구축하기

by 코딩하는 동현😎 2025. 1. 24.

CI/CD(Continuous Integration/Continuous Deployment)는 지속적인 통합과 배포를 가능하게 하는데, 전 이벤트 기반 아키텍처로 구성된 자바 스프링 부트 프로젝트를 베포 자동화 하기 위해서 Producer, Consumer 서버 두개를 베포하는것을 했습니다.

.
├── .github
│   └── workflows
│       ├── ci.yml        # CI: 빌드 및 테스트용 워크플로우 파일
│       └── cd.yml        # CD: 배포용 워크플로우 파일
├── producer
│   ├── src
│   ├── build.gradle
│   └── Dockerfile
├── consumer
│   ├── src
│   ├── build.gradle
│   └── Dockerfile
├── settings.gradle
└── README.md

 

 

개요

이 글에서는 GitHub Actions를 활용해 Gradle 기반의 Producer와 Consumer 프로젝트를 빌드하고, Docker Hub에 이미지를 업로드하며, AWS EC2 서버에 배포하는 CI/CD 환경을 구축하는 과정을 다룹니다. 특히, 워크플로우 작성과 민감한 정보 처리를 중심으로 설명합니다.


1. CI/CD 환경 개요

CI/CD(Continuous Integration/Continuous Deployment)는 지속적인 통합과 배포를 가능하게 하며, 다음과 같은 장점을 제공합니다:

  • 코드 품질 개선: 푸시마다 자동으로 빌드와 테스트 수행.
  • 배포 자동화: 배포 과정을 자동화해 실수를 방지하고 시간을 절약.

아키텍처 개요

  1. GitHub Actions로 코드가 푸시되거나 PR(Pull Request)이 생성되면 워크플로우 시작.
  2. 프로젝트를 빌드 후 Docker 이미지를 생성.
  3. Docker Hub에 이미지를 푸시.
  4. AWS EC2 서버로 SSH 연결을 통해 컨테이너 실행.

2. CI/CD 구성

2-1. 사전 준비

  1. Docker Hub 계정 생성 및 이미지 저장소(repository) 생성
    • Docker Hub에서 <이름>/producer, <이름>/consumer 같은 이미지를 저장할 저장소 생성.
  2. GitHub Secrets 설정
    • GitHub 레포지토리의 Settings > Secrets and variables > Actions에서 아래 정보를 등록:
      • DOCKER_USERNAME: Docker Hub 사용자 이름.
      • DOCKER_PASSWORD: Docker Hub 비밀번호.
      • EC2_HOST, EC2_USERNAME, EC2_KEY: EC2 접속 정보.
      • 기타 필요한 환경 변수: SPRING_DATASOURCE_URL, REDIS_PASSWORD 등.

2-2. 워크플로우 파일 작성

 

워크플로우 파일 위치

 

GitHub Actions에서 워크플로우는 반드시 .github/workflows/ 디렉토리에 YAML 형식으로 저장해야 합니다. 이는 GitHub가 워크플로우 파일을 자동으로 탐지하고 실행하기 위한 표준 위치입니다.

 


CI 파일: 코드 빌드

ci-workflow.yml 파일은 Producer와 Consumer 프로젝트를 각각 빌드합니다.

name: CI for Producer and Consumer

on:
  push:
    branches:
      - 'main'
      - 'develop'
  pull_request:
    branches:
      - 'main'
      - 'develop'

permissions:
  contents: read

jobs:
  build-producer:
    name: Build Producer
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: ☕️ Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
      - name: 🐘 Build Producer with Gradle
        run: |
          chmod +x ./gradlew
          ./gradlew :producer:clean :producer:build -x test --stacktrace

  build-consumer:
    name: Build Consumer
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: ☕️ Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
      - name: 🐘 Build Consumer with Gradle
        run: |
          chmod +x ./gradlew
          ./gradlew :consumer:clean :consumer:build -x test --stacktrace
 

설명:

  • on: push와 pull_request 이벤트로 워크플로우가 실행.
  • jobs:
    • build-producer: Producer 프로젝트를 빌드.
    • build-consumer: Consumer 프로젝트를 빌드.

CD 파일: 빌드 및 배포

cd-workflow.yml 파일은 Docker 이미지를 생성하고 Docker Hub에 푸시한 뒤, AWS EC2에 배포합니다.

name: CD with Gradle and Docker for Producer and Consumer

on:
  push:
    branches:
      - 'main'
      - 'develop'
  pull_request:
    branches:
      - 'main'
      - 'develop'

permissions:
  contents: read

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: ☕️ Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'
      - name: Grant execute permission for Gradlew
        run: chmod +x ./gradlew
      - name: 🐘 Build Producer with Gradle
        run: ./gradlew :producer:clean :producer:build -x test --stacktrace
      - name: 🐘 Build Consumer with Gradle
        run: ./gradlew :consumer:clean :consumer:build -x test --stacktrace
      - name: 💣 Build and Push Docker Images
        run: |
          docker login -u <DOCKER_USERNAME> -p <DOCKER_PASSWORD>
          docker build -f ./producer/Dockerfile -t <DOCKER_USERNAME>/producer:latest ./producer
          docker build -f ./consumer/Dockerfile -t <DOCKER_USERNAME>/consumer:latest ./consumer
          docker push <DOCKER_USERNAME>/producer:latest
          docker push <DOCKER_USERNAME>/consumer:latest
      - name: 🚀 Deploy without Docker Compose
        uses: appleboy/ssh-action@master
        with:
          host: <EC2_HOST>
          username: <EC2_USERNAME>
          key: ${{ secrets.EC2_KEY }}
          port: 22
          script: |
            echo "Stopping and removing existing containers"
            sudo docker stop producer || true
            sudo docker rm producer || true
            sudo docker stop consumer || true
            sudo docker rm consumer || true

            echo "Pulling and running the latest images"
            sudo docker pull <DOCKER_USERNAME>/producer:latest
            sudo docker run -d --name producer -p 8080:8080 <DOCKER_USERNAME>/producer:latest
            sudo docker pull <DOCKER_USERNAME>/consumer:latest
            sudo docker run -d --name consumer -p 8081:8081 <DOCKER_USERNAME>/consumer:latest

            echo "Cleaning up unused Docker images"
            sudo docker image prune -f
 
 

설명:

  • Docker Build & Push: Producer와 Consumer 이미지를 Docker Hub에 업로드.
  • AWS EC2 배포:
    • 기존 컨테이너를 중지하고 삭제.
    • Docker Hub에서 최신 이미지를 Pull 후 실행.
    • 미사용 이미지를 삭제하여 공간 절약.

3. 결과 확인

배포가 완료되면 AWS EC2에서 다음 명령어로 컨테이너 상태를 확인할 수 있습니다:

sudo docker ps

4. 주의 사항

  1. Secrets 관리: 모든 민감 정보는 GitHub Secrets에 저장.
  2. Docker 이미지 태깅: latest 외에도 버전 태그를 사용하면 더욱 안정적인 배포 관리 가능.
  3. EC2 설정: EC2에 Docker가 설치되어 있어야 하며, 필요한 포트가 열려 있어야 합니다.
반응형

댓글