Github Action을 이용한 CI/CD 개발 주기 자동화

Jeongmin Yeo (Ethan)·2021년 3월 27일
17

Github

목록 보기
1/2
post-thumbnail

CI(Continuous integration) / CD(Continuous Delivery)

CI (Continuous integration)

  • 개발자를 위한 자동화 프로세스인 지속적인 통합(Continuous Integration)을 의미한다.
  • CI를 성공적으로 구현할 경우 애플리케이션에 대한 새로운 코드 변경 사항이 정기적으로 빌드 및 테스트되서 공유 레파지토리에 통합되므로 여러 명의 개발자가 동시에 애프리케이션 개발과 관련된 코드 작업을 할 경우 충돌하는 문제를 해결할 수 있다.
  • 요약하자면 code 작성 - Build - Test를 짧은 주기로 자동화 하는것

CD (Continuous Delivery) 또는 (Continuous Deployment)

  • Continuous Delivery와 Continuous Deployment는 유사하다 둘 다 애플리케이션에 적용한 변경 사항이 버그 테스트를 거쳐 리포지토리에 업로드 되는 것을 뜻한다.
  • Continuous Delivery와 Continuous Deployment는 같은 의미로 쓰이기도 하지만 딱 하나의 차이가 있다.
  • 실제 개발에선 개발환경과 운영환경을 구별해서 개발한다. 개발 환경에서는 Delivery와 Deployment 모두 자동화를 한다.
  • 하지만 운영환경에선 Delivery는 마지막에 개발자가 수동으로 해줘야 하는 부분을 남겨놓고 Deployment는 운영환경에서도 자동화를 한다.

Github Action

Github Action은 이러한 CI / CD를 가능하게 해주는 툴 중 하나다.

이러한 툴은 많은데 Jenkins도 있고 AWS Code Deploy, GCP의 Code Build등 다양하게 있다.

Github Action을 사용하기 위해서는 Github 레파지토리에 Action 탭을 누르면 가능하다.

그 후 자동화된 서비스를 만들기 위해서는 workflow라는 걸 만들어야 한다.


Workflow

Workflow란 CI / CD 같은 자동화된 프로세스를 만들기 위한 Yaml 파일이다.

그러므로 확장자로 .yml 이나 .yaml을 붙여야 한다.

workflow을 알기 위해서는 해당 개념들이 있는데 workflow > job > step > action 순으로 명시를 해서 파일을 작성해야 한다.

workflows

workflows는 레파지토리에 정의한 자동화된 프로세스 절차이다.

workflows는 하나 이상의 Jobs으로 구성되고 이벤트 기반으로 트리거된다.

예를들면 schedule을 설정해 정해진 시간에 실행되도록 하거나 main 브런치에 코드가 push가 되거나 어떤 브런치에 pull request가 만들어지면 실행되도록 설정할 수 있다.

또는 webhook을 이용해 외부 이벤트에 대한 응답으로 실행할 수 있다.

Jobs

job은 하나 이상의 steps로 구성되고 workflow에 있는 job 끼리는 병렬적으로 실행된다.

하지만 job 끼리는 순서대로 실행되도록 설정할 수 있다. 뭐 예를들면 한 job은 build 과정을 실행하고 다른 job은 test를 실행하도록 명시하고 test job은 build가 완료된 후 실행하도록 할 수 있다.

이때 두 job 끼리는 다른 환경에서 실행되므로 깉은 스토로지를 사용하도록 설정해야 한다.

Steps

하나의 step은 하나의 task 라고 생각하면 된다. 하나의 job에 여러 task를 넣을 수 있다.

step은 action이나 shell command로도 생성될 수 있다.

하나의 job에 있는 step은 같은 runner에 의해 실행된다.

Actions

workflow에서 가장 작은 개념으로 action은 독립된 실행할 명령이라고 생각하면 된다.

Runners

runner는 Github에 의해 호스팅 되고있는 서버이다. runner를 통해서 job을 실행시킨다고 생각하면 된다.

Example the workflow file

name: learn-github-actions 

on: 
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs: 
  check-bats-version: 
    runs-on: ubuntu-latest

    steps: 
      - uses: actions/checkout@v2 
      - uses: actions/setup-node@v1 # 
      
      - run: npm install -g bats
      - run: bats -v

name: learn-github-actionsworkflow 이름을 말한다.
on: push, pull_request이 workflow가 trigger 될 이벤트를 명시해야 한다. 여기서는 push와 pull_request를 명시했다.
jobs:workflow에서 실행할 job을 말한다
check-bats-version:Job의 이름을 말한다.
runs-on: ubuntu-latestJob이 돌아갈 환경을 명시한다. 여기서는 ubuntu 최신 버전을 입력한 것
steps:하나의 job에서 실행할 step들을 명시한다.
- uses: actions/checkout@v2uses는 다른 오픈소스 커뮤니티에 있는 actions를 가지고 와서 실행하라고 job에게 알려주는 것 checkout@v2는 나의 레파지토리 환경을 runner로 복사하는 걸 말한다.
- uses: actions/setup-node@v1이 uses는 runner에 node 패키지를 설치하도록 한다.
- run: npm install -g batsrunner에서 command로 실행할 명령을 말한다.
- run: bats -vrunner에서 command로 실행할 명령을 말한다.

Github Action workflow - matrix build

예제에선 ubuntu 환경에서 빌드를 했지만 Github Action에서는 여러 환경으로 동시에 빌드하게 할 수 있다

이를 matrix build라고 한다. 즉 다양한 운영체제에서 다양한 노드 버전으로 돌려보는 것을 말한다.

왜 매트릭스라는 이름이 붙었냐면 행렬을 뜻한다 행에서는 운영체제 열에서는 노드 버전 이런식으로

Github Actions workflow - matrix build
jobs: 
  build:
    runs-on: ubuntu-lastest
    
    strategy:
      matrix:
        os: [ubunutu-latest, windows-2016]
        node-version: [12.x, 14.x]			

Github Action build artifact

Github Action에서 job을 빌드와 테스트로 분리했다면 서로 다른 환경을 가지고 있는 것이다.

그러므로 Built-in 아티팩트 스토로지를 이용해 빌드 아티팩트를 서로 공유해야 한다.

사용할 때 주의할 점은 Job을 순서대로 동기화해서 진행시켜야한다. 어떤 job 이후에 실행하고 싶다! 이러한 옵션을 추가할 수 있다

github action workflow yaml file
jobs:
  job_1:
    name: Add 3 and 7
    runs-on: ubuntu-latest
    steps:
      - shell: bash
        run: |
          expr 3 + 7 > math-homework.txt
      - name: Upload math result for job 1
        uses: actions/upload-artifact@v2 # built-in 아티팩트 스토로지에 업로드 한다. 
        with:
          name: homework
          path: math-homework.txt

  job_2:
    name: Multiply by 9
    needs: job_1 # job_1이 끝난후에 실행하도록 명시했다. 
    runs-on: windows-latest
    steps:
      - name: Download math result for job 1 
        uses: actions/download-artifact@v2 # 아티팩트 스토로지에서 다운로드 한다. 
        with:
          name: homework
      - shell: bash
        run: |
          value=`cat math-homework.txt`
          expr $value \* 9 > math-homework.txt
      - name: Upload math result for job 2
        uses: actions/upload-artifact@v2
        with:
          name: homework
          path: math-homework.txt

Github Action CI(Continuous integration)

Github Actions - workflow yaml file [Java Maven 기준]
name: Java CI with Maven 

on:  
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:  
  package:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2  
    - name: Set up openjdk-15 
      uses: actions/setup-java@v1 # 여기서는 자바 버전으로 15.0.1을 입력 
      with:
        java-version: 15.0.1
    - name: Build with Maven
      run: mvn -B package --file pom.xml # maven을 이용한 package 명령 

Maven - package

maven package는 크게 두가지 목표가 있다.

  1. clean target directory ,

  2. package project -> output으로 jar 파일을 만들어내는 것

maven의 lifecycle은 clean -> validate -> compile -> test -> pacakge 순으로 진행된다.

package 하기 위해선 source code를 compile 후 test 하고 모든 테스트가 통과된 후에 가능하다. 그걸 생략할려면 skiptest를 명령해줘야한다,


Github Action을 이용한 협업 - Main 브런치 관리하는 방법

Github에서 협업할 때 main 브런치는 특히 보호해야할 존재다. Main 브런치를 관리하는 방법에서 중요한 것 몇개만 소개하겠다.

Main 브런치를 관리하는 방법

  • 빌드가 안되는 코드는 애초에 PR(Pull Request) 리뷰로 가지 않도록 한다.
  • 브런치 정책에 따라서 PR 리뷰를 하고 이를 통해서 merge를 하도록 한다.
    • 그리고 리뷰가 끝난 후 라벨링(labeling)을 해서 PR들을 관리하도록 한다.
    • git-flow를 써서 브런치 관리하는 방법 추천한다.

Branch Proection Rules

Github Repository -> Settings -> Branches 로 가면 Branch proection rules에서 브런치 정책들을 설정할 수 있다.

대표적인 Branch Protection rules

  • Require pull request reviews before merging

    • 이 브런치에 머지하기전에 PR을 날리고 리뷰를 받아야 하며 몇명 이상의 리뷰를 통과해야만 한다.
  • Require status checks to pass before merging

    • 머지 전에 빌드 성공 여부를 체크하는 것
    • 세부사항으로는 머지전에 항상 그 브런치는 최신의 상태여야 한다. 라는 것
  • Include adminstrators

    • 어드민은 이 브런치 정책을 따르지 않을수도 있는데 어드민도 강제로 따르게 하는 것

Github Action CD(Continuous Delivery)

Github Action을 통해 Google Cloud Kubernetes Engine에 CD를 설정하는 방법이다.

다음 파일은 main 브런치에 푸쉬가 되면 자동적으로 GKE에 배포되도록 설정한 파일이다.

Dockerfile, Kubernetes에서 service와 deployment, kustomization 파일을 작성했다는 기준으로 만든 yml 파일이다.

Github Actions - deploy.yml [Google Cloud Kubernetes Engine 기준]
name: Deploy to GKE

on:
  push:
    branches:
      - main

env:
  PROJECT_ID: ${{ secrets.GKE_PROJECT }} # GCP Project ID 
  GKE_CLUSTER: cluster-test-2021 # GKE Cluseter ID
  GKE_ZONE: asia-northeast3-c # GKE Cluseter Zone
  DEPLOYMENT_NAME: app-demo # GKE deloyment name 
  IMAGE: gke-image-name # Container Image Name 

jobs:
  setup-build-publish-deploy:
    name: SetUp Build Publish Deploy
    runs-on: ubuntu-latest

    steps:

      # Checkout Repository
      - name: Checkout Repository
        uses: actions/checkout@v2

      # Setup Google Cloud CLI
      - name: Setup Google Cloud CLI
        uses: google-github-actions/setup-gcloud@v0.2.0
        with:
          service_account_key: ${{ secrets.GKE_SA_KEY }}
          project_id: ${{ secrets.GKE_PROJECT }}

      - run: |-
          gcloud --quiet auth configure-docker

      # Get the GKE credentials so we can deploy to the cluster
      - uses: google-github-actions/get-gke-credentials@v0.2.1
        with:
          cluster_name: ${{ env.GKE_CLUSTER }}
          location: ${{ env.GKE_ZONE }}
          credentials: ${{ secrets.GKE_SA_KEY }}

      # Build the Docker image
      - name: Build
        run: |-
          docker build \
            --tag "gcr.io/$PROJECT_ID/$IMAGE:$GITHUB_SHA" \
            --build-arg GITHUB_SHA="$GITHUB_SHA" \
            --build-arg GITHUB_REF="$GITHUB_REF" \
            .

      # Push the Docker image to Google Container Registry
      - name: Publish
        run: |-
          docker push "gcr.io/$PROJECT_ID/$IMAGE:$GITHUB_SHA"

      # Set up  Kustomize
      - name: Set up Kustomize
        run: |-
          curl -sfLo kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/v3.1.0/kustomize_3.1.0_linux_amd64
          chmod u+x ./kustomize

      # Deploy the Docker image to the GKE cluster
      - name: Deploy
        run: |-
          ./kustomize edit set image gcr.io/PROJECT_ID/IMAGE:TAG=gcr.io/$PROJECT_ID/$IMAGE:$GITHUB_SHA
          ./kustomize build . | kubectl apply -f -
          kubectl rollout status deployment/$DEPLOYMENT_NAME
          kubectl get services -o wide


  • Github에서 secrets 환경변수로 GCP Project ID와 IAM Service Account의 Key를 넣었다.

  • 컨테이너로 만든 도커 이미지에 최신 commit 값을 태그로 사용했고 Google Cloud의 Container Registry에 이미지를 푸쉬했다.

  • Kubernetes kustomize를 이용해 service, deployment 리소스 집합을 구성해서 실행시켰다.

다음은 여기서 사용한 yml 파일들이다.

1. Kubernetes - kustomization.yml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yml
  - service.yml
2. Kubernetes - service.yml
apiVersion: v1
kind: Service
metadata:
  name:  app-demo
spec:
  selector:
    app:  app-demo
  type:  LoadBalancer
  ports:
    - port: 80
      targetPort: 8080
3. Kubernetes - deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-demo
spec:
  selector:
    matchLabels:
      app: app-demo
  replicas: 1
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: app-demo
    spec:
      containers:
        - name: app-demo
          image: gcr.io/PROJECT_ID/IMAGE:TAG
          ports:
            - containerPort: 8080
profile
좋은 습관을 가지고 싶은 평범한 개발자입니다.

0개의 댓글