[CI/CD] 모델과 코드 배포


ML/AI 서비스를 제공하기 위해서 해당 모델과 추론 코드 등을 Production 서버에 배포하게 된다. 그러나 이 때 단순하게 배포하는 게 아닌 운영 안정성과 속도 등 사용자들의 니즈를 충족시키기 위한 여러가지 고려사항 혹은 개념들이 필요하다. 이 포스트에서 CI/CD 에 대해 알아보자.

환경 분리와 배포

  • 소프트웨어 개발에서 의미하는 환경의 정의와 배포에 대해 알아보자. 먼저 개발 환경은 아래와 같이 구분할 수 있다.
    • 로컬 환경 : 처음 개발할 때는 각자의 로컬 컴퓨터에서 개발하게 된다. 이 때 각자의 환경을 통일시키기 위해 Docker 등을 사용한다. 또한 패키지 의존성 문제를 완화하기 위해서 poetry 등을 사용한다.
    • Dev 환경 : 의미 그대로 개발 환경이다. 로컬에서 개발한 기능을 테스트할 수 있는 환경으로, 테스트 서버를 띄우고 거기서 개발하게 된다.
    • Staging 서버 : Production 환경에 배포하기 전에 운영하거나 보안, 성능 등을 측정하는 환경이다.
    • Production. 서버 : 실제 서비스를 운영하는 환경, 서버다.
  • 이와 같이 개발환경을 나누는 이유는 아래와 같다.
    • 실제 운영 중인 서비스에 장애가 발생하는 것을 피해야 한다.
    • 만약 Dev = Staging = Production 인 경우에 소스 코드를 수정, 저장하면 바로 반영된다.
    • 이 때 장애가 나면 바로 운영 서버에서 장애가 나게 된다. 의도하지 않게 자동 저장이 될 수도 있고, 업데이트가 될 수도 있다. 따라서 개발 환경을 꼭 나누게 된다.
  • 현업 개발 프로세스에서 환경(dev, staging, prod 등)을 분리하는 이유는 검증되지 않은 변경 사항이 유저에게 전달되어 예상치 못한 오류가 발생하는 것을 방지하기 위해서다.
    • 환경을 분리함으로써 각각의 환경에서 개발된 소프트웨어가 사용자에게 배포되기 전에 테스트되고 검증될 수 있다.
    • 특히, Production 환경에서 발생할 수 있는 예상치 못한 오류나 문제를 사전에 파악하고 수정함으로써 사용자에게 불편을 줄일 수 있다.
    • 따라서 의도하지 않은 변경 사항이 유저에게 전달되어 예상치 못한 오류가 발생하는 것을 방지하기 위해 각각의 환경을 분리하는 것이 일반적이다.

Git Flow

  • 현업에서의 github 을 활용한 개발 프로세스다.
  • main branch (운영용), staging branch (staging 서버용), dev branch, feature/기능 이름 branch 가 존재한다.
  • feature branch 에서 dev branch 로 PR 과 review 를 넣는다. 그러면 dev branch 는 staging branch 로 PR, review 를 보낸다. 또 staging 에서는 main 으로 PR, review 를 보낸다.
  • staging 서버는 없을 수도 있다. 또한 feature/기능 이름 branch 도 다를 수 있다. github 에서 branch 를 따는 이름은 다 다들 수 있다.
  • 그러나 main 이 production(운영용), staging 이 staging, dev 가 개발 환경을 뜻하는 것은 변하지 않는다.

    Untitled

  • 위 그림처럼 각각의 branch 를 서버와 연결시킨다. feature branch 는 보통 로컬에서 개발하게 된다.

CI/CD

  • 서버에 코드를 보내는 것과 반복적으로 진행할 Test 를 어떻게 실행해야 할까?
  • feature branch 에서 Dev branch 로 PR, review 를 날리고 Merge 한다. 이를 수동으로 하면 로컬에서 git pull 을 하게 된다.
  • 그리고 Test 후 FTP 또는 scp 로 Dev 서버에 파일을 전송하여 코드를 배포하게 된다. 이는 매우 번거로운 일이다.

    Untitled

  • 이에 따라 CI 혹은 CD 구축의 필요성이 증가했다.
  • Continuous Integration(CI), 지속적 통합
    • 새롭게 작성한 코드의 변경 사항이 Build 또는 Test 할 때 test case 에서 통과되는지를 확인한다.
    • CI 는 지속적으로 코드의 품질 관리를 위해 사용하는 과정이다.
    • 일반적으로 10 명의 개발자가 코드를 수정했다면 모두 CI 프로세스를 진행한다.
  • Continuous Deploy/Delivery(CD), 지속적 배포
    • 작성한 코드가 항상 신뢰 가능한 상태가 되면 자동으로 배포될 수 있도록 하는 과정이다. 따라서 CI 이후 CD 를 진행한다.
    • dev / staging / main branch 에 Merge 가 될 경우, 해당 branch 가 바라보고 있는 서버에 코드를 자동으로 전달한다. 즉 배포하는 것이다.
    • 각 branch 에서 Merge 됐을 때, git pull 을 하고 이를 FTP, scp 명령어로 서버에 보내는 과정을 수동으로 하지 않고, 자동으로 하는 과정이다.
  • 정리하면, CI 는 빌드/테스트 자동화에 해당하고, CD 는 배포 자동화에 해당한다.
  • CI 에서는 데이터 검증 과정도 가능하다. 데이터가 어떤 상태인지 확인하도록 만들 수 있다.
  • CI 에서 테스트는 Python 의 pytest 를 이용한다. 테스트 코드를 작성하여 github 에서 merge 가 되는 순간 테스트가 돌아가도록 만들고, 모두가 pass 되면 merge 할 수 있도록 설정하는 것이다.
  • local 에서 개발하고 main 으로 merge 시 production 서버에 코드를 배포하는 것도 가능하다.
  • 개인 프로젝트에서는 서버를 두지 않는 경우도 존재한다.
    • Dev, Staging, Prod 모두 다 서버로, 모두 비용이다.
    • 이 서버들은 모두 다 똑같은 성능이 아니라, Prod 가 실제로 서비스를 받기 때문에 용량이 제일 커야한다.
    • Dev 와 Staging 은 유저가 실제로 쓰지 않아 트래픽이 굉장히 작다.
    • 따라서 개인 프로젝트에서는 로컬에서 개발하고 Prod 보내는 경우도 많다. 그럼에도 branch 를 나눠서 작업하는 습관을 들이면 더 좋은 협업 능력을 함양할 수 있다.

ML 모델 배포 시 주의할 점

  • Docker 이미지
    • 대부분 Docker 를 이용하여 배포하게 된다. 이 때 Image 사이즈가 큰 편이기 때문에 관리가 필요하다.
    • Multi stage build와 같은 테크닉을 사용해서 최대한 Docker Image 의 사이즈를 줄여야 한다. 이에 대해서는 해당 포스트에서 정리했다.
    • 또한 호스트 머신의 디스크 용량도 관리가 필요하다. Docker 를 띄우면 CLI 에 로그가 많이 남는다. 이 로그들은 어딘가에 저장되고 있을 가능성이 크다. 이 로그를 주기적으로 삭제하거나 AWS S3 와 같은 클라우드로 보낼 수 있다.
  • 모델 버저닝
    • 모델 코드에 대한 확실한 버저닝이 필요하다.
    • 어떤 버전의 모델이 현재 배포 중이고, 과거에는 어떤 버전으로 배포되었는지 이런 히스토리가 관리되어야 한다.
    • 우리가 만든 모델에 대해 롤백이 필요하다면 어떤 버전으로 재배포해야 하는지 이런 것들도 기록되어야 한다.
    • 모델의 버전 별 특징을 쉽게 알아볼 수 있어야 한다. 1.0.1, 1.2.0 이런 버전들이 무엇을 의미하는지 기록이 되어 있어야, 모델 버저닝이 잘 되어서 모델을 운영할 때 이슈에 빠르게 대응할 수 있다.
  • 모델 아티팩트
    • 모델을 저장할 때는 Docker Image 에 저장하는 것보다 S3 와 같은 Object Storage 에 저장하는 것을 권장한다.
    • 일반적으로 딥러닝 모델은 엄청 무거운데, 이를 Image 에 저장하게 되면 그만큼 사이즈가 커져 이슈가 발생할 수 있다.
    • 모델을 로드하고 싶으면, 클라우드 스토리지나 ML flow 같은 모델 저장소, 모델 레지스트리에 저장하고 불러오는 것이 좋다. 이렇게 되면 모델을 불러오는 로직만 있으면 된다.
    • 이 떄 모델 버전과 아티팩트의 버전이 다른 경우도 있으므로 메타 정보를 확인해야 한다. 이 때 효과적인 도구가 MLflow다.
      • MLflow 를 잘 활용하면 기록, 메타데이터 기반으로 모델을 사용할 수 있다.
  • 파일 권한 관리(VM 인스턴스, Object Storage 등)
    • VM 인스턴스에서 pth 파일을 읽지 못한다면 정상 실행이 불가능하다.
    • 따라서 VM 인스턴스에서 Object Storage 에 접근할 수 있도록 서비스 계정을 만들고 설정해줘야 한다.
    • Object Storage 는 다른 사용자가 버킷을 삭제하면 롤백이 불가능하기 때문에 잘 관리해줘야 한다. 따라서 삭제 권한을 제어하는 것이 필요하다.
    • 실제로 삭제 권한은 회사에서 일반 직원에게 잘 주지 않는다. 프로젝트 할 때 이런 것도 생각해야 한다.

Github Action

  • Github Action 은 배포하는 과정에서 사용할 대표적인 CI/CD 도구다. 회사, 개인 프로젝트 어디서든 사용할 수 있다.
  • Github 에서 출시한 기능으로, 소프트웨어의 Workflow 자동화를 도와주는 도구다. 아래와 같이 여러 Workflow 를 지원한다.

    Untitled

  • Node.js 등 다양한 언어별 Workflow 를 지원한다. 그리고 위와 같이 기본적인 템플릿이 제공되기 때문에, 이를 스스로의 스타일로 수정하면 된다.
  • 그리고 Github Action 을 이용해서 아래와 같은 것들이 가능하다.
  • Test Code
    • 특정 함수의 return 값이 어떻게 나오는지 확인하는 Test Code 를 자동화할 수 있다.
    • 특정 변수의 타입이 원하는 타입이 맞는지도 test 할 수 있다.
    • Unit Test, End to End Test 도 가능하다.
  • 배포
    • Prod, Staging, Dev 서버에 코드를 배포하는 것을 자동화할 수 있다.

      Untitled

    • 배포는 Image 혹은 파일을 서버로 보내는 것이라 생각하면 된다.
    • 파일의 경우 FTP 또는 scp 로 파일 전송을 할 수도 있고, Docker Image 를 Push 하는 방법 등이 있다.
  • Python, Shell 스크립트 실행
    • Github Repo 에 저장된 스크립트를 일정 주기를 가지고 실행할 수도 있다.
    • 이에 따라 일정 주기로 체크해야 하는 것들은 github action 으로 수행하기도 한다. batch serving 에서 봤던 linux 의 crontab 의 대용으로 쓰기도 한다.
    • 그러나 시간이 정상적으로 동작하지 않을 때도 있다. 따라서 batch serving 을 할 때는 github action 이 좋지 않을 수 있다. 그러나 사이드 프로젝트나 개인 프로젝트에서 cron 으로 주기적인 작업을 하고 싶을 때 github action 을 사용하는 것은 좋은 방법이다.
    • 데이터 수집을 주기적으로 해야할 경우에도 활용 가능하며, Issue 에 기록하는 것도 만들 수 있다.

      Untitled

    • 위와 같이 setup-python 과 같은 미리 만들어진 것을 이용해서 Python 의 환경 세팅도 가능하다.
  • Github Tag, Release 자동으로 설정
    • main branch 에 Merge 될 경우에 특정 작업을 실행하도록 만들 수 있다.
    • 예를 들어 새로 Merge 된 경우, 기존 버전에서 버전 Up(1.0.1 → 1.0.2) 하는 것을 자동화 한다.
    • 새로운 branch 생성 시 특정 작업을 실행하는 것도 가능하다. 마찬가지로 새로운 branch 를 만들고 remote 에 push 하면 새로운 작업이 실행되는 것도 가능하다.
  • 이처럼 개발자라면 github action 은 범용적으로 많이 쓸 수 있는 도구다. 위에서 소개한 것 외에도 다양한 workflow 를 만들 수 있다. 이에 따라 다양한 사용자들이 만든 workflow 템플릿이 많이 공유되어 있다.
  • 원하는 기능이 있는 경우 “<기능> github action" 등으로 검색하면 어렵지 않게 찾아볼 수 있다.

    Untitled

github action pricing

  • public repo 는 무료지만, private repo 는 아래의 조건을 따른다.

    Untitled

github action 제약조건

  • github repo 당 workflow 는 최대 20 개까지 등록할 수 있다. 또한 많은 작업들을 하나의 repo 에서 다 처리하기는 어렵다.
  • workflow 에 존재하는 job(실행)은 최대 6시간 동안 진행할 수 있으며, 초과시 자동으로 중지된다. 따라서 계속 뭔가를 돌리는 작업은 어렵다.
  • 또한 동시에 실행할 수 있는 job 에도 제한이 존재한다. 즉 계속 무료는 아니다.

github action 사용 방식

  • github action 을 사용할 때는 코드 작업 $\rightarrow$ github action 으로 무엇을 할 것인지 생각 $\rightarrow$ 사용할 workflow 정의 $\rightarrow$ 정상 작동 확인의 방식으로 사용한다.
  • workflow 정의할 때는 github action 에 workflow 파일을 올리고 merge 될 때 trigger 조건 등을 설정해서 계속 시도해보는 것.

Github Action Core

  • github action 의 핵심개념으로는 workflow, event, job, step, action, runner 이 있다.
  • 아래와 같은 YAML 파일을 repo 내 .github/workflows/ 에 위치시킨다.

    name: Github Actions Demo
    on: [push]
    jobs:
      Explore-Github-Actions:
        runs-on: ubuntu-latest
        steps:
          - run: echo "The job was automatically triggered by a & event."
          - run: echo "This job is now running on a $ server hosted by Github."
          - run: echo "The name of your branch is $ and your repository is $."
          - name: Check out repository code
            uses: actions/checkout@v2
          - run: echo "The $ repository has been cloned to the runner."
          - run: echo "The workflow is now ready to test your code on the runner."
          - name: List files in the repository
            run: |
              ls $
          - run: echo "This job's status is $."
    
  • Workflow
    • 여러 job 으로 구성되고 event 로 trigger(실행)되는 자동화된 process 다.
    • workflow 하나에 job 1, 2, 3 등 여러 job 이 생길 수 있다.
    • 즉 github action 에서의 최상위 개념이다. 이러한 workflow 파일은 YAML 파일로 작성되고, github repo 의 .github/workflows 폴더에 저장된다.
    • 다른 오픈소스를 봤을 때 위 폴더가 있으면 github action 코드가 있는 것이다.
  • Event
    • workflow 를 trigger 하는 특정 활동이나 규칙을 뜻한다.
      • 특정 branch 로 push 하는 경우
      • 특정 branch 로 pull request 하는 경우
      • 특정 시간대에 반복(Cron)하는 경우
    • 위 예제에서 Event 는 on [push] 라고 되어 있다. 이는 push 될 때 돌아가는 workflow 라는 것이다. 여기에 branch 를 직접 기입할 수도 있다.
  • Jobs
    • Runner 에서 실행되는 Steps 의 조합이다.
    • 여러 job 이 있는 경우 병렬로 실행하며, 순차적으로도 실행 가능하다.
    • 다른 job 에 의존 관계를 가질 수 있다. 예를 들어 A job 이 success 된 후 B job 을 실행하도록 할 수 있다.
  • Steps
    • job 에서 실행되는 개별 작업이다.
    • action 을 실행하거나 쉘 커맨드를 실행한다.
    • 하나의 job 안에서는 데이터를 공유할 수 있다.
    • job 안에 step 이 있다.
  • Actions
    • job 을 생성하기 위해 여러 step 을 묶은 개념으로, workflow 의 제일 작은 단위이며 재사용이 가능한 컴포넌트다.
    • uses: actions/checkout@v2 는 actions 의 checkout v2 를 사용하겠다 라는 것이다.
    • 개인적으로 action 을 만들수도 있고, marketplace 의 action 을 가져다가 사용할 수도 있다.
    • uses 다른 만들어진 특정 action 을 가져와서 템플릿처럼 쓰겠다는 것을 의미한다.
  • Runner
    • github action 도 일종의 서버에서 실행되는 개념이다. Runner 는 workflow 가 실행될 서버를 의미한다.
    • Github-hosted Runner 는 Github 에서 유지하고 관리하며 요금제에 무료/유료 시간을 사용할 수 있다. 여기서 vCPU2, memory 7GB, Storage 14GB 와 같은 성능을 지정해줄 수 있다. heavy 한 작업은 어렵다.
    • Self-hosted Runner 는 직접 서버를 호스팅해서 사용하는 방법이다.
      • 로컬 컴퓨터를 이용해서 github action 이 동작할 서버를 호스팅하는 것으로, github action 을 사용하고자 하는 repo 에서 Settings - Actions - Runners 에서 지정해줄 수 있다.
    • 이처럼 Github Action 의 서버를 사용할 수도 있고, github action 을 내 컴퓨터에서 하고 싶으면 내 서버를 직접 호스팅해서 사용할 수 있다.

Github Action 예제1

  • 먼저 github repo 를 새로 생성한다.
  • 이후 github action 을 사용하도록 설정해줘야 한다. 특정한 곳에서는 사용이 되지 않는다.
  • Add file - Create new file 로 Python 파일을 하나 만들어준다. hello-world.py 를 만들어보자.
  • 해당 github repo 에서 상단의 Action 을 클릭한다.
  • 맨 하단에 Continuous Integration 을 클릭한다.
  • 검색창에 “python application” 검색 후 “Configure” 을 클릭한다. 그러면 기본적인 파일 형태가 불러와진다.
  • 이제 해당 repo ./github/workflows/python-app.yml 과 같이 템플릿으로 아래와 같은 파일이 생성된다.

    on:
      push:
        branches: [ main ]
      prull_requests:
        branches: [ main ]
    
    jobs:
      build:
    
        runs-on: ubuntu-latest
    
        steps:
        - uses: actions/checkout@v4
        - name: Set up Python 3.10
          uses: actions/setup-python@v3
          with:
            python-version: "3.10"
        - name: Install dependencies
          run: |
            python -m pip install --upgrade pip
            pip install flake8 pytest
            if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
        - name: Lint with flake8
          run: |
            # stop the build if there are Python syntax errors or undefined names
            flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
            # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
            flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
        - name: Gihub Action Demo
          run: |
            python hello-world.py
    
  • 맨 마지막 run 안에 python hello-world.py 를 실행하면 된다. 이제 해당 repo 에 커밋 후 push 하게 되면 Actions 에 노란색 동그라미가 나오게 된다. 이는 작업중이라는 의미다. 이를 클릭하면 디테일을 볼 수 있다.

    Untitled해당 블로그도 github action 이 적용되어 있다.

  • 동그라미가 초록색으로 바뀌면 Success 를 의미하고, 마찬가지로 클릭하면 로그를 확인할 수 있다.
  • 우측에 Re-run all jobs 를 누르면 재실행도 가능하다.
  • 정의한 Step 별로 로그를 확인할 수 있다. 오류가 발생하면 특정 부분만 클릭하면 된다.
  • 이제 위 YAML 파일을 분석해보자.
    • on
      • Event 와 관련되며, 언제 workflow 가 실행될 것인가를 의미한다.
      • 위 예제는 main branch 에 푸시될 때 실행하기 혹은 main branch 에 PR 이 될 때 실행하겠다는 것을 의미한다.
    • jobs
      • jobs 를 정의한다. 위 예제에서 build 는 해당 job 의 이름이다. 로그를 보면 jobs 에 build 라고 나오는 것을 볼 수 있다. 물론 이름을 바꿀수도 있다.

        Untitled

    • runs-on
      • 어떤 환경에서 실행할 것인가를 뜻한다. Github-hosted Runner, Self-hosted Runner 모두 가능하다. 이 예제에서는 github action 이 유지관리하는 ubuntu 를 사용한다.
      • Self-hosted Runner 를 사용하려면 Runner 를 로컬 서버로 만들어준 후, self-hosted 라고 작성하면 된다.
    • uses
      • 사용할 github action 이다.
      • uses 가 없는 경우에는 run 에 작성된 쉘 커맨드를 실행한다.
    • name
      • step 의 이름을 뜻한다.
  • github action 은 보통 템플릿으로 다른 사용자들이 만들어둔 것이 있으니 그걸 보면서 다양한 usecase 를 파악하고 적용하는 것을 추천한다. 이를 처음부터 암기해서 바닥부터 쓰는 경우는 거의 없다.
  • 템플릿이나 마켓플레이스를 보고 uses 에 기입해주면 된다.

Github Action 예제2

  • 다른 예제를 통해 현업의 관점에서 github action 을 이용한 CI/CD 를 이해해보자.
  • 모델 Image 를 준비한다. 즉 코드를 배포하기 전에 모델(.pt, .pkl, .onnx 등)도 준비해서 서빙할 환경을 만들어준다. 서빙할 모델 코드, Online Serving 을 위한 API Endpoint 정의 코드, 컨테이너화를 위한 의존성 정의(requirements.txt)가 필요하다.
  • Dockerfile 은 아래와 같이 작성할 수 있다.

    FROM python:3.8
    
    WORKDIR /code
    COPY ./requirements.txt /code/requirements.txt
    RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
    COPY src /code/app
    
    CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
    
  • Docker Image Repository 는 수많은 Docker Image 들을 저장하는 저장소다.
    • 자체적으로 Image 의 이름과 버전 등을 저장하고 불러올 수 있다.
    • Docker 의 공식 저장소는 Dockerhub 다. 또는 AWS ECR 과 같은 클라우드의 저장소를 사용할 수 있다.
  • 해당 예제에서는 GCP 의 Google Cloud Artifact Registry 를 사용해보자. GCP 의 이미지 저장소로서, 아티팩트를 저장할 수 있다. 즉 Docker Image, npm 패키지 등을 저장하고 관리할 수 있다.

    Untitled

  • 위 그림과 같이 gcloud CLI 설치 후 인증을 설정해주면 gcloud 에서 Docker 설정을 사용할 수 있게 된다.
  • Artifact Registry 에 Docker Image Push 과정은 아래와 같다.
    • Docker Image Build
    • 태그 설정
    • Image Push
  • 아래 그림과 같이 GCP artifacts 웹에 접속 후, Project id 를 확인한다. 이후 “create repository” 를 클릭한다.

    Untitled

  • 이름을 지정한 후 Format 을 Docker 로 정의한다. 나머지는 아래 그림과 같이 설정 후 생성한다.

    Untitled

  • 이제 로컬에서 Docker Image 를 Build 한다. 이 때 -t 옵션을 통해 model_deploy:test 로 설정한다.
  • 그리고 docker tag 기존 이미지:태그 새 이미지 이름:태그 커맨드를 이용해서 빌드할 때 썼던 태그를 GCP 의 artifact registry 로 태그한다.
    • 아래와 같이 커맨드를 실행할 수 있다.

      Untitled

    • 위와 같이 실행해주면 새 이미지 이름은 GCP 의 Artifact repository URL 을 가지게 된다.

  • 이후 docker push Artifact Registry/프로젝트 id/Repository/이미지 이름:버전 커맨드를 이용해서 Image 를 Artifact Registry 에 올린다.
    • 아래와 같이 실행한다.

      Untitled

      • Image 가 Artifact Registry 에 올라가고, 웹 콘솔에서 Image 가 올라간 것을 확인할 수 있다.
  • 올라간 Docker Image 를 Pull 하고 싶다면 docker pull 하면 된다.
  • 이제 Github action 을 사용해서 특정 조건에 docker image build, push 를 자동화해보자.
    • 예를 들어 main branch 에 Merge 되면 docker Image 를 새로 만들고 registry 에 push 하는 작업이다. 즉 로컬에서 위와 같이 registry 에 올리는 게 아니라 github action 을 통해서 하는 방법이다.
  • 이를 위해 github repo 의 setting 에서 Secrets and variables → Actions → Reopsitory secrets 에 secret 을 만들어준다.
    • 여기에 GCP 서비스 계정을 설정한다. 서비스 계정에서 다운된 json 파일의 전체 경로를 기입하고 이름을 지정해주면 된다.
    • 이를 통해 github action 에서 GCP 에 접근이 가능해진다.

      Untitled

    • json 의 ENV 설정에서 project id, region, repository, image 를 지정한다.
    • jobs 에 CI 라고 되어 있는데, steps, run, uses 등을 확인한다.
    • SHA(hash) 값을 태그 값으로 설정해서, 매 커밋마다 이름이 겹치지 않도록하여 새로운 Image 가 Build + Push 될 수 있도록 한다.
    • Google Cloud 인증을 위해 auth 를 사용한다. 여기서 우리가 secret 에 저장한 credential 키를 사용하게 된다.
    • 이후 Docker 에 로그인 하고 docker push 해준다.
    • 이를 통해 main 에 merge 됐을 때 새로운 Image 를 빌드하고 push 하는 것까지 자동화가 된다.
  • 이제 서버에 배포하기 위해 GCP Compute Engine 을 세팅하고 인스턴스를 생성하자.
    • 컨테이너 옵션을 사용한다. 컨테이너 Image 배포에서 위에서 빌드한 Image 를 사용한다. 이를 통해 직접 만든 Image 를 사용해서 compute engine 을 띄우게 된다.
    • 컨테이너 구성은 어떤 Image 를 사용할 것인가를 입력하면 된다. Volume Mount 도 사용할 수 있다.
    • 인스턴스 생성을 완료하면 외부 IP 주소를 확인할 수 있다.
  • VM instance 에 새 Image 배포하기 (컨테이너 업데이트)
    • 컨테이너 업데이트에는 정답이 있는 건 아니다.
    • 인스턴스를 띄울 때 Docker Image 를 기반으로 생성하면, 이후 업데이트된 Image 를 기반으로 컨테이너 업데이트가 가능하다. 클라우드마다 기능이 다르긴 하지만, Image 가 바뀌면 새롭게 컨테이너를 업데이트 하는 기능이 대부분 존재한다.
    • 만약 컨테이너로 띄우지 않고, 그냥 서버를 띄웠다면 새로운 코드를 pull 하고 scp/FTP 등으로 서버에 전송하고, 그 코드를 기반으로 앱을 재실행하게 된다. reload 옵션 등이 있으면 설정해준다.
    • reload = True 로 주면, 파일이 바뀔 때마다 새롭게 알아서 웹을 껐다 킨다. 그게 아니면 웹을 껐다가 다시 동작시켜야 하는데, 그 찰나에 이슈가 있을 수도 있다. 이 시간동안 API 응답을 못하기 때문이다.

      Untitled

    • 새로운 모델, 코드, 의존성 등이 하나의 Image 로 만들어지고, 컨테이너 Image 업데이트 요청을 compute engine 에게 하는 방법이 권장된다.
  • Compute Engine 에서는 GCP 명령어를 이용해서 Image 로 만들어진 인스턴스를 빠르게 업데이트 할 수 있는 기능을 CLI 로 제공하고 있다.
    • gcloud compute instances update-container <서버 이름> --container-image <컨테이너 이미지>

    Untitled

  • CI (Continuous Integration) 를 진행한 후, GCE VM Instance 에 변경 사항을 전파한다.
    • workflow.yml 에 needs 라는 필드에 needs: [ci] 와 같이 ci 작업이 정상 완료되어야 실행하겠다고 알려준다.
    • 업데이트할 인스턴스, 존(영역) 정보를 사용하여 해당 인스턴스를 업데이트하게 된다.

      Untitled

    • github action 에서는 위와 같이 쉘 커맨드를 많이 사용한다.
    • secret 같은 환경변수를 잘 사용하는 것이 보안에 안전하다.
    • 이렇게 되면 Actions 메뉴에서 ci 가 완료된 후 cd 가 실행됨을 확인할 수 있다. 즉 needs 를 통해 dependeny 가 생긴 것이다.

      Untitled

  • 이렇게 Image 를 만들고 registry 에 push 한 뒤, 해당 Image 를 사용해서 compute engine 에 배포한다. 이후 Github Action 을 통해 코드가 변경되면 CI 를 진행하고 CD 를 통해 자동 업데이트 할 수 있다.
  • 중요한 것은 이미 많이 공유되어 있는 github action 템플릿들을 적극적으로 이용해서 CI/CD 에 대해 익숙해지는 것이다.
맨 위로 이동 ↑

댓글 남기기