Skip to main content

Command Palette

Search for a command to run...

GitHub Action을 활용한 AWS ECR 자동배포

사내 IDC Jenkins에서 GitHub Action으로 갈아타기

Published
6 min read
GitHub Action을 활용한 AWS ECR 자동배포

기존 Jenkins 설정을 활용한 배포 과정

Jenkins를 활용한 빌드-배포도 나쁘진 않았다

근데 GitHub Action을 쓰다보니 다신 못 돌아가겠다

Jenkins - Groovy 기반 스크립트는 뭔가 어색하다

  • Jenkins Pipeline Script는 Groovy를 사용함
  • Backend 분들은 Spring Boot 설정 파일에서 사용하는 경우도 있어서 익숙할지 모르겠으나..
  • Spring Boot와 어색한 우리 Frontend 에겐 러닝 커브가 있음
  • 그래도 한줄한줄 보다보면 어떤 느낌으로 흘러가는 지 정도는 파악할 수 있음
    • 뭔가 파이썬스럽기도 하고..
    • check parameter -> git checkout/pull -> docker build -> push to ECR -> GitLab k8s commit

Jenkins on IDC - 그냥 뭔가 불편하다

  • 버전이 너무 낮음

    • GitLab도.. Jenkins도 버전이 오래됨..
    • 플러그인들 붙이기도 어려움
      • 검색해서 뭔가 신박한걸 해보려고 하지만 할 수가 없음
    • UI도 왠지 구림
  • IDC 서버에서 작동

    • CPU 성능이 GitHub Action 서버의 40% 정도 수준임
      • => 빌드 속도에서 차이가 날 수 밖에 없음
    • 가끔 다른 프로젝트들에서 이미 여러개 실행 중인 경우, 대기 큐에 밀려서 끝날때까지 기다려야 함
    • 어쩌다 한번씩 젠킨스 서버가 터져서 배포를 못하기도 함.. 디스크 용량 초과라든지.. 마스터 계정 비번을 못찾는다든지..
Jenkins - IDCGitHub
image.pngimage.png

image.png

출처: https://www.cpubenchmark.net/compare/Intel-Xeon-E5-2673-v4-vs-Intel-Xeon-E5-2620-v3/2888vs2418


GitHub Actions를 활용한 ECR 배포 자동화

  • 현재 ArgoCD의 업데이트 반영 로직은, AWS ECR을 직접 지켜보는 것이 아닌, IDC GitLab k8s 레포지토리를 지켜보도록 되어 있음
    • 이것만 아니면 ECR 배포만 해서 자동으로 배포까지 이어질텐데, 이 부분 때문에 결국 사내망을 한번은 접속해서 commit을 해줘야 함
    • 어차피 QA 서버 확인하려면 사내망 접속하긴 해야 되니깐 뭐..
    • 언제될지 모르겠지만.. 방화벽 문제 해결 되면 이것도 해결 되긴 할 듯..
  • 그래서 위 Jenkins Pipeline에서 Push to ECR까지만 GitHub Action으로 옮기고, 마지막 Tag Update하는 부분만 Jenkins에 남겨둔다

QA 배포 자동화 스크립트

name: AWS EKS Auto-Deploy (QA)

on:
  pull_request:
    branches:
      - 'develop'

jobs:
  build:
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node: [14]

    outputs:
      version: ${{ steps.set-version.outputs.version }}
      tag: ${{ steps.set-version.outputs.tag }}

    steps:
      - name: Git Clone, Checkout
        uses: actions/checkout@v3

      - name: Set up Docker Buildx for Docker Cache
        uses: docker/setup-buildx-action@v2

      - name: Cache Dependencies
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node }}
          cache: 'yarn'

      - name: Check Build Environments
        run: npx envinfo

      - name: Install Dependencies
        run: yarn install --no-progress --emoji=false

      - name: Build App
        run: |
          rm -rf node_modules/.cache
          yarn build

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_QA }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_QA }}
          aws-region: ap-northeast-2

      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      - name: Set ENVs
        id: set-version
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: finance/finance-front/dev
          version: ${{ steps.set-version.outputs.version }}
        run: |
          version=$(jq -r .version package.json)-$(date '+%Y%m%d-%H%M%S')
          echo "::set-output name=version::$version"
          echo "::set-output name=tag::$ECR_REGISTRY/$ECR_REPOSITORY:$version"

      - name: Build, tag, and push image to Amazon ECR
        uses: docker/build-push-action@v3
        with:
          context: .
          push: true
          tags: ${{ steps.set-version.outputs.tag }}
          file: qa.github.Dockerfile

      - name: Check Container Tag
        env:
          version: ${{ steps.set-version.outputs.version }}
        run: echo version $version
  • GitHub Action의 장점 중 하나는 이미 다른 사람들이 만들어놓은 Workflow를 플러그인처럼 갖다 붙일 수 있다는 것
    • 직접 Git Checkout 하고, Node, Docker 환경설정하고 ECR login-push 하는 스크립트를 짤 필요 없이,
    • 그냥 갖다 쓰기만 하고 parameters만 잘 넣어주면 된다
  • 또한 의존성(node_modules) 설치한 것들을 캐싱해서 쓸 수 있다는 것
    • 물론 특정 디스크 용량 초과할 경우 비용 발생할 수 있음
    • actions/setup-node@v3을 활용해 node_modules 디렉토리를 캐싱할 수 있는데, 쌩으로 yarn install 했을 때 1분~1분 30초 정도 소요된다면, 캐싱을 통해 30초 내외로 시간을 단축할 수 있다
    • yarn PnP Zero-installs 설정을 하게되면, 3초 내외로 단축할 수 있다
      • node_modules 디렉토리 전부를 git에 넣어버리는 셈이라..
      • zip 파일들이라 용량도 크게 줄어든다

변수 설정

  • 나름 제일 까다로웠던 (?) 부분
  • jq를 활용한 version tag 생성
    • container tag를 생성할 때, 기존처럼 버전을 직접 입력하는게 아니라 package.json에 있는 버전을 가져와서 자동으로 생성하도록 만들고 싶었음
    • 처음엔 어떻게 해야 될지 몰라서, shell에서 package.json 파일 내용을 출력한 다음 정규식으로 version에 해당하는 값을 가져올 수 있는 방법을 찾아봤음
    • 많은 개발 지식은 물론 쓰레기도 섞여있는 stackOverflow를 보면서 이런저런 삽질을 하다보니 jq라는 라이브러리로 json parsing이 가능하다는 걸 알게 됨
    • version=$(jq -r .version package.json)-$(date '+%Y%m%d-%H%M%S')
      • 이 스크립트로 프로젝트 최상단 package.json에 있는 version 값과 현재 datetime 값을 합쳐 tag 생성
  • outputs을 활용한 전역변수 생성
    • 각 step마다 env를 설정할 수 있는데 이건 그 step에서만 사용할 수 있는 지역변수임
    • 전역변수 env를 선언할 수도 있긴한데, 위 tag처럼 동적으로 생성하는 건 안됨. 상수만 가능한 듯
      • https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#env
    • 그래서 동적으로 전역변수를 선언하려면 outputs를 활용해야 함
    • [전역변수명]: ${{ steps.[stepId].outputs.[변수명] }}
      • jobs.outputs에서 전역변수명들을 선언하고, 어떤 step에서 어떤 output으로 나올 건지 알려준다
    • echo "::set-output name=[변수명]::$version"
      • 해당 step에서 변수명에 해당하는 값을 대입해준다

Docker Build; Image 사이즈 최적화

  • GitHub Action에서 사용할 수 있는 OS는 Ubuntu, MacOS, Windows 3가지임

    • https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
  • GitHub Action에서 빌드하고, 빌드 결과물, 최소한의 필요한 의존성만 Docker로 Copy해서 넘김

    • Docker base OS가 alpine이긴한데, Ubuntu에서 빌드해도 문제 안됨
    • 문제되는 경우는 Window - Unix 계열 end of line 문자가 달라서 빌드 파일에 영향을 미치거나,
    • CPU architecture가 intel - arm 달라서 Esbuild, Parcel (SWC), node-sass 등 일부 빌드 관련 패키지가 아예 달라지는 경우 등
      • Rust는 하루 빨리 M1을 지원하라..!!
    • 그거 말고는 아직 못봤음, 암튼 동일한 intel CPU면 Ubuntu - Alpine 차이 정도는 문제 안됨
  • GitHub Action도 Container 환경(아마도 99% 확률로 Docker?)에서 실행되는 거라, 기존처럼 Docker-in-Docker 방식으로 빌드하면 속도가 더 느려질 듯

  • 이때 Docker Cache는 굳이 쓸 필요가 없음

Container Tag

  • 여기서 만든 Docker Container Tag를 출력하고,
  • 이 tag를 복사해서, 사내 Jenkins에서 tag만 업데이트하는 pipeline을 실행시킨다

조심해야 할 것

GitHub Action 월간 사용량

image.png

출처: https://docs.github.com/en/billing/managing-billing-for-github-actions/about-billing-for-github-actions

  • Pro/Team Plan 월 3,000분까지 무료, 그 이후부터는 분당 과금됨
  • ChartIQ 패키지 추가하면서 빌드 한번에 10분씩 걸리는데..
    • 월 20일 출근 X 하루 15번 빌드 X 빌드 1회당 10분 => 3000분
    • 모바일 파트쪽 빌드/테스트하는 것까지 포함하면 월간 사용량이 꽤 될 것 같아서 조금 걱정됨..
    • 사실 걱정 보다는 나중에 시간 줄이라고(돈 아끼라고) 얘기 나올 거 같아서 귀찮아질 것 같음
    • => PR 만들어놓고 1 commit - 1 push 보다는 commit 어느정도 쌓인 후 push 하는게 좋을듯..

AWS ECR 비용 문제

  • 다행히도 ECR inbound 네트워크 비용은 발생하지 않는 듯 image.png

    출처: https://aws.amazon.com/ko/ecr/pricing/

    • 자동배포를 하면서 QA 서버 빌드는 PR / push 할 때마다 Docker 이미지가 GitHub -> AWS ECR로 날아감
    • 이때 프로젝트에 따라 Docker image 크기는 적게는 300mb 많게는 500 mb 내외
    • 당장은 데이터 수신 비용이 $0 여서 이건 문제가 안됨
  • 만약 나중에 AWS에서 돈을 받겠다고 한다면?

    • AWS CodeBuild, CodeDeploy 도입을 고려해볼 수 있을 듯
      • AWS 동일 region 내 네트워크 비용은 발생하지 않으므로..
      • 근데 CodeBuild는 월간 무료 사용량 100분 밖에 안되기 때문에, 비용 비교를 잘 해봐야 할 듯

좀더 개선해 볼 것

Cron으로 정기 배포 (Xvezda님 의견 👍)

  • Jenkins에서 어쨌든 k8s 레포지토리 태깅 커밋은 한번 해줘야 함
  • QA 서버 정도는 5분~10분 단위로 Cron 걸어서 자동 배포해줘도 크게 문제 없을 듯
  • Cron은 Jenkins에서 해도 되고, GitLab CI에서 해도 됨
    • 개인적으로는 이것도 GitLab CI가 좀더 편함.. (서버시간 엉망인것만 빼고..)
  • Prod 서버는 그래도 QA 서버에서 충분히 확인하고 수동으로 배포하는게 좋지 않을까..

페이지 마다 모노레포 패키지로 나눠서 빌드 시간을 줄여보자 (somedaycode님 의견 👍)

  • turborepo 적용해 패키지 병렬 빌드 가능하도록 함
  • domain / BE 패키지는 5~15초 내외 소요되고 있어서, 병렬 빌드하는 이점이 크게 없음
  • 문제는 FE 빌드시간인데, Chart 빌드 => FE:CSR 빌드 => FE:SSR 빌드 순서가 반드시(;;) 지켜져야하는 상황
    • ChartIQ 라이브러리 사이즈가 꽤 되다보니, Chart~FE 빌드만 7~8분 정도 소요됨
  • Chart 패키지 쓰는 페이지(상세페이지) - 나머지 페이지 나눠서 병렬 빌드하면 이 문제가 좀 해결될 수도?
    • 좀 큰 공사가 될수도 있어서 겁남..
    • Next/Nuxt 쓰면 알아서 해결될 문제긴 한데.. 아무튼..

references

resources

More from this blog

Kimi Agentic Slides + Nano Banana로 4살 아들 맞춤 동화 만들기

Kimi Agentic Slides 지난주 금요일 (11. 28.) Kimi에서 Agentic Slide를 런칭하면서, 48시간 무제한 슬라이드 생성 이벤트를 진행했다. Nano Banana Pro가 워낙 강렬해서 이건 또 뭐가 다른지 써보고 있는 와중에, 아들이 옆방에서 징징대는 소리가 들렸고, 이걸로 동화책을 만들어봐도 되겠다는 생각이 뇌리를 스쳤다. 아들이 워낙 자동차를 좋아하고, 요즘 육아 이슈가 사회성을 길러주는 거라, 처음엔 사회성...

Dec 1, 20252 min read28
Kimi Agentic Slides + Nano Banana로 4살 아들 맞춤 동화 만들기

[발번역] Deno에 package.json 지원을 추가한 이유

원문: Why We Added package.json Support to Deno 내맘대로 세줄 요약 Node.js package.json 구린거 맞음. 이거에 대한 생각 여전함 근데 HTTP URL 쓰다보니 더 복잡하고 지저분해진 것도 맞음 이것저것 해보다가 뭐 어쩔수없이 이번에 package.json 지원하긴 했는데, 앞으로 deno: URL 지원할 거임. 기대해보셈 Deno 최신 버전 (v1.31, https://deno.com...

Mar 21, 20235 min read199

Wii-World

28 posts