Github Actions이란 언제 필요하고, 무엇이고, 어떻게 사용할까?

LeeKyoungChang·2024년 2월 9일
1
post-thumbnail
post-custom-banner

📚 1. 언제 필요할까?

✔️ 주어진 상황

스크린샷 2024-02-09 오후 12 27 31

A라는 개발자는 main branch에서 push event가 발생할 때마다 가상서버에 프로젝트를 변경 후 백그라운드로 띄워야한다.

main branch에서 event가 발생할 때마다 이와 같은 상황을 반복하다보니 귀찮아졌다.

개선하고자 특정 branch에서 push 이벤트가 발생했을 때 통합과 배포를 자동화하고 싶었다.

 

✔️ 2019년 이후, 이제는 Github Actions이다.

스크린샷 2024-02-09 오후 12 34 05

사진 참고 자료

자료를 찾다가 우연히 블로그를 읽게 되었는데, 2019년 말 Github에서는 Github Actions을 공개하게 되었고 무엇보다 다른 CI/CD 와 차별화되는 한 가지 요소가 존재한다.
이는, Actions 이라는 재사용 가능한 컴포넌트를 제공하는 점이다.

즉, Github Marketplace에서는 다양한 공통 작업을 수행하는 수천 개의 Actions이 이미 공개가 되어있기 때문에 이용하는 고객들은 자신에게 필요한 표준 작업을 위해 코드를 재입력할 필요가 없으며, 필요한 Actions을 검색한 후 자신의 workflow에 바로 통합하면 된다.

 

 

📚 2. 무엇일까?

✔️ CI/CD란?

먼저, Github Actions을 사용하기 전 CI/CD를 이해해야한다.
CI/CD는 지속적인 통합과 배포를 의미하며 특정 이벤트가 발생했을 때 내부에 있는 명령어들이 자동으로 실행된다.

  • CI : 개발자가 주기적으로 코드 변경시마다 자동으로 메인 저장소에 통합하는 것
  • CD : CI 과정을 통과한 코드를 자동으로 실제 운영 환경에 배포하는 과정

 

✔️ Github Actions 간단한 실행 과정

스크린샷 2024-02-09 오후 1 09 38

현재 그림을 대상으로 보면

(1) workflow 디렉터리에서 정의한 event에 해당되는 이벤트가 발생하며 워크플로우를 활성화시킨다.
(2) Workflow 내에서는 각각의 Jobs들이 독립적으로 실행된다. (위에서 아래로 순서로 실행된다.)
(3) steps에서는 개별 작업을 실행할 수 있는 최소 단위로, 스크립트 실행 또는 액션 실행 등의 작업을 수행할 수 있다.

 

✔️ Github Actions에서 사용되는 용어

Actions 용어로는 크게 키워드와 표현식 두 가지로 나뉘어진다. (개인적인 생각)

  • 키워드(Keywords) : YAML 파일의 구조를 정의하는 데 사용되는 예약어 key: value 로 구성
  • 표현식(Expressions) : 워크플로우를 실행하는 동안 계산되어야 하는 값 ${{ }} 안에 작성

 

📖 A. 키워드(Keywords)

name: Example Workflow

on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout version 3
        uses: actions/checkout@v3

      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'adopt'
  • name : workflow 이름 정의
  • on : 어떤 Github 이벤트에 의해 실행될지 정의
  • jobs : 워크플로우에서 실행될 작업들을 정의
  • build : 작업의 식별자
  • runs-on : 작업이 실행될 환경을 지정
  • steps : 각 작업에서 실행될 단계들
  • uses : 특정 Github Actions을 사용할 때 지정 (위에서 말한 Github Marketplace에서 제공하는 actions/checkout@v3)
  • run : 스크립트 또는 커맨드 라인 명령을 실행할 때 사용
  • with : 워크플로우에서 특정 액션을 실행할 때 필요한 입력 파라미터 설정

 

📖 B. 표현식(Expressions)

jobs:
  example:
    runs-on: ubuntu-latest
    steps:
      - name: Docker meta
        id: docker_meta
        uses: crazy-max/ghaction-docker-meta@v1
        with:
          images: ${{ secrets.DOCKER_IMAGE }}
          tag-custom: latest, ${{ github.run_number }}

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
  • ${{ secrets.DOCKER_IMAGE }} : Docker Image 이름
  • ${{ secrets.DOCKER_USERNAME }} : Docker Hub 아이디
  • ${{ secrets.DOCKER_PASSWORD }} : Docker Hub 비밀번호
  • ${{ github.run_number }} : 현재 워크플로우 실행의 고유 번호
  • ${{ github.event_name }} : 현재 이벤트의 이름
  • ${{ github.ref_name }} : 실행되는 브랜치나 태그의 이름

✔️ secrets 설정은 Settings -> Secrets and variables -> Actions

New repository secret에 추가하면 된다.
스크린샷 2024-02-09 오후 1 31 47

 

💡 참고
application.yml, application.properties 파일은 Repository에 추가하면 안되기 때문에 (gitignore에서 yml을 추가한 상태)
✔️ secrets 설정은 Settings -> Secrets and variables -> Actions 에서 추가한 후 호출하면 된다.

스크린샷 2024-02-09 오후 1 35 06

 

 

📚 3. 어떻게 사용할까?

📖 A. 전체 코드

name: server CI/CD

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


jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout version 3
        uses: actions/checkout@v3

      - name: Set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'adopt'
      
      - name: Create YAML File
        run: |
          echo "${{ secrets.APPLICATION_YAML }}" > ./src/main/resources/application.yml
          echo "${{ secrets.APPLICATION_AWS_YAML }}" > ./src/main/resources/application-aws.yml

      - name: Build with Gradle
        run:
          ./gradlew clean bootJar --build-cache -Dorg.gradle.jvmargs=-Xmx4g

        # - name: Check jar file
        #   run: ls -al

      # Docker Image 메타데이터 생성 및 관리
      - name: Docker meta
        id: docker_meta
        uses: crazy-max/ghaction-docker-meta@v1
        with:
          images: ${{ secrets.DOCKER_IMAGE }}
          tag-custom: latest, ${{ github.run_number }} # 사용자 정의 태그 사용

      # Docker Image build 환경 구성
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1

      # Docker login 후, Docker Hub에 Image를 push
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      # - name: Extract commit message
      #   id: commit-message
      #   run: |
      #     echo "COMMIT_MESSAGE=$(git log -1 --pretty=%B | base64)" >> $GITHUB_ENV

      - name: Docker Build and Push
        uses: docker/build-push-action@v2
        with:
          context: .
          file: ./Dockerfile
          platforms: linux/amd64
          push: true
          tags: ${{ steps.docker_meta.outputs.tags }}
          label: |
            ${{ steps.docker_meta.outputs.labels }}


  #         # commit.message=${{ env.COMMIT_MESSAGE }}

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: ssh connect to ec2
        uses: appleboy/ssh-action@v0.1.6
        with:
          host: ${{ secrets.EC2_HOST }} # 퍼블릭 IPv4 주소
          username: ${{ secrets.EC2_USERNAME}} # ubuntu
          key: ${{ secrets.EC2_KEY }} # ssh pem키 => BEGIN RSA ~ END RSA
          script: |
            sudo docker rm -f ${{ secrets.DOCKER_SERVER_NAME }}
            sudo docker pull ${{ secrets.DOCKER_IMAGE }}:latest
            sudo docker image prune -f
            sudo docker run --name ${{ secrets.DOCKER_SERVER_NAME }} -p 8000:8080 -d ${{ secrets.DOCKER_IMAGE }}:latest
스크린샷 2024-02-09 오후 2 10 55

 

✔️ on

어떤 branch에서 이벤트 발생시 실행될 것인지 지정한다.

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

 

✔️ jobs에서 작업의 식별자 - build, deploy

작업의 식별자는 독립적으로 실행된다. 즉, 실행하며 build에서 생성한 파일은 deploy에서는 보이지 않는다.

(1) build

  • 배포파일 만들기
  • 배포파일 대상 Docker Image 만들기
  • Docker Image에 빌드 번호와 latest를 붙여 Docker Hub에 push

(2) deploy

  • EC2에서 Docker Hub에 저장된 Docker Image latest를 pull
  • Docker Image를 백그라운드로 띄우기
jobs:
  build:
	  ~~

  deploy:
	  ~~

 

✔️ build steps

      - name: Checkout version 3
        uses: actions/checkout@v3

워크플로우가 실행되는 GitHub 리포지토리의 코드를 체크아웃한다.
체크아웃은 Repository 코드를 가상 환경에 복사하는 과정이다.

 

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

actions/setup-java@v3 액션을 사용하여 JDK 버전 17 설정
Java 기반의 프로젝트를 빌드하거나 실행하기 위해 필요한 환경을 구축

 

     - name: Create YAML File
        run: |
          echo "${{ secrets.APPLICATION_YAML }}" > ./src/main/resources/application.yml
          echo "${{ secrets.APPLICATION_AWS_YAML }}" > ./src/main/resources/application-aws.yml

위에 있는 💡 참고 를 확인하자

 

      - name: Build with Gradle
        run:
          ./gradlew clean bootJar --build-cache -Dorg.gradle.jvmargs=-Xmx4g

이전에 이미 수행된 작업의 결과를 재사용하기 위해 --build-cache 을 사용하였고,

Gradle 데몬(백그라운드에서 실행되는 빌드 프로세스)을 위한 Java 가상 머신(JVM)의 최대 힙 크기를 설정, JVM에 할당할 최대 메모리 크기는 4GB로 설정하기 위해 -Dorg.gradle.jvmargs=-Xmx4g을 사용하였다.

 

      - name: Docker meta
        id: docker_meta
        uses: crazy-max/ghaction-docker-meta@v1
        with:
          images: ${{ secrets.DOCKER_IMAGE }}
          tag-custom: latest, ${{ github.run_number }}

Docker Image와 Docker Tag 설정을 추가

 

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

Docker Image build 환경 구성이다.

 

      - name: Docker Build and Push
        uses: docker/build-push-action@v2
        with:
          context: .
          file: ./Dockerfile
          platforms: linux/amd64
          push: true
          tags: ${{ steps.docker_meta.outputs.tags }}
          label: |
            ${{ steps.docker_meta.outputs.labels }}

위에서 설정한 Docker Image를 Docker Hub에 저장한다.

  • tags : 위에서 지정한 태그가 추가된다.
  • label : 이미지나 컨테이너에 대한 메타데이터를 저장하는 키-값(key-value) 쌍
  • file : Dockerfile이 실행된다. (Dockerfile은 jar파일 기준으로 만들었다.)
  • platforms : Docker 이미지를 빌드할 때 명시적으로 타겟이 되는 플랫폼을 지정하는 옵션

 

✔️ deploy steps

    needs: build

build가 실행된 후에 deploy jobs가 실행되도록 설정한 것이다.

 

uses: appleboy/ssh-action@v0.1.6

appleboy/ssh-action@v0.1.6 : SSH를 통해 원격 서버에 명령어를 실행할 수 있게 해주는 액션 (선택)
aws-actions/configure-aws-credentials : AWS 계정에 대한 액세스를 설정하는 단계
aws-actions/amazon-ec2-instance-action : EC2 인스턴스를 시작, 중지, 종료 등의 작업을 자동화할 수 있다.

 

          host: ${{ secrets.EC2_HOST }} # 퍼블릭 IPv4 주소
          username: ${{ secrets.EC2_USERNAME}} # ubuntu
          key: ${{ secrets.EC2_KEY }} # ssh pem키 => BEGIN RSA ~ END RSA
          script: |
            sudo docker rm -f ${{ secrets.DOCKER_SERVER_NAME }}
            sudo docker pull ${{ secrets.DOCKER_IMAGE }}:latest
            sudo docker image prune -f
            sudo docker run --name ${{ secrets.DOCKER_SERVER_NAME }} -p 8000:8080 -d ${{ secrets.DOCKER_IMAGE }}:latest

가상서버 EC2에서 Docker Image를 Docker Hub에서 pull 받은 후, 컨테이너를 실행한다.

 

이제는 main 브랜치에서 특정 이벤트가 발생하면 자동으로 가상환경에 백그라운드로 띄우는 것이 된다. 야호!!!

 

 


참고 자료

profile
"야, (오류 만났어?) 너두 (해결) 할 수 있어"
post-custom-banner

0개의 댓글