Cloud Build를 사용한 GitOps 스타일의 지속적 배포

이 페이지에서는 호스팅 제품과 널리 사용되는 GitOps 방법론만 사용하여 Google Cloud 에서 지속적 통합 및 배포(CI/CD) 파이프라인을 만드는 방법을 설명합니다.

오랫동안 Google의 엔지니어들은 구성 및 배포 파일을 Google의 기본 소스 코드 저장소에 보관해 왔습니다. 이 방법론은 사이트 안정성 엔지니어링 8장(Beyer et al., 2016)에서 다루었으며, Google Cloud Next '17 키노트에서 켈시 하이타워가 시연한 바 있습니다.

GitOps의 핵심은 '코드로 표현한 환경', 즉 Git 저장소에 저장된 Kubernetes 매니페스트와 같은 파일을 사용하여 배포를 선언적으로 기술한다는 개념입니다.

이 가이드에서는 커밋된 코드로부터 컨테이너 이미지를 자동으로 빌드하는 CI/CD 파이프라인을 만들고, Artifact Registry에 이미지를 저장하고, Git 저장소의 Kubernetes 매니페스트를 업데이트하고, 이 매니페스트를 사용하여 애플리케이션을 Google Kubernetes Engine에 배포합니다.

CI/CD 파이프라인 아키텍처

이 튜토리얼에서는 두 개의 Git 저장소를 사용합니다.

  • app 저장소: 애플리케이션 자체의 소스 코드를 포함합니다.
  • env 저장소: Kubernetes 배포용 매니페스트를 포함합니다.

변경사항을 app 저장소에 푸시하면 Cloud Build 파이프라인이 테스트를 실행하고 컨테이너 이미지를 빌드하여 Artifact Registry에 푸시합니다. Cloud Build는 이미지를 푸시한 후 배포 매니페스트를 업데이트하여 env 저장소에 푸시합니다. 이때 다른 Cloud Build 파이프라인이 트리거되며, 이 파이프라인은 GKE 클러스터에 매니페스트를 적용하고 성공하면 env 저장소의 다른 분기에 매니페스트를 저장합니다.

appenv 저장소를 분리하는 이유는 수명 주기와 용도가 서로 다르기 때문입니다. app 저장소는 주 사용자가 실제 사람이며 특정 애플리케이션 전용입니다. env 저장소는 주 사용자가 Cloud Build 등의 자동화된 시스템이며 여러 애플리케이션에 공유될 수 있습니다. env 저장소는 각각 특정 환경에 매핑되는 여러 분기(이 튜토리얼에서는 production만 사용)를 가질 수 있으며 특정 컨테이너 이미지를 참조하지만, env 저장소는 그렇지 않습니다.

이 튜토리얼을 마치면 다음 작업을 손쉽게 처리할 수 있는 시스템이 구축됩니다.

  • Cloud Build 기록을 조회하여 배포 실패와 성공 구분
  • env 저장소의 production 분기를 조회하여 현재 사용 중인 매니페스트에 액세스
  • Cloud Build의 해당 빌드를 다시 실행하여 이전 버전으로 롤백

CI/CD 파이프라인의 흐름

이 튜토리얼 정보

이 튜토리얼에서는 Cloud Source Repositories를 사용하여 Git 저장소를 호스팅하지만 GitHub, Bitbucket, GitLab 등의 다른 타사 제품을 사용해도 같은 결과를 얻을 수 있습니다.

이 파이프라인은 배포 전 검증 메커니즘을 구현하지 않습니다. GitHub, Bitbucket 또는 GitLab을 사용하는 경우 파이프라인을 수정하여 pull 요청을 이 용도로 사용할 수 있습니다.

팀에서 블루-그린, 카나리아 분석, 멀티 클라우드 등의 고급 배포 패턴을 구현하려는 경우 Spinnaker가 권장되지만, 소규모 조직 및 프로젝트에서는 이러한 기능 모음 없이도 성공적으로 CI/CD 전략을 구현할 수 있습니다. 이 튜토리얼에서는 도구를 사용하여 GKE에 호스팅된 애플리케이션에 적합한 CI/CD 파이프라인을 만드는 방법을 알아봅니다.

이 튜토리얼에서는 편의상 env 저장소에서 단일 환경(production)만 사용하지만 필요한 경우 여러 환경으로 배포하도록 확장할 수 있습니다.

Cloud Source Repositories에 Git 저장소 만들기

이 섹션에서는 이 튜토리얼에 사용되는 두 개의 Git 저장소(appenv)를 만들고 app 저장소를 샘플 코드로 초기화합니다.

  1. Cloud Shell에서 두 개의 Git 저장소를 만듭니다.

    gcloud source repos create hello-cloudbuild-app
    gcloud source repos create hello-cloudbuild-env
    
  2. GitHub에서 샘플 코드를 복제합니다.

    cd ~
    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    
  3. Cloud Source Repositories를 원격으로 구성합니다.

    PROJECT_ID=$(gcloud config get-value project)
    git remote add google \
        "https://source.developers.google.com/p/${PROJECT_ID}/r/hello-cloudbuild-app"
    

클론한 코드에는 'Hello World' 애플리케이션이 포함되어 있습니다.

from flask import Flask
app = Flask('hello-cloudbuild')

@app.route('/')
def hello():
  return "Hello World!\n"

if __name__ == '__main__':
  app.run(host = '0.0.0.0', port = 8080)

Cloud Build로 컨테이너 이미지 만들기

복제한 코드에는 다음 Dockerfile이 포함되어 있습니다.

FROM python:3.13-slim
RUN pip install flask
WORKDIR /app
COPY app.py /app/app.py
ENTRYPOINT ["python"]
CMD ["/app/app.py"]

이 Dockerfile을 사용하면 Cloud Build로 컨테이너 이미지를 만들어 Artifact Registry에 저장할 수 있습니다.

  1. Cloud Shell에서 다음 명령어를 사용하여 최신 커밋에 따라 Cloud Build 빌드를 만듭니다.

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    COMMIT_ID="$(git rev-parse --short=7 HEAD)"
    gcloud builds submit --tag="us-central1-docker.pkg.dev/${PROJECT_ID}/my-repository/hello-cloudbuild:${COMMIT_ID}" .
    

    이 명령어를 실행하면 Cloud Build는 컨테이너 이미지가 만들어질 때 생성되는 로그를 터미널에 스트리밍합니다.

  2. 빌드가 끝난 후 새 컨테이너 이미지를 Artifact Registry에서 사용할 수 있는지 확인합니다.

    Artifact Registry로 이동

    Artifact Registry의 hello-cloudbuild 이미지

지속적 통합 파이프라인 만들기

이 섹션에서는 소규모 단위 테스트를 자동으로 실행하고 컨테이너 이미지를 빌드하여 Artifact Registry에 푸시하도록 Cloud Build를 구성합니다. Cloud Source Repositories에 새 커밋을 푸시하면 이 파이프라인이 자동으로 트리거됩니다. 코드에 포함된 cloudbuild.yaml 파일은 파이프라인의 구성입니다.

steps:
# This step runs the unit tests on the app
- name: 'python:3.13-slim'
  id: Test
  entrypoint: /bin/sh
  args:
  - -c
  - 'pip install flask && python test_app.py -v'

# This step builds the container image.
- name: 'gcr.io/cloud-builders/docker'
  id: Build
  args:
  - 'build'
  - '-t'
  - 'us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/hello-cloudbuild:$SHORT_SHA'
  - '.'

# This step pushes the image to Artifact Registry
# The PROJECT_ID and SHORT_SHA variables are automatically
# replaced by Cloud Build.
- name: 'gcr.io/cloud-builders/docker'
  id: Push
  args:
  - 'push'
  - 'us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/hello-cloudbuild:$SHORT_SHA'
  1. Cloud Build 트리거 페이지를 엽니다.

    트리거로 이동

  2. 트리거 만들기를 클릭합니다.

  3. 다음 옵션을 입력합니다.

    • 이름 필드에 hello-cloudbuild를 입력합니다.
    • 이벤트에서 분기로 푸시를 선택합니다.
    • 소스에서 hello-cloudbuild-app저장소로 선택하고 ^master$분기로 선택합니다.
    • 빌드 구성에서 Cloud Build 구성 파일을 선택합니다.
    • Cloud Build 구성 파일 위치 필드에 /cloudbuild.yaml을 차례로 입력합니다.
  4. 만들기를 클릭하여 빌드 트리거를 저장합니다.

    팁: 많은 프로젝트의 빌드 트리거를 만들어야 하는 경우에는 Build Triggers API를 사용하면 됩니다.

  5. Cloud Shell에서 애플리케이션 코드를 Cloud Source Repositories에 푸시하여 Cloud Build의 CI 파이프라인을 트리거합니다.

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    git push google master
    
  6. Cloud Build 콘솔을 엽니다.

    Cloud Build로 이동

    최근에 실행한 빌드와 완료된 빌드가 표시됩니다. 빌드를 클릭하면 실행 상태를 추적하고 로그를 조사할 수 있습니다.

지속적 배포 파이프라인 만들기

Cloud Build는 지속적 배포 파이프라인에도 사용됩니다. hello-cloudbuild-env 저장소의 candidate 분기에 커밋이 푸시될 때마다 파이프라인이 실행됩니다. 이 파이프라인은 Kubernetes 클러스터에 매니페스트의 새 버전을 적용하고, 여기에 성공하면 매니페스트를 production 분기에 복사합니다. 이 프로세스의 속성은 다음과 같습니다.

  • candidate 분기는 배포 시도 기록입니다.
  • production 분기는 배포 성공 기록입니다.
  • Cloud Build에서 배포 성공과 실패를 확인할 수 있습니다.
  • Cloud Build의 해당 빌드를 다시 실행하여 이전 배포로 롤백할 수 있습니다. 롤백하면 배포 기록을 정확히 반영하기 위해 production 분기도 업데이트됩니다.

이제 지속적 통합 파이프라인을 수정하여 hello-cloudbuild-env 저장소의 candidate 분기를 업데이트하고 지속적 배포 파이프라인을 트리거하겠습니다.

GKE에 Cloud Build 액세스 권한 부여

애플리케이션을 Kubernetes 클러스터에 배포하려면 Cloud Build에 Kubernetes Engine 개발자 ID 및 액세스 관리 역할이 필요합니다.

Shell

Cloud Shell에서 다음 명령어를 실행합니다.

PROJECT_NUMBER="$(gcloud projects describe ${PROJECT_ID} --format='get(projectNumber)')"
gcloud projects add-iam-policy-binding ${PROJECT_NUMBER} \
    --member=serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
    --role=roles/container.developer

콘솔

  1. Google Cloud 콘솔에서 GKE 권한 페이지로 이동합니다.

    권한으로 이동

  2. Kubernetes Engine 개발자 역할의 상태를 사용 설정으로 설정합니다.

hello-cloudbuild-env 저장소 초기화

hello-cloudbuild-env 저장소를 두 개의 분기(hello-cloudbuild-envhello-cloudbuild-env) 및 배포 프로세스를 기술하는 Cloud Build 구성 파일로 초기화해야 합니다.

  1. Cloud Shell에서 hello-cloudbuild-env 저장소를 클론하고 hello-cloudbuild-env 분기를 만듭니다.

    cd ~
    gcloud source repos clone hello-cloudbuild-env
    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    git checkout -b production
    
  2. hello-cloudbuild-app 저장소에서 사용할 수 있는 cloudbuild-delivery.yaml 파일을 복사하고 변경사항을 커밋합니다.

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    cp ~/hello-cloudbuild-app/cloudbuild-delivery.yaml ~/kubernetes-engine-samples/management/gitops-style-delivery/cloudbuild.yaml
    git add .
    git commit -m "Create cloudbuild.yaml for deployment"
    

    cloudbuild-delivery.yaml 파일은 Cloud Build에서 실행될 배포 프로세스를 설명합니다. 프로세스는 두 단계로 구성됩니다.

    1. Cloud Build가 GKE 클러스터에 매니페스트를 적용합니다.

    2. 여기에 성공하면 Cloud Build가 매니페스트를 production 분기에 복사합니다.

    steps:
    # This step deploys the new version of our container image
    # in the hello-cloudbuild Kubernetes Engine cluster.
    - name: 'gcr.io/cloud-builders/kubectl'
      id: Deploy
      args:
      - 'apply'
      - '-f'
      - 'kubernetes.yaml'
      env:
      - 'CLOUDSDK_COMPUTE_REGION=us-central1'
      - 'CLOUDSDK_CONTAINER_CLUSTER=hello-cloudbuild'
    
    # This step copies the applied manifest to the production branch
    # The COMMIT_SHA variable is automatically
    # replaced by Cloud Build.
    - name: 'gcr.io/cloud-builders/git'
      id: Copy to production branch
      entrypoint: /bin/sh
      args:
      - '-c'
      - |
        set -x && \
        # Configure Git to create commits with Cloud Build's service account
        git config user.email $(gcloud auth list --filter=status:ACTIVE --format='value(account)') && \
        # Switch to the production branch and copy the kubernetes.yaml file from the candidate branch
        git fetch origin production && git checkout production && \
        git checkout $COMMIT_SHA kubernetes.yaml && \
        # Commit the kubernetes.yaml file with a descriptive commit message
        git commit -m "Manifest from commit $COMMIT_SHA
        $(git log --format=%B -n 1 $COMMIT_SHA)" && \
        # Push the changes back to Cloud Source Repository
        git push origin production
  3. candidate 분기를 만들고 두 분기를 푸시하여 Cloud Source Repositories에 제공합니다.

    git checkout -b candidate
    git push origin production
    git push origin candidate
    
  4. hello-cloudbuild-env 저장소의 Cloud Build 서비스 계정에 소스 저장소 작성자 IAM 역할을 부여합니다.

    PROJECT_NUMBER="$(gcloud projects describe ${PROJECT_ID} \
        --format='get(projectNumber)')"
    cat >/tmp/hello-cloudbuild-env-policy.yaml <<EOF
    bindings:
    - members:
      - serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com
      role: roles/source.writer
    EOF
    gcloud source repos set-iam-policy \
        hello-cloudbuild-env /tmp/hello-cloudbuild-env-policy.yaml
    

지속적 배포 파이프라인의 트리거 만들기

이 섹션에서는 hello-cloudbuild-env 저장소의 candidate 분기에 푸시할 때 Cloud Build가 트리거되도록 구성합니다.

  1. Cloud Build의 트리거 페이지를 엽니다.

    트리거로 이동

  2. 트리거 만들기를 클릭합니다.

  3. 다음 옵션을 입력합니다.

    • 이름 필드에 hello-cloudbuild-deploy를 입력합니다.
    • 이벤트에서 분기로 푸시를 선택합니다.
    • 소스에서 hello-cloudbuild-env저장소로 선택하고 ^candidate$분기로 선택합니다.
    • 구성에서 Cloud Build 구성 파일(yaml 또는 json)을 선택합니다.
    • Cloud Build 구성 파일 위치 필드에 /cloudbuild.yaml을 차례로 입력합니다.
  4. 만들기를 클릭합니다.

지속적 배포 파이프라인을 트리거하도록 지속적 통합 파이프라인 수정

이 섹션에서는 지속적 통합 파이프라인에 몇 단계를 추가하여 Kubernetes 매니페스트의 새 버전을 생성하고 hello-cloudbuild-env 저장소에 푸시하여 지속적 배포 파이프라인을 트리거합니다.

  1. cloudbuild.yaml 파일을 cloudbuild-trigger-cd.yaml 파일의 확장 예시로 바꿉니다.

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    cp cloudbuild-trigger-cd.yaml cloudbuild.yaml
    

    cloudbuild-trigger-cd.yamlcloudbuild.yaml의 확장된 버전입니다. 새 Kubernetes 매니페스트를 생성하고 지속적 배포 파이프라인을 트리거하는 단계를 추가합니다.

    # This step clones the hello-cloudbuild-env repository
    - name: 'gcr.io/cloud-builders/gcloud'
      id: Clone env repository
      entrypoint: /bin/sh
      args:
      - '-c'
      - |
        gcloud source repos clone hello-cloudbuild-env && \
        cd hello-cloudbuild-env && \
        git checkout candidate && \
        git config user.email $(gcloud auth list --filter=status:ACTIVE --format='value(account)')
    
    # This step generates the new manifest
    - name: 'gcr.io/cloud-builders/gcloud'
      id: Generate manifest
      entrypoint: /bin/sh
      args:
      - '-c'
      - |
         sed "s/GOOGLE_CLOUD_PROJECT/${PROJECT_ID}/g" kubernetes.yaml.tpl | \
         sed "s/COMMIT_SHA/${SHORT_SHA}/g" > hello-cloudbuild-env/kubernetes.yaml
    
    # This step pushes the manifest back to hello-cloudbuild-env
    - name: 'gcr.io/cloud-builders/gcloud'
      id: Push manifest
      entrypoint: /bin/sh
      args:
      - '-c'
      - |
        set -x && \
        cd hello-cloudbuild-env && \
        git add kubernetes.yaml && \
        git commit -m "Deploying image us-central1-docker.pkg.dev/$PROJECT_ID/my-repository/hello-cloudbuild:${SHORT_SHA}
        Built from commit ${COMMIT_SHA} of repository hello-cloudbuild-app
        Author: $(git log --format='%an <%ae>' -n 1 HEAD)" && \
        git push origin candidate
    
  2. 수정사항을 커밋한 후 Cloud Source Repositories에 푸시합니다.

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    git add cloudbuild.yaml
    git commit -m "Trigger CD pipeline"
    git push google master
    

    이렇게 하면 Cloud Build의 지속적 통합 파이프라인이 트리거됩니다.

  3. 지속적 통합 빌드를 조사합니다.

    Cloud Build로 이동

    hello-cloudbuild-app 저장소의 최근 실행 및 완료된 빌드가 나타납니다. 빌드를 클릭하면 실행 상태를 추적하고 로그를 조사할 수 있습니다. 이 파이프라인의 마지막 단계에서는 새 매니페스트를 hello-cloudbuild-env 저장소에 푸시하여 지속적 배포 파이프라인을 트리거합니다.

  4. 지속적 배포 빌드를 조사합니다.

    Cloud Build로 이동

    hello-cloudbuild-env 저장소의 최근 실행 및 완료된 빌드가 표시됩니다. 빌드를 클릭하면 실행 상태를 추적하고 로그를 조사할 수 있습니다.

전체 파이프라인 테스트

이제 전체 CI/CD 파이프라인이 구성되었습니다. 이 섹션에서는 파이프라인을 처음부터 끝까지 테스트합니다.

  1. GKE 서비스 페이지로 이동합니다.

    Google Kubernetes Engine 서비스로 이동

    목록에는 최근에 완료된 지속적 배포 빌드에서 만든 hello-cloudbuild라는 단일 서비스가 포함되어 있습니다.

  2. hello-cloudbuild 서비스의 엔드포인트를 클릭합니다. 'Hello World!'가 표시됩니다. 엔드포인트가 없거나 부하 분산기 오류가 표시되는 경우 부하 분산기가 완전히 초기화될 때까지 몇 분 정도 기다려야 할 수 있습니다. 필요하다면 새로고침을 클릭하여 페이지를 업데이트합니다.

  3. Cloud Shell에서 애플리케이션 및 단위 테스트의 'Hello World'를 'Hello Cloud Build'로 바꿉니다.

    cd ~/kubernetes-engine-samples/management/gitops-style-delivery/
    sed -i 's/Hello World/Hello Cloud Build/g' app.py
    sed -i 's/Hello World/Hello Cloud Build/g' test_app.py
    
  4. 변경사항을 커밋하고 Cloud Source Repositories에 푸시합니다.

    git add app.py test_app.py
    git commit -m "Hello Cloud Build"
    git push google master
    

    이렇게 하면 전체 CI/CD 파이프라인이 트리거됩니다.

  5. 몇 분 후에 브라우저에서 애플리케이션을 다시 로드합니다. 'Hello Cloud Build!'가 표시됩니다.

롤백 테스트

이 세션에서는 'Hello World!'를 표시하던 애플리케이션 버전으로 롤백합니다.

  1. hello-cloudbuild-env 저장소의 Cloud Build 콘솔을 엽니다.

    Cloud Build로 이동

  2. 사용 가능한 빌드 중에서 두 번째로 최신인 빌드를 클릭합니다.

  3. 다시 빌드를 클릭합니다.

  4. 빌드가 끝나면 브라우저에서 애플리케이션을 다시 로드합니다. 'Hello World!'가 다시 표시됩니다.