구리

[CI/CD] Github Actions을 통한 CI/CD 구축기 본문

DevOps

[CI/CD] Github Actions을 통한 CI/CD 구축기

guriguriguri 2024. 12. 31. 08:53

회사 서비스는 기존 Rancher에서 배포했었는데 버전이 낮아 CI/CD 적용이 불가했다. 그러다 k8s 환경으로 옮기면서 CI/CD를 구축할 수 있게 되었다.

요구사항은 다음과 같다.

  • Github Actions를 통해 dev, stage 환경에서의 CI/CD를 구축한다. (prod 환경의 k8s는 타팀이 관리하고 있었기에 배포 요청을 해야했었어서 prod는 고려하지 않았다.)
  • 빌드 시점에 비밀번호 관리 툴인 1password에 저장된 환경 변수를 주입한다.
  • Argo CD를 사용하고 있었기에 Docker Hub에 이미지 push 후 Argo CD repo에 릴리즈 이미지 버전을 변경하는 Commit, Push를 진행한다.
  • dev, stage CI/CD 외 테스트 브랜치를 위한 CI/CD가 필요하다.

그리하여 아래와 같이 CI/CD를 구축하게 되었다.

name: Dev/Stage Build and Push

on:
  push:
    branches: ['feature/prerelease_*']

jobs:
  sonarqube-scan:
    runs-on: ubuntu-latest
    steps:
      - name: 1. Checkout source code
        uses: actions/checkout@v4

      - name: 2. SonarQube Scan
        uses: SonarSource/sonarqube-scan-action@v4.1.0
        env:
          SONAR_TOKEN: ${{ secrets에 저장된 소나큐브 토큰 }}
          SONAR_HOST_URL: ${{ secrets에 저장된 소나큐브 URL }}
        with:
          args: >
            -Dsonar.projectKey=프로젝트명
            -Dsonar.sources=.

  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - name: 1. Checkout source code
        uses: actions/checkout@v4

      - name: 2. Extract image version
        run: |
          IMAGE_NAME="dockerhub에 push할 docker 이미지명"
          VERSION=$(grep "$IMAGE_NAME" docker-compose.yml | grep "image" | sed -E 's/.*:([^:"]+)["]*$/\1/')
          echo "VERSION=$VERSION" >> $GITHUB_ENV
          echo "Extracted Image Version: $VERSION"

      - name: 3. Login to DockerHub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets에 저장된 dockerhub user name }}
          password: ${{ secrets에 저장된 dockerhub token }}

      - name: 4. Install 1Password CLI
        uses: 1password/install-cli-action@v1

      - name: 5. Get 1Password Secrets
        run: op read op://금고명/파일명/notesPlain > .env
        env:
          OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets에 저장된 1password token }}

      - name: 6. Display .env File Content (Masked)
        run: |
          echo "Contents of .env file (partially masked):"
          sed -E 's/(=.*)/=****/g' .env | tee .env.masked
          cat .env.masked

      - name: 7. Set up Docker Buildx for docker/bake-action
        uses: docker/setup-buildx-action@v3 

      - name: 8. Docker compose build, push
        uses: docker/bake-action@v5
        with:
          push: true
          set: |
            *.cache-from=type=gha
            *.cache-to=type=gha,mode=max

      - name: 9. Remove prev repository clone if it exists
        run: |
          if [ -d 프로젝트명 ]; then
            rm -rf 프로젝트명 # '프로젝트명' # 디렉토리가 있으면 삭제
            echo 'Removed existing 프로젝트명 directory' # 삭제 후 메시지 출력
          fi

      - name: 10. Clone the CI-CD repository
        run: |
          git clone --depth 1 --branch main https://x-access-token:${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/github repo 경로
          echo 'Clone completed' # 클론 완료 메시지 출력

      - name: 11. Commit and push changes to CI-CD repository
        run: |
          cd CI-CD dev repo 경로
          git config --global user.name '작성자 이름' # 커밋 작성자 이름 설정
          git config --global user.email '작성자 이메일' # 커밋 작성자 이메일 설정

          sed -i '/image: docker 이미지명$/!b;n;s/tag: .*/tag: ${{ env.VERSION }}/' values.yaml
          git add . # 변경 사항 스테이징

          # CI-CD stage repo 경로로 이동
          cd ../../../
          cd ./CI-CD stage repo 경로

          sed -i '/image: docker 이미지명$/!b;n;s/tag: .*/tag: ${{ env.VERSION }}/' values.yaml
          git add . # 변경 사항 스테이징
          echo 'Changes staged for commit' # 스테이징 완료 메시지

          git commit --allow-empty -m '[BOT] 프로젝트명 ${{ env.VERSION }} 이미지 Sync For dev/stage'
          echo 'Commit completed' # 커밋 완료 메시지 출력

          git push origin main # 변경 사항을 원격 리포지토리로 푸시
          echo 'Push completed' # 푸시 완료 메시지 출력

job : sonarqube-scan

소나 큐브는 정적분석 도구로 런타임 환경을 구성하지 않고도 소스 코드를 분석할 수 있다. 회사에서는 소스 코드 분석을 위해 소나 큐브를 사용하고 있다.

따라서 Github Actions을 통해 이미지 빌드 전에 소스 코드 분석 자동화를 진행했다.

job : build-and-push

이미지 빌드, 푸시 그리고 ArgoCD와 연동된 repo에 이미지 버전 업데이트 커밋, 푸시 단계로 이뤄져있으며 아래 step별로 구성했다.

1-2. Extract image version

기존 로컬 빌드시 docker compose를 이용했기에 docker-compose.yml에 있던 이미지 버전을 추출해 환경변수로 설정한다.

3-6. Set Env

로컬에서 빌드시 .env 파일에 접근할 수 있었지만 .env 파일은 대체로 보안상의 이유로 github에 푸시되지 않는다. 따라서 Github Actions를 통해 패키지를 빌드할때 .env 환경 변수 파일에 접근할 수 없다.

회사에서는 비밀번호 관리 솔루션인 1password를 사용하고 있으며 해당 도구를 통해 각종 환경변수를 관리 및 공유하고 있다. 따라서 1password에 있는 환경변수를 가져와 .env 파일을 생성한다.

Github Actions Secrets을 통해 환경변수를 주입하는 방법도 있겠지만 환경변수가 많아지면 관리가 어려워질 것 같아 해당 방식은 사용하지 않았다.

환경 변수 주입 후 제대로 설정되었는지 확인 차원에서 마스킹 처리된 환경변수를 출력하게끔 했다.

7-8. Docker Image Build & Push

해당 단계에서는 이미지 빌드 및 DockerHub에 빌드 이미지를 푸시한다.

docker/bake-action를 사용해 기존 docker-compose를 이용한 빌드 방식을 그대로 유지했다. 이때 buildx라는 CLI 플러그인을 사용하는데 Github Actions에 직접 캐시를 저장시키는 Github Cache API를 제공하기에 해당 액션을 선택했다. (기본적으로 Github Actions의 런타임 환경은 매번 초기화되기에 별도의 설정이 없으면 도커 캐싱이 이뤄지지 않는다.)

참고로 Github Cache는 실험 단계로 7일 후에는 삭제되며 10GB 용량 제한이 있다. 만약 용량 초과시 가장 오래된 캐시부터 삭제된다.

보통 개발이 모두 끝난 후 QA 기간(약 1주일)중에 배포를 많이 하기에 부족한 용량은 아닌 것 같다. 부족하다 싶으면 그때 다른 대안을 찾으면 될 것 같다.

9-11. CI-CD Repo commit & push

이미지 빌드 및 푸시가 끝나면 ArgoCD가 연동된 리포지토리로 이동해 이미지 버전을 변경하는 커밋 및 푸시를 진행한다. 이때 원격 푸시를 위해서는 Personal Access Token이 필요하니 미리 발급 후 Actions secrets에 등록해야 한다.

위 workflow는 배포 브랜치에 push를 하게 되면 자동으로 배포가 되기에 특정 환경(dev or stage)에 테스트용으로 배포를 할 수 없다.
따라서 수동으로 workflow를 실행할 수 있는 workflow_dispatch 기능을 이용해 특정 브랜치 및 환경을 선택해 수동으로 배포할 수 있도록 구축했다.

name: Dev or Stage 수동 배포

on:
  workflow_dispatch:
    inputs:
      version:
        description: "version (예: 1.0.0 or 1.0.17)"
        required: true
        default: "latest"
      env:
        description: "dev or stage 환경 선택"
        required: true
        default: "dev"
        type: choice
        options:
          - dev
          - stage
      branch:
        description: "Build 할 Branch"
        required: true
        default: "develop"

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    steps:
      - name: 1. Checkout code
        uses: actions/checkout@v4
        with:
          ref: ${{ github.event.inputs.branch }} # 선택한 브랜치 체크아웃

     # 이미지 빌드 및 docker hub push 과정 생략

      - name: 10. Commit and push changes to CI-CD repository for dev environment
        if: ${{ github.event.inputs.env == 'dev' }}
        run: 생략

      - name: 10. Commit and push changes to CI-CD repository for stage environment
        if: ${{ github.event.inputs.env == 'stage' }}
        run: 생략

위 workflow를 실행하면 Github Actions에서 아래와 같이 선택할 수 있는 드롭다운이 보여지게 된다.

workflow 완료 여부에 대한 slack 알림 추가 등 수정할 부분이 좀 더 있는 것 같다. 현재는 일단 구축만 해놓은 상황이라 QA 기간때 사용하면서 단점들을 보완해야겠다.