GitHub Actions로 CI/CD 구축 (1)

민씨·2024년 1월 8일
0
post-custom-banner

개요

소프트웨어 개발 라이프 사이클을 생각해 보겠습니다.

개발

소프트웨어 개발은 크게 세 단계로 구분됩니다. 먼저, 요구사항을 파악하여 기획자가 전체적인 계획을 세웁니다. 이후 디자이너가 피그마 등의 도구를 이용하여 와이어프레임을 제작합니다. 개발자는 기획과 와이어프레임을 참고하여 적절한 기술 스택을 선택하고 구현을 진행합니다.

  1. 기획
  2. 와이어프레임
  3. 구현

빌드

소프트웨어가 구현되면, 소스코드를 컴파일하여 배포 가능한 형태로 패키징 합니다. 예를 들어 자바 프로젝트의 경우 jar 파일로 패키징 될 수 있습니다. 패키징 된 파일은 도커 이미지 같은 형태로 변환되어 이미지 저장소에 공유될 수 있습니다.

  1. 컴파일 : 소스코드 컴파일
  2. 패키징 : 배포 가능한 형태로 변환
  3. 셰어링 : 저장소에 공유

테스팅

빌드 과정과 병행하여 진행되는 유닛, 통합 테스팅과 전체 애플리케이션 워크플로우를 테스팅하는 시스템 테스팅을 진행합니다.

  1. 유닛 테스팅 : 개별 모듈 테스트
  2. 통합 테스팅 : 모듈 연동 테스트
  3. 시스템 테스팅 : 전체 시스템 테스트

배포

마지막으로 개발 서버 혹은 프로덕션 서버에 배포됩니다.

지속적 통합(CI)

지속적 통합(CI)은 위 소프트웨어 라이프 사이클 중 개발 ~ 테스팅을 의미합니다.

  1. 소스코드 작성
  2. GitHub 푸시
  3. 빌드
  4. 테스팅
  5. 패키징
  6. 셰어링

지속적 배포(CD)

지속적 통합(CI) 과정에 배포 과정을 추가하면 지속적 배포(CD)가 됩니다.
이는 소프트웨어 라이프 사이클 중 개발 ~ 배포를 의미합니다.

  1. 지속적 통합(CI)
  2. 배포

개발자는 기능 구현과 트러블 슈팅 과정에서 이러한 단계들을 반복하게 됩니다.

이 과정이 수동으로 이루어지면 많은 시간이 소요되므로, 자동화 환경을 구축하는 것은 매우 중요한데요.

이에 저는 프로젝트를 진행하기 전 GitHub Actions를 이용하여 CI/CD 환경을 구축하려합니다.

GitHub Actions

CI/CD를 구성하기 위한 서비스는 Jenkins, AWS CodePipeline 등이 있지만 저는 GitHub Actions를 선택했습니다.

제가 생각하는 워크플로우는 아래 그림과 같은데요.

lgg drawio

  • 소스코드 저장소 : GitHub
  • 자동화 시스템 : GitHub Actions
  • 이미지 저장소 : GitHub Container Registry

모든 기능을 GitHub 하나의 플랫폼에서 관리할 수 있다는 것이 가장 큰 이유였습니다.

덤으로 Jenkins는 서버가 추가로 필요하고 AWS는 과금에 대한 부담감이 살짝 있는 반면, GitHub Actions는 개인으로 사용할 시 거의 무료나 다름이 없습니다.

이번 포스팅에서는 Spring Boot 프로젝트를 이용하여 아래의 단계를 자동화 해보겠습니다.

  1. 패키징 : jar 파일 생성
  2. 이미지 빌드 : 도커 이미지 생성
  3. 셰어링 : 이미지 푸시

패키징

Actions 탭의 New workflow를 누르시면 GitHub에서 제공하는 이미 작성된 yaml 파일이 존재합니다. 이를 기본으로 시작합니다.

기본 파일에서 아래의 두 가지는 변경하였습니다.

  • 자바 17을 사용하기 때문에 11 → 17
  • 라이센스를 corretto를 사용하기에 temurin → corretto
name: Java CI with Gradle

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3

      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'corretto'

      - name: Build with Gradle
        uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0
        with:
          arguments: build

처음에는 복잡해 보일 수 있지만, 간단하게 생각해 봅시다.

runs-on 이라고 붙은 것은 가상의 환경을 의미합니다.

즉, 우분투 운영체제의 가상의 컴퓨터가 하나 만들어졌고, 해당 컴퓨터에서 내가 입력하는 명령어들이 실행되고 있다고 생각하면 이해하기가 쉽습니다.

actions, action 는 GitHub Actions에서 제공하는 라이브러리로 복잡한 설정 없이 필요한 환경을 구성할 수 있도록 도와줍니다.

actions/checkout@v3는 저장소의 소스코드를 가상 컴퓨터로 복사합니다.

actions/setup-java@v3는 JDK를 설치해 줍니다. 물론 직접 설치해도 상관은 없지만 이미 잘 구성된 설정을 사용하는 것이 효율적입니다.

gradle/gradle-build-action는 Gradle을 이용하여 빌드를 진행합니다.

즉 로컬에서 아래의 명령어를 수행하는 것과 똑같습니다.

$ ./gradlew build

따라서 아래와 같이 작성해도 됩니다.

- name: Build with Gradle
  run: ./gradlew build

하지만 역시 남이 만들어놓은 기능은 캐싱이라던지 조금 더 특별한 기능이 존재할 가능성이 크기 때문에, 저는 이해 가능한 범주라면 사용하는 편입니다.

라는 내용이 있으니 참고해 봅시다.

재미있는건 기본적으로 제공하는 파일을 생성만 했을 뿐인데 jar 파일을 생성하는 패키징 과정이 끝났다는 것입니다.

이미지 빌드

패키징이 완료되면 아래의 그림처럼 build/libs 폴더 하위에 [rootProject.name]-[version].jar 파일이 생성됩니다.

스크린샷 2024-01-05 오후 4 20 05

생성된 jar 파일은 아래와 같이 실행합니다.

$ java -jar build/libs/app-0.0.1-SNAPSHOT.jar

따라서, 이미지 생성을 위한 도커 파일을 아래와 같이 작성할 수 있습니다.

touch Dockerfile
# 베이스 이미지 - corretto:17
FROM amazoncorretto:17

# jar 복사
COPY ./build/libs/app-0.0.1-SNAPSHOT.jar ./app.jar

# jar 실행
CMD ["java", "-jar", "app.jar"]

이미지 생성은 build 명령어로 할 수 있습니다.

$ docker build -t [IMAGE_NAME] .

최종 목표는 생성한 이미지를 GitHub Container Registry에 푸시하는 것인데요.

이를 위해서는 이미지 이름을 아래와 같이 생성하여야 합니다. (참고)

ghcr.io/NAMESPACE/IMAGE_NAME:latest
  • NAMESPACE : github 아이디
  • IMAGE_NAME : 이미지 이름

예를 들면,

$ docker build -t ghcr.io/ber01/lo-gak-gye:latest .

저는 workflows를 작성하기 전에 로컬환경에서 먼저 실행을 해보는데요.

위 명령어를 실행해 보니 이미지 생성이 잘 됩니다.

그렇다면 그대로 workflows에 작성해 주면 됩니다.

- name: Docker image Build
  run: docker build -t ghcr.io/ber01/lo-gak-gye:latest .

이미지 빌드까지 완료됐습니다.

셰어링

생성한 이미지를 GitHub Container Registry에 푸시해 보겠습니다.

이번에도 로컬 환경에서 먼저 해보겠습니다.

이미지 푸시를 위해서는 Personal Access Token(이하 PAT)이 필요한데요.

  • GitHub Settings의 Develop에서 발급이 가능합니다.

scope를 write, delete, workflows를 선택한 뒤 생성해 줍니다.

이후 CR_PAT 환경 변수에 발급받은 PAT를 넣어줍니다.

$ export CR_PAT=[발급받은_PAT]

컨테이너 레지스트리에 로그인합니다.

$ echo $CR_PAT | docker login ghcr.io -u [GITHUB_ID] --password-stdin

아까 생성했던 이미지를 푸시합니다.

$ docker push ghcr.io/[GITHUB_ID]/[IMAGE_NAME]:latest

이 과정을 workflows에 작성해 주면 됩니다.

컨테이너 레지스트리에 로그인하는 과정은 docker/login-action이 존재합니다.

- name: Login to GitHub Container Registry
  uses: docker/login-action@v3
  with:
    registry: ghcr.io
    username: ${{ github.actor }}
    password: ${{ secrets.CR_TOKEN }}

여기서 username은 본인의 GITHUB_ID를 넣어주면 됩니다.

password는 방금 발급받은 PAT를 작성해 주면 되는데요.

직접 작성하면 토큰이 유출 되므로 secrets 을 통해 접근하여야 합니다.

저장소 -> Settings -> Secrets and variables -> Actions -> New repository secret

스크린샷 2024-01-05 오후 8 33 10

위 절차대로 CR_TOKEN 시크릿을 생성하면 {{ secrets.CR_TOKEN }} 문법으로 workflows내에서 접근할 수 있습니다.

앞으로 민감한 정보는 여기에 담아둡시다.

스크린샷 2024-01-05 오후 8 34 16

컨테이너 레지스트리에 로그인하는 workflows의 예시는 아래와 같습니다.

username까지 시크릿으로 생성할 필요는 없어보입니다.

- name: Login to GitHub Container Registry
  uses: docker/login-action@v3
  with:
    registry: ghcr.io
    username: ber01
    password: ${{ secrets.CR_TOKEN }}

이미지 푸시는 간단합니다.

- name: Docker image Push
  run: docker push ghcr.io/ber01/lo-gak-gye:latest

셰어링까지 완료되었습니다.

작성된 최종 workflows는 아래와 같습니다.

name: Java CI with Gradle

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

permissions:
  contents: read

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
      # 소스코드 체크아웃
      - uses: actions/checkout@v3

	  # JDK 17 설치
      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'corretto'
          
      # jar 패키징
      - name: Build with Gradle
        uses: gradle/gradle-build-action@bd5760595778326ba7f1441bcf7e88b49de61a25 # v2.6.0
        with:
          arguments: build

      # 이미지 빌드
      - name: Docker image Build
        run: docker build -t ghcr.io/ber01/lo-gak-gye:latest .

      # Container Registry 로그인
      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ber01
          password: ${{ secrets.CR_TOKEN }}

      # 이미지 푸시
      - name: Docker image Push
        run: docker push ghcr.io/ber01/lo-gak-gye:latest

마치며

1편에서는 GitHub에 소스코드를 푸시하면 GitHub Container Registry의 이미지 저장소에 이미지가 자동으로 생성되도록 자동화 하였습니다.

2편에서는 AWS EC2에 접근하여 자동으로 배포하는 것 까지 다뤄보겠습니다.

감사합니다.

profile
進取
post-custom-banner

0개의 댓글