Github Actions 적용기

최우혁·2022년 6월 29일
0

ICN_APP

목록 보기
7/7
post-thumbnail

자동배포를 적용해보자

(출처 : https://flexagon.com/wp-content/uploads/2020/04/a-world-without-ci.cd-meme.jpg)

이전의 상황

지금까지 프로젝트를 배포하는 방식은 일일히 내가 무언가를 해야했었다.
jar 파일을 생성해서 docker image를 생성하고 그 image를 docker hub 로 push 하는 방식이었다.
실제 배포하는 서버에서는 docker hub에서 해당 이미지를 pull한 뒤, docker run을 통해 마무리 지었었다.

하지만 이러한 방식을 하다보니 매번 커맨드를 입력해야만 했고, 몇 번 하다가 귀찮아서 스크립트를 작성해서 스크립트로 동작하도록 만들었다. 그러다보니 매번 commit과 배포를 따로 해야하는 것이 여간 귀찮은게 아니었다.

그렇게하여 자동 배포 방식을 찾아보던 중, Github에서 자동 배포를 할 수 있다는 것을 알게 되어 적용을 해봤다.

프로젝트 구조 파악

먼저 Github Actions를 알아보기 전, 내 프로젝트 상황을 살펴봐야만 했다.
현재 나는 oracle database를 클라우드 환경에서 사용하고 있다. (oracle cloud free tier)
이 데이터베이스에 접근을 하기 위해서는 oracle wallet이 필요한데, 그래서 빌드한 jar를 docker image로 생성할 때 로컬에 있는 wallet을 포함하여 생성하도록 dockerfile을 작성했었다.

FROM openjdk:8-jdk-alpine
ADD {Build된 파일 이름}.jar app.jar
COPY Wallet /var/oracle/wallet
ENV JAVA_OPTS=""
ENTRYPOINT ["java","-jar","/app.jar"]

이런식으로 말이다...

그리고 이 wallet은 디렉토리 구조로 되어있으며, 해당 디렉토리 안에는 인증을 위한 파일들이 있었다.
먼저 해결해야하는 문제는 이 wallet을 어떻게 Github 배포 가운데에서 포함할 것인가 였다.

이 프로젝트는 public repository로 구성되어 있었고, 저 wallet 디렉토리를 해당 repository에 올린다면 우려스러운 상황이 펼쳐지지 않을까 걱정이 되긴 했다. 그래서 여러 방법도 찾아보고 생각해본 결론은 원격 저장소에 (AWS S3와 같은) wallet을 저장하고 Github actions 과정에서 해당 디렉토리를 받아서 image화 하는 방법이었다.

나는 모든 클라우드를 프리티어로 사용하고 있으며, google cloud 도 사용하고 있었기에 언제나 무료로 사용할 수 있는 Google Cloud Storage를 사용하기로 결정했다.

버킷을 만든 뒤 wallet을 압축하여 넣어놓은 모습.

wallet이 이렇게 해결이 되자, 다른 민감 정보 파일들도 동일하게 해야 하나 싶었다.
그래서 application.properties도 올리려 했는데, wallet과 달리 application.properties는 자주 변경이 일어난다.

그럼 변경이 될 때마다 매번 해당 저장소에 새로 저장을 해야하는데 그것도 번거로울 것 같았다.
또 다시 폭풍 서칭 후 알게 된 것은 Github Secret에 application.properties 파일의 내용을 문자열로 저장해두면 잘 갖다 쓴다는 것이었다.
믿기지 않았으나 일단 해보자 싶어서 이제 Github Actions의 빌드 파이프라인을 구성하기로 결정했다.

파이프라인 작성

# This is a basic workflow to help you get started with Actions

name: Deploy

# Controls when the workflow will run
on:
  # Triggers the workflow on push or pull request events but only for the "main" branch
  push:
    branches: [ "master" ]
  pull_request:
    branches: [ "master" ]

먼저 첫 부분은 이렇게 시작을 했다.
해당 파이프라인의 이름은 Deploy이며 그 다음 줄에 있는 on은 이 이벤트에 대한 설명이다. 그 아래에 있는 push와 pull_request, branches는 모두가 알 것이라 생각한다.

이 부분의 의미는 해당 파이프라인이 언제 시작할지를 나타내는 것이다. 따라서 저 부분은 master branch에 push나 PR이 생기면 자동으로 이 파이프라인이 실행된다는 의미이다.


# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    name: Build image
    runs-on: ubuntu-latest
    
    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - name : Checkout
        uses: actions/checkout@v3
        
      - name : Set up JDK 1.8
        uses : actions/setup-java@v1
        with :
          java-version: 1.8

그 밑에는 실제로 실행할 job에 대한 명시가 이루어진다. (job은 다른 job에 의존 관계를 가질 수 있으며, 독립적으로 병렬 실행도 가능하다.)
job의 이름은 build이며, 어떠한 환경에서 이 과정이 이루어질지 명시를 해두었다. 본인은 ubuntu 환경에서 주로 수동 배포를 해왔기에, 자동 배포 또한 동일한 환경에서 진행하기 위해 ubuntu 최신 버전으로 명시를 해두었다.

이제 steps라는 키워드를 마주하게 된다.
steps는 태스크들의 집합이며 이 안에서 커맨드를 날리거나 Action을 실행할 수 있다.

가장 먼저 배포를 하기 위해선 저장소에 있는 코드를 체크아웃 하는 과정이 선행되어야 할 것이다. 그래서 저장소에 있는 코드를 이 workflow가 실행되는 환경에 체크아웃할 수 있도록 Action을 추가해준다.

uses에 사용하고자 하는 액션의 위치를 {소유자}/{저장소명}@{참조자} 의 형태로 명시하는데요. GitHub에서 제공하는 체크아웃 액션의 소유자는 actions이고, 저장소 이름은 checkout이며 현재 포스팅 시점에서 사용 가능한 최신 버전은 v3입니다.
(출처 : https://www.daleseo.com/github-actions-checkout/)

코드를 환경에 체크아웃 했다면, 빌드를 해야할 것이다. 내 프로젝트는 Spring Boot(JAVA)로 구성되어 있기에 java를 실행할 수 있도록 JDK 1.8 버전을 해당 환경에 Setup 해두는 Action을 명시하였다.


	  # GCP auth
      - id: gcp_auth
        uses: google-github-actions/auth@v0
        with:
          credentials_json: ${{ secrets.SERVICE_ACCOUNT_KEY }}

      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v0

      - name: Get_file
        run: |-
          gsutil cp ${{ secrets.GSUTIL_ADDR }} .

그 다음 task는 Cloud 관련 task 명시이다.
나는 Google Cloud Storage에 Oracle DB에 접속하기 위한 Wallet을 올려두었다. 그렇기 때문에 빌드가 되는 환경에서 해당 Storage의 Wallet 파일에 접근할 수 있어야 한다.

Google Cloud Platform은 외부 어플리케이션에서 계정 연결을 하기 위해 Account Key 라는 것을 생성할 수 있게 하였다. 따라서 자신의 Google Cloud 계정에서 json 형식으로 Key를 생성하여 로컬에 다운받자.
(https://cloud.google.com/iam/docs/creating-managing-service-account-keys?hl=ko)

이런식으로 되어있다.

Secret에 대한 내용은 아래에서 다루도록 하겠으며, Service Account Key를 이용해 GCP에 대한 인증을 획득하면, 해당 Wallet이 있는 클라우드 상의 위치가 필요하다. 클라우드 콘솔을 통해 Storage에 접근하여 해당 파일의 gsutil URI 경로를 복사하자.

그리고 Get_File이라는 Action을 정의하여, 해당 Wallet을 클라우드 콘솔로부터 내려받을 수 있도록 커맨드를 작성하였다.


	  # docker Login
      - name: Image_build
        uses: docker/login-action@v1
        with: 
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      
      # Grant execute permission
      - name: Grant execute permission for gradlew
        run: chmod +x gradlew
        
      ## create application.properties
      - name: make application.properties
        run:
          mkdir ./src/main/resources | 
          touch ./src/main/resources/application.properties 
        shell: bash
        
      - name: deliver application.properties
        run: echo "${{ secrets.PROPERTIES }}" > ./src/main/resources/application.properties
        shell: bash

이렇게 Oracle DB에 접속하기 위한 Wallet을 Cloud Storage에서 내려받은 후, 나는 최종 빌드 파일을 도커 허브에 올릴 계획이기 때문에 도커 허브 계정에 로그인을 하는 Action을 추가하였다.

그리고 본격적으로 빌드를 하기 위해 grdlew에 실행 권한을 주는 Action을 추가하였고, gitignore를 통해 외부 저장소로 올리지 않은 application.properties를 빌드 하기 전 프로젝트의 경로에 추가를 해주었다.

해당 프로젝트는 public Repository 였기 때문에, properties 파일을 올리지 않았다. 따라서 위에서 체크아웃을 했을 때에는 git에 올라와 있는 프로젝트의 코드만 받았으므로 properties 파일은 존재하지 않는 상황이다.

계속해서 secrets. 로 시작하는 환경 변수가 보일 것이다. 이것은 Repository에 올릴 수 없는 민감 정보를 Github Repository의 Settings-Secrets-Actions 에 Key-Value 값으로 설정해두는 것이다.
Repository 소유자 포함 모든 인원들은 해당 내용이 한번 입력되면 내용을 확인할 수 없으며, 프로젝트 빌드-배포 과정 중 프로세스만 환경 변수를 통해 내용을 알 수 있다.

자 이제 프로젝트 빌드에 필요한 모든 리소스와 의존성들은 전부 다 갖춰졌다. 이제 빌드만 진행하면 된다.


	  # Build
      - name: Build with Gradle
        run: ./gradlew clean build
        
      # 전송할 파일을 담을 디렉토리 생성
      - name: Make Directory for deliver
        run: mkdir deploy
        
      # Jar 파일 Copy
      - name: Copy Jar
        run: cp ./build/libs/*.jar ./deploy/
          
      # Build docker image
      - name: Image_build and push
        uses: docker/build-push-action@v2
        with:
          context: .
          file: ./Dockerfile
          push: true
          tags: ${{ secrets.DOCKER_TAG }}

이제 Gradle을 통해 나의 프로젝트를 clean-build를 하였다. 이 과정 이후에 생기는 결과물은 jar 파일이다.
이 jar 파일을 이제 Dokerfile을 이용하여 docker image로 생성을 할 것이다.
그렇게 하기 위해서 /build/libs/ 경로에 있는 jar 파일을 Dockerfile이 있는 디렉토리 하위의 /deploy라는 디렉토리를 만들어 그 곳으로 복사를 시킨다.

FROM openjdk:8-jdk-alpine
ADD ./deploy/{Build된 이름}.jar app.jar
ADD wallet.tar.gz /var/oracle
ENV JAVA_OPTS=""
ENTRYPOINT ["java","-jar","/app.jar"]

Github에 올라와 있는 Dockerfile

그 후에는 Dockerfile을 이용하여 빌드 된 jar를 image화 하고 docker hub에 push 하도록 Action을 구성하였다.

추가 작업

이렇게 빌드 후 dockerhub에 배포하는 파이프라인을 통해 일일히 수작업으로 하던 배포 과정을 손 쉽게 할 수 있었다.

왜 자꾸 빌드 실패인가..?

빌드-배포 작업을 클릭하면 이렇게 로그를 볼 수 있다. 따라서 어떤 부분에서 Fail을 했는지 확인할 수 있다.

뭔가 더 추가할게 없나 생각해보다가, 예전에 29CM 기술 블로그에서 주문량 추세에 따른 알림을 슬랙과 연동해서(아마 Kibana에서 했던 것 같은데) 받을 수 있게 했다는 글이 생각나서 이 배포 결과를 나도 슬랙으로 받아보자!! 는 목표를 갖게 되었다.

해당 글 링크(29CM 기술 블로그)
https://medium.com/29cm/open-distro-for-elasticsearch-%EB%A1%9C%EA%B7%B8%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%9E%A5%EC%95%A0-%ED%83%90%EC%A7%80-8ff60dc3e5f


슬랙연동

먼저 Slack API 홈페이지에 들어가자. (https://api.slack.com/apps)
그리고 나의 WorkSpace에 내 App을 등록하고 알림을 받을 채널을 선택하자.

자세한 내용은 이 쪽으로...
https://velog.io/@tigger/Github-Action-Slack%EA%B3%BC-%EC%97%B0%EB%8F%99%ED%95%B4%EC%84%9C-%EB%B9%8C%EB%93%9C-%EA%B2%B0%EA%B3%BC-%EB%B0%9B%EA%B8%B0

그리고 아까 작성했던 Github Actions 의 yml 파일에 Action을 추가해주자.

- name: action-slack
        uses : 8398a7/action-slack@v3
        with:
          status: ${{ job.status }}
          author_name: Github Action Result
          fields: repo,message,commit,author,action,eventName,ref,workflow,job,took
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
        if: always() # Pick up events even if the job fails or is canceld.

이 과정을 통해 빌드가 실패하면 실패했다는 알림을, 성공했다면 성공했다는 알림을 설정해둔 슬랙의 채널에서 받을 수 있다!!


환경변수 관련

본인은 이렇게 환경 변수들을 구성하였다.

마무리

확실히 로컬에서 커밋하고 푸쉬할 때마다 자동으로 배포가 되어서 수작업으로 빌드-배포하던 시간이 크게 줄어들었다.
다만 마지막 종착지가 docker hub이기 때문에, Cloud 가상 머신에 들어가서 이미지를 pull하고 다시 run 해야 되는 과정은 손으로 해야만 했다.(자동 빌드-배포가 아니라 자동 빌드-배ㅍ 정도 되는듯 😩😩)

이 과정 또한 더 공부해서 아예 버튼 하나로 완벽하게 배포가 될 수 있는 파이프라인을 구성할 수 있기를 바라며 글을 끝맺는다..!!

profile
백엔드 주니어입니다🙌

0개의 댓글