개발노트

25.03.03 GitHub Actions(CI / CD)와 Docker로 AWS에 자동 배포하기 (2) 본문

DevOps

25.03.03 GitHub Actions(CI / CD)와 Docker로 AWS에 자동 배포하기 (2)

ddong-kka 2025. 3. 3. 16:07

개요

이전에서 과정은 AWS EC2를 구매하고 도커를 통해 postgreSQL , 배포할 앱을 배포했었다.

이번에는 Spring Boot 애플리케이션을 빌드하고, 도커 이미지를 배포한 후 , EC2 서버에서 실행하는 CI/CD 파이프라인을 

구축해보겠다.

 

목표

  1. CI ( Continuous Integration )
    • GitHub에 코드 푸시하면, 자동으로 Gradle 빌드 및 테스트 실행
    • Docker 이미지 빌드 및 GitHub Container Registy(GHCR) 또는 Docker Hub에 푸시
  2. CD ( Continuous Deployment )
    • EC2 서버에서 컨테이너 자동 배포
    • Github Actions에서 EC2에 SSH 접속 후 컨테이너 실행

 

1. GitHub Secrets 설정

노출되지 말아야할 값들을 GitHub Secrets에 추가해준다.

new repository secret 을 눌러 사용할 값들을 설정해준다.

 

나는 간단하게만 구성했다.

Actions 설정
내가 설정한 값

 


 

2. `.github/workflows/deploy.yml` 작성

Github Actions 워크플로우 파일을 작성한다.

name: board_api CI/CD  # CI/CD 파이프라인의 이름 설정

on:
  push:
    branches:
      - master  # master 브랜치에 푸시가 발생할 때 파이프라인 실행

jobs:
  build:
    runs-on: ubuntu-latest  # 빌드 작업이 실행될 환경 (우분투 최신 버전)
    outputs:
      image_tag: ${{ steps.set_tag.outputs.image_tag }}  # IMAGE_TAG를 outputs으로 설정

    steps:
      - name: checkout
        uses: actions/checkout@v4  # 레포지토리의 소스 코드를 체크아웃하는 단계

      - name: JDK 17 version setup
        uses: actions/setup-java@v3  # JDK 17 버전을 설정하는 단계
        with:
          distribution: 'temurin'  # temurin JDK 배포판 사용
          java-version: '17'  # JDK 17 버전 사용

      # gradlew 실행 권한 부여 및 빌드
      - name: Gradle 실행 권한 부여 및 빌드
        run: |
          chmod +x ./gradlew  # gradlew 실행 권한을 부여
          ./gradlew clean build  # Gradle을 사용해 빌드 작업 실행

      - name: DockerHub 로그인
        run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin  # DockerHub에 로그인

      - name: Docker 이미지 태그 생성
        id: set_tag  # ID를 지정해야 outputs 사용 가능
        run: |
          # 현재 날짜와 시간, GitHub 커밋 SHA 값을 기반으로 태그를 생성
          TAG_NAME=main-$(date +%Y%m%d-%H%M%S)-${GITHUB_SHA::7}
          echo "IMAGE_TAG=$TAG_NAME" >> $GITHUB_ENV  # 환경 변수 설정
          echo "image_tag=$TAG_NAME" >> $GITHUB_OUTPUT # outputs에 저장

      - name: Docker 이미지 빌드
        run: |
          # Docker 이미지를 빌드할 때 캐시를 사용하지 않도록 --no-cache 옵션을 추가
          docker build --no-cache -t ${{ secrets.DOCKER_USERNAME }}/board-api:${{ env.IMAGE_TAG }} .  # Docker 이미지 빌드

      - name: Docker Hub에 푸시
        run: |
          # 빌드한 이미지를 DockerHub에 푸시
          docker push ${{ secrets.DOCKER_USERNAME }}/board-api:${{ env.IMAGE_TAG }}  # 빌드한 이미지를 DockerHub에 푸시

  deploy:
    needs: build  # build 작업이 완료된 후 deploy 작업 실행
    runs-on: ubuntu-latest  # 배포 작업을 실행할 환경 (우분투 최신 버전)
    steps:
      - name: EC2 서버에 SSH로 접속하여 배포
        uses: appleboy/ssh-action@v0.1.7  # SSH를 통해 EC2 서버에 접속하여 배포 작업 수행
        with:
          host: ${{ secrets.EC2_HOST }}  # EC2 호스트 주소
          username: ${{ secrets.EC2_USER }}  # EC2 사용자명
          key: ${{ secrets.EC2_PRIVATE_KEY }}  # EC2 서버의 개인 키
          script: |
            # build job에서 설정한 image_tag 값을 참조
            IMAGE_TAG=${{ needs.build.outputs.image_tag }}  
            echo "Deploying Docker image: ${{ secrets.DOCKER_USERNAME }}/board-api:${IMAGE_TAG}"

            # 기존 컨테이너가 실행 중이면 정지하고 삭제
            sudo docker stop board-api || true
            sudo docker rm board-api || true

            # 최신 이미지를 DockerHub에서 풀
            sudo docker pull ${{ secrets.DOCKER_USERNAME }}/board-api:${IMAGE_TAG}

            # 최신 태그를 'latest'로 설정
            sudo docker tag ${{ secrets.DOCKER_USERNAME }}/board-api:${IMAGE_TAG} ${{ secrets.DOCKER_USERNAME }}/board-api:latest

            # 새로운 Docker 컨테이너 실행 (포트 8080에 매핑)
            sudo docker run -d --name board-api --network board_network -p 8080:8080 ${{ secrets.DOCKER_USERNAME }}/board-api:latest

 

작성법에 대해선 다른 글에서 정리해보겠다.

 

CI/CD 가 동작되기 위해선 application.yml이 있어야한다.


 

2.  CI /CD 동작 확인

작성한 파일을 github에 푸쉬하고 나면 gitHub Actions가 동작하는것이 보일것이 상단의 Actions 탭에서 진행 과정을 볼 수 있다.

 

Actions 탭

 

모든 과정 통과

 

 

정상적으로 빌드되었자만 jobs 간의 env 값이 공유가 안되어서 태그명이 제대로 전달되지않는 문제가 있었지만

outputs 로 태그명을 선언해 문제를 해결했다. 이제 정상적으로 동작되는지 알아보자

 


3. 결과 확인

 Map<String,Object> successResponse = Map.of(
                    "status" , "success",
                    "message", "회원가입 성공!!!!!",
                    "test" , "cd 동작 성공"
            );

 

요청할 컨틀롤러의 응답을 변경했다. test : cd 동작 성공  이라는문구가 함께 반환되면 변경한 코드가 자동으로 EC2

인스턴스에 업데이트된거다. 요청해보겠다.

 

업데이트 성공

 

도커를 사용하여 PostgreSQL 컨테이너와 배포할 앱 컨테이너를 도커 네트워크로 연결하고, 정상적으로 동작하는 것을 확인한 후, GitHub Actions를 활용하여 CI/CD 파이프라인을 구현해보았다. 이 방식을 다음 사이드 프로젝트에도 적용해볼 

예정이다.