GitHub Actions를 통한 CI/CD 구축 (feat. Docker, AWS EC2)

조성우·2024년 4월 30일
0

AWS

목록 보기
4/7
post-thumbnail

GitHub Actions를 활용해 CI/CD를 구축하는 프로세스를 알아보자

Spring Boot / Java 프로젝트를 기반으로 Docker를 활용해 Build하여 Docker Image를 생성하고, 이를 AWS EC2 상에서 컨테이너화하는 작업을 GitHub Actions로 자동화할 것임


1. CI 구축 (Continuous Integration)


1.1. Github Actions - Secrets 설정

Your-Repository-Settings-Secrets and variables-Actions

  • DOCKERHUB_USERNAME, DOCKERHUB_PASSWORD
    : Docker Image를 Push & Pull 하기 위해, Docker Hub의 계정 정보 입력
  • APPLICATION_YML (선택)
    : application.yml의 민감한 정보를 서버에 배포할 때 적용하기 위해 따로 정리해둠 (DBMS Password 등의 정보. 빌드 시에 해당 파일이 생성되도록 할 것임)

1.2. GitHub Actions Workflow 생성

Java with Gradle을 베이스로 스크립트 생성

gradle.yml 파일 수정
(아래는 예시이며, 링크를 참고하여 스크립트를 이해하고 Customize 해보자)

name: Java CI with Gradle

# 동작 조건 설정: develop 브랜치에 push or pull request가 발생할 경우 동작
on:
  push:
    branches: [ "develop" ]
  pull_request:
    branches: [ "develop" ]

permissions:
  contents: read

jobs:
  # Spring Boot 애플리케이션을 빌드하여 Docker Hub에 Push하는 과정
  build-docker-image:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4
    
    # 0. 외부 설정 파일 세팅 (application-db.yml)
    - name: 외부 설정 파일 세팅
      run: echo "${{ secrets.APPLICATION_DB_YML }}" > ./src/main/resources/application-db.yml
    
    # 1. Java 21 세팅
    - name: Set up JDK 21
      uses: actions/setup-java@v4
      with:
        java-version: '21'
        distribution: 'corretto'

    # 2. gradlew 권한 부여
    - name: Run chmod to make gradlew executable
      run: chmod +x gradlew
    
    # 3. Spring Boot 애플리케이션 빌드
    - name: Build with Gradle
      run: ./gradlew build
      
    # 4. Docker 이미지 빌드 & Push
    - name: Docker Image Build & Push
      run: |
        docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_PASSWORD }}
        docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/project-name .
        docker push ${{ secrets.DOCKERHUB_USERNAME }}/project-name

※ 참고 사항

  1. 위 Secrets에서 등록한 APPLICATION.YML 변수를 활용하여 # 0. 외부 설정 파일 세팅 부분에서 파일로 만들어주었음 (민감한 DBMS 정보 등을 담고 있음)
  2. Docker Build & Push는 애플리케이션 코드 변경이 있을 때마다 Gradle Build와 함께 자동으로 수행되도록 CI 단계에 포함하였음

위의 스크립트를 적용하면,
develop 브랜치에 Push or Pull Request 발생 시에 Workflow가 실행되면서 Gradle BuildDocker Build & Push까지 수행됨


2. CD 구축 (Continuous Deployment)

이제 Docker Hub에 업로드된 Docker Image를 AWS EC2에서 컨테이너화(run) 하는 작업까지 추가해보자

2.1. self-hosted runner (자체 호스트형 실행기)

: GitHub가 관리하는 인스턴스가 아닌, 사용자가 원하는 서버에서 Github Actions Application을 실행할 수 있는 기능

AWS EC2에 SSH 접속을 하기 위한 Access Key, GitHub Actions Secrets 등이 필요 없이 해당 서버에서 명령을 실행할 수 있음!!!

원하는 서버에서 GitHub Actions Runner를 1. 다운로드, 2. 설정, 3. 실행 과정을 거쳐야 한다.


Your-Repository-Settings-Actions-Runners 클릭


New self-hosted runner 클릭


EC2에서 아래 명령어를 실행하여 Download & Configure
(./config.sh 까지만 실행함)


※ 위 과정 중 발생하는 오류 해결 방법 (Amazon Linux 사용 시 발생하는 오류)

  1. shasum 관련 오류
echo "64a47e18119f0c5d70e21b6050472c2af3f582633c9678d40cb5bcb852bcc18f  actions-runner-linux-x64-2.316.0.tar.gz" | shasum -a 256 -c

-bash: shasum: command not found 오류 메시지 발생
(shasum: SHA 메시지 다이제스트, 암호화 체크섬, 암호화 해시코드를 계산하는 데 사용)

따라서 아래 명령어를 실행해 shasum 설치

yum install perl-Digest-SHA

2. Libicu 종속성 오류
./config.sh --url https://github.com/xxx --token xxx
Libicu's dependencies is missing for Dotnet Core 6.0
Execute sudo ./bin/installdependencies.sh to install any missing Dotnet Core 6.0 dependencies.

위 오류 메시지대로 sudo ./bin/installdependencies.sh 명령어를 입력해도 fedora linux인 관계로 종속성 설치가 이루어지지 않음

따라서 아래 명령어를 통해 libicu 설치

sudo yum install libicu

이후 self-hosted runner aapplication을 서비스로 설정할 것임 (항상 자동 실행)
(Configuring the self-hosted runner application as a service 참고함)

아래 명령어를 입력해 서비스를 설치 및 시작함

sudo ./svc.sh install
sudo ./svc.sh start

이제 Github Actions를 수신할 준비가 완료됨

2.2. GitHub Actions Workflow 스크립트 이어서 수정...


  # 1.2 스크립트에 이어서...

  # 위 build-docker-image 작업에서 Push한 이미지를 EC2에서 Pull & Run시키는 과정 
  run-docker-image-on-ec2:
    # build-docker-image 작업이 완료되어야 실행됨
    needs: build-docker-image

    # self-hosted-runner에서 동작
    runs-on: self-hosted

    steps:
    # 1. 최신 이미지 Pull
    - name: docker pull
      run: |
        docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_PASSWORD }}
        docker pull ${{ secrets.DOCKERHUB_USERNAME }}/project-name
      
    # 2. 기존 컨테이너 중지
    - name: docker stop container
      run: docker stop $(docker ps -q) 2>/dev/null || true

    # 3. 최신 이미지 컨테이너화하여 실행시
    - name: docker run new container
      run: docker run --rm -d -p 8080:8080 ${{ secrets.DOCKERHUB_USERNAME }}/project-name

    # 4. 기존 이미지 정리
    - name: delete old docker image
      run: docker system prune -f

※ 참고 사항

  1. docker pull 수행 전에 로그인을 해준 이유는 해당 이미지를 private 설정하였기 때문임
  2. docker run에서 --rm 옵션은 컨테이너가 종료될 때 컨테이너와 관련된 리소스 제거
  3. 4. 기존 이미지 정리 부분에서 컨테이너 중지 후 삭제해 주어도 되지만 해당 부분을 뒤로 뺀 이유는 기존 이미지에서 공유하여 사용하던 레이어 부분을 다시 다운로드하도록 하지 않기 위해서임 (ex. demo 이미지가 a, b, c 레이어로 구성되어 있을 때, 최신 버전에서 a만 변경되었을 경우 a만 새로 다운로드함)

3. GitHub Actions Workflow 수행 확인

👍

0개의 댓글