Compute Engine에 배포


이 가이드에서는 Cloud Build 및 Terraform을 사용하여 Compute Engine 관리형 인스턴스 그룹(MIG)에서 제로 다운타임 블루/그린 배포를 수행하는 방법을 설명합니다.

Cloud Build를 사용하면 애플리케이션을 빌드하고 Compute Engine, Google Kubernetes Engine, GKE Enterprise, Cloud Functions와 같은 다양한 Google Cloud 런타임에 배포하는 등 다양한 개발자 프로세스를 자동화할 수 있습니다.

Compute Engine MIG를 사용하면 동일한 여러 가상 머신(VM)에서 애플리케이션을 운영할 수 있습니다. 자동 확장, 자동 복구, 리전(멀티 영역) 배포, 자동 업데이트 등의 자동화된 MIG 서비스를 활용하여 워크로드의 확장성 및 가용성을 높일 수 있습니다. 블루/그린 지속적 배포 모델을 사용하여 프로덕션에서 실행 중인 한 MIG(블루)에서 다른 MIG(그린)로 사용자 트래픽을 점진적으로 전송하는 방법을 알아봅니다.

디자인 개요

다음 다이어그램은 이 문서에 설명된 코드 샘플에서 사용하는 블루/그린 배포 모델을 보여줍니다.

블루/그린 모델

대략적으로 이 모델에는 다음 구성요소가 포함됩니다.

  • 블루 및 그린의 Compute Engine VM 풀 두 개
  • 3개의 외부 HTTP(S) 부하 분산기:
    • 최종 사용자의 트래픽을 VM 인스턴스의 블루 또는 그린 풀로 라우팅하는 블루/그린 부하 분산기입니다.
    • QA 엔지니어와 개발자로부터의 트래픽을 블루 VM 인스턴스 풀로 라우팅하는 블루 부하 분산기입니다.
    • QA 엔지니어와 개발자로부터의 트래픽을 그린 인스턴스 풀로 라우팅하는 그린 부하 분산기입니다.
  • 2개의 사용자 그룹:
    • 블루 또는 그린 인스턴스 풀을 가리키는 블루/그린 부하 분산기에 액세스할 수 있는 최종 사용자입니다.
    • 개발 및 테스트 목적으로 두 풀 집합에 모두 액세스해야 하는 QA 엔지니어 및 개발자입니다. 여기에 속하는 경우 블루 및 그린 부하 분산기에 모두 액세스할 수 있으며, 이는 사용자를 블루 인스턴스 풀과 그린 인스턴스 풀로 각각 라우팅합니다.

블루 및 그린 VM 풀은 Compute Engine MIG로 구현되며 외부 IP 주소는 외부 HTTP(S) 부하 분산기를 사용하여 MIG의 VM으로 라우팅됩니다. 이 문서에 설명된 코드 샘플은 Terraform을 사용하여 이 인프라를 구성합니다.

다음 다이어그램은 배포에서 발생하는 개발자 작업을 보여줍니다.

개발자 작업 흐름

위의 다이어그램에서 빨간색 화살표는 배포 인프라를 처음 설정할 때 발생하는 부트스트랩 흐름을 나타내고, 파란색 화살표는 각 배포 중에 발생하는 GitOps 흐름을 나타냅니다.

이 인프라를 설정하려면 부트스트랩 프로세스를 시작하고 GitOps 흐름의 구성요소를 설정하는 설정 스크립트를 실행합니다.

설정 스크립트는 다음 작업을 수행하는 Cloud Build 파이프라인을 실행합니다.

  • Cloud Source Repositoriescopy-of-gcp-mig-simple이라는 저장소를 만들고 소스 코드를 GitHub 샘플 저장소에서 Cloud Source Repositories의 저장소로 복사합니다.
  • applydestroy라는 이름의 Cloud Build 트리거 두 개를 만듭니다.

apply 트리거는 Cloud Source Repositories의 main.tfvars라는 Terraform 파일에 연결됩니다. 이 파일에는 블루 및 그린 부하 분산기를 나타내는 Terraform 변수가 포함되어 있습니다.

배포를 설정하려면 main.tfvars 파일에서 변수를 업데이트합니다. apply 트리거는 tf_apply을 실행하고 다음 작업을 수행하는 Cloud Build 파이프라인을 실행합니다.

  • Compute Engine MIG 2개(그린 1개, 블루 1개), Compute Engine VM 인스턴스 4개(그린 MIG 2개, 블루 MIG 2개), 부하 분산기 3개(블루, 그린, 분할기), 공개 IP 주소 3개를 만듭니다.
  • 블루 및 그린 인스턴스에서 배포된 애플리케이션을 확인하는 데 사용할 수 있는 IP 주소를 출력합니다.

폐기 트리거는 수동으로 트리거되어 적용 트리거로 생성된 모든 리소스를 삭제합니다.

목표

  • Cloud Build 및 Terraform을 사용하여 Compute Engine VM 인스턴스 그룹 백엔드와 함께 외부 HTTP(S) 부하 분산기를 설정합니다.

  • VM 인스턴스에서 블루/그린 배포를 수행합니다.

비용

이 문서에서는 비용이 청구될 수 있는 다음과 같은 Google Cloud 구성요소를 사용합니다.

프로젝트 사용량을 기준으로 예상 비용을 산출하려면 가격 계산기를 사용하세요. Google Cloud를 처음 사용하는 사용자는 무료 체험판을 사용할 수 있습니다.

이 문서에 설명된 태스크를 완료했으면 만든 리소스를 삭제하여 청구가 계속되는 것을 방지할 수 있습니다. 자세한 내용은 삭제를 참조하세요.

시작하기 전에

  1. Google Cloud 계정에 로그인합니다. Google Cloud를 처음 사용하는 경우 계정을 만들고 Google 제품의 실제 성능을 평가해 보세요. 신규 고객에게는 워크로드를 실행, 테스트, 배포하는 데 사용할 수 있는 $300의 무료 크레딧이 제공됩니다.
  2. Google Cloud CLI를 설치합니다.
  3. gcloud CLI를 초기화하려면 다음 명령어를 실행합니다.

    gcloud init
  4. Google Cloud 프로젝트를 만들거나 선택합니다.

    • Google Cloud 프로젝트를 만듭니다.

      gcloud projects create PROJECT_ID

      PROJECT_ID를 만들려는 Google Cloud 프로젝트의 이름으로 바꿉니다.

    • 만든 Google Cloud 프로젝트를 선택합니다.

      gcloud config set project PROJECT_ID

      PROJECT_ID를 Google Cloud 프로젝트 이름으로 바꿉니다.

  5. Google Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다.

  6. Google Cloud CLI를 설치합니다.
  7. gcloud CLI를 초기화하려면 다음 명령어를 실행합니다.

    gcloud init
  8. Google Cloud 프로젝트를 만들거나 선택합니다.

    • Google Cloud 프로젝트를 만듭니다.

      gcloud projects create PROJECT_ID

      PROJECT_ID를 만들려는 Google Cloud 프로젝트의 이름으로 바꿉니다.

    • 만든 Google Cloud 프로젝트를 선택합니다.

      gcloud config set project PROJECT_ID

      PROJECT_ID를 Google Cloud 프로젝트 이름으로 바꿉니다.

  9. Google Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다.

사용해 보기

  1. Google 코드 샘플 저장소에서 설정 스크립트를 실행합니다

    bash <(curl https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-build-samples/main/mig-blue-green/setup.sh)
    
  2. 설정 스크립트에서 사용자 동의를 요청하면 yes를 입력합니다.

    이 스크립트는 몇 초 후에 실행됩니다.

  3. Google Cloud 콘솔에서 Cloud Build 빌드 기록 페이지를 엽니다.

    빌드 기록 페이지 열기

  4. 최신 빌드를 클릭합니다.

    세 가지 빌드 단계가 포함된 Cloud Build 파이프라인을 보여주는 빌드 세부정보 페이지가 표시됩니다. 첫 번째 빌드 단계에서는 Cloud Source Repositories에 저장소를 만들고 두 번째 단계는 GitHub 샘플 저장소의 콘텐츠를 Cloud Source Repositories에 클론하고, 세 번째 단계에서는 두 개의 빌드 트리거를 추가합니다.

  5. Cloud Source Repositories를 엽니다.

    Cloud Source Repositories 열기

  6. 저장소 목록에서 copy-of-gcp-mig-simple을 클릭합니다.

    페이지 하단의 기록 탭에는 copy-of-gcp-mig-simple이라는 저장소를 만들기 위해 Cloud Build가 수행한 A copy of https://github.com/GoogleCloudPlatform/cloud-build-samples.git 설명의 커밋 한개가 표시됩니다.

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

    트리거 페이지 열기

  8. applydestroy라는 빌드 트리거 두 개가 표시됩니다. apply 트리거는 main 분기의 infra/main.tfvars 파일에 연결됩니다. 이 트리거는 파일이 업데이트될 때마다 실행됩니다. destroy 트리거는 수동 트리거입니다.

  9. 배포 프로세스를 시작하려면 infra/main.tfvars 파일을 업데이트합니다.

    1. 터미널 창에서 deploy-compute-engine이라는 폴더를 만들고 이동합니다.

      mkdir ~/deploy-compute-engine
      cd ~/deploy-compute-engine
      
    2. copy-of-gcp-mig-simple 저장소를 클론합니다.

      gcloud source repos clone copy-of-mig-blue-green
      
    3. 클론된 디렉터리로 이동합니다.

      cd ./copy-of-mig-blue-green
      
    4. infra/main.tfvars를 업데이트하여 블루를 그린으로 바꿉니다.

      sed -i'' -e 's/blue/green/g' infra/main.tfvars
      
    5. 업데이트된 파일을 추가합니다.

      git add .
      
    6. 파일을 커밋합니다.

      git commit -m "Promote green"
      
    7. 파일을 푸시합니다.

      git push
      

      infra/main.tfvars를 변경하면 apply 트리거 실행이 트리거되고 배포가 시작됩니다.

  10. Cloud Source Repositories를 엽니다.

    Cloud Source Repositories 열기

  11. 저장소 목록에서 copy-of-gcp-mig-simple을 클릭합니다.

    페이지 하단의 기록 탭에 설명 Promote green이 포함된 커밋이 표시됩니다.

  12. apply 트리거 실행을 보려면 Google Cloud 콘솔에서 빌드 기록 페이지를 엽니다.

    빌드 기록 페이지 열기

  13. 첫 번째 빌드를 클릭하여 빌드 세부정보 페이지를 엽니다.

    두 개의 빌드 단계가 포함된 apply 트리거 파이프라인이 표시됩니다. 첫 번째 빌드 단계에서는 Terraform 적용을 실행하여 배포를 위한 Compute Engine 및 부하 분산 리소스를 만듭니다. 두 번째 빌드 단계는 애플리케이션이 실행되는 것을 볼 수 있는 IP 주소를 출력합니다.

  14. 브라우저에서 그린 MIG에 해당하는 IP 주소를 엽니다. 배포를 보여주는 다음과 비슷한 스크린샷이 표시됩니다.

    배포

  15. Compute Engine 인스턴스 그룹 페이지로 이동하여 블루 및 그린 인스턴스 그룹을 확인합니다.

    인스턴스 그룹 페이지 열기

  16. VM 인스턴스 페이지를 열어 VM 인스턴스 4개를 확인합니다.

    VM 인스턴스 페이지 열기

  17. 외부 IP 주소 페이지를 열어 세 개의 부하 분산기를 확인합니다.

    외부 IP 주소 페이지 열기

코드 이해하기

이 코드 샘플의 소스 코드에는 다음이 포함됩니다.

  • 설정 스크립트와 관련된 소스 코드
  • Cloud Build 파이프라인과 관련된 소스 코드
  • Terraform 템플릿과 관련된 소스 코드

설정 스크립트

setup.sh는 부트스트랩 프로세스를 실행하고 블루/그린 배포를 위한 구성요소를 만드는 설정 스크립트입니다. 스크립트는 다음 작업을 수행합니다.

  • Cloud Build, Resource Manager, Compute Engine, Cloud Source Repositories API를 사용 설정합니다.
  • 프로젝트의 Cloud Build 서비스 계정에 roles/editor IAM 역할을 부여합니다. 이 역할은 Cloud Build에서 배포에 필요한 GitOps 구성요소를 만들고 설정하는 데 필요합니다.
  • 프로젝트의 Cloud Build 서비스 계정에 roles/source.admin IAM 역할을 부여합니다. 이 역할은 Cloud Build 서비스 계정에서 프로젝트에 Cloud Source Repositories를 만들고 샘플 GitHub 저장소의 콘텐츠를 Cloud Source Repositories에 클론하는 데 필요합니다.
  • 다음 역할을 하는 bootstrap.cloudbuild.yaml 인라인이라는 Cloud Build 파이프라인을 생성합니다.

    • Cloud Source Repositories에 새 저장소를 만듭니다.
    • 샘플 GitHub 저장소의 소스 코드를 Cloud Source Repositories의 새 저장소로 복사합니다.
    • 적용 트리거를 만들고 빌드 트리거를 폐기합니다.
set -e

BLUE='\033[1;34m'
RED='\033[1;31m'
GREEN='\033[1;32m'
NC='\033[0m'

echo -e "\n${GREEN}######################################################"
echo -e "#                                                    #"
echo -e "#  Zero-Downtime Blue/Green VM Deployments Using     #"
echo -e "#  Managed Instance Groups, Cloud Build & Terraform  #"
echo -e "#                                                    #"
echo -e "######################################################${NC}\n"

echo -e "\nSTARTED ${GREEN}setup.sh:${NC}"

echo -e "\nIt's ${RED}safe to re-run${NC} this script to ${RED}recreate${NC} all resources.\n"
echo "> Checking GCP CLI tool is installed"
gcloud --version > /dev/null 2>&1

readonly EXPLICIT_PROJECT_ID="$1"
readonly EXPLICIT_CONSENT="$2"

if [ -z "$EXPLICIT_PROJECT_ID" ]; then
    echo "> No explicit project id provided, trying to infer"
    PROJECT_ID="$(gcloud config get-value project)"
else
    PROJECT_ID="$EXPLICIT_PROJECT_ID"
fi

if [ -z "$PROJECT_ID" ]; then
    echo "ERROR: GCP project id was not provided as parameter and could not be inferred"
    exit 1
else
    readonly PROJECT_NUM="$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')"
    if [ -z "$PROJECT_NUM" ]; then
        echo "ERROR: GCP project number could not be determined"
        exit 1
    fi
    echo -e "\nYou are about to:"
    echo -e "  * modify project ${RED}${PROJECT_ID}/${PROJECT_NUM}${NC}"
    echo -e "  * ${RED}enable${NC} various GCP APIs"
    echo -e "  * make Cloud Build ${RED}editor${NC} of your project"
    echo -e "  * ${RED}execute${NC} Cloud Builds and Terraform plans to create"
    echo -e "  * ${RED}4 VMs${NC}, ${RED}3 load balancers${NC}, ${RED}3 public IP addresses${NC}"
    echo -e "  * incur ${RED}charges${NC} in your billing account as a result\n"
fi

if [ "$EXPLICIT_CONSENT" == "yes" ]; then
  echo "Proceeding under explicit consent"
  readonly CONSENT="$EXPLICIT_CONSENT"
else
    echo -e "Enter ${BLUE}'yes'${NC} if you want to proceed:"
    read CONSENT
fi

if [ "$CONSENT" != "yes" ]; then
    echo -e "\nERROR: Aborted by user"
    exit 1
else
    echo -e "\n......................................................"
    echo -e "\n> Received user consent"
fi

#
# Executes action with one randomly delayed retry.
#
function do_with_retry {
    COMMAND="$@"
    echo "Trying $COMMAND"
    (eval $COMMAND && echo "Success on first try") || ( \
        echo "Waiting few seconds to retry" &&
        sleep 10 && \
        echo "Retrying $COMMAND" && \
        eval $COMMAND \
    )
}

echo "> Enabling required APIs"
# Some of these can be enabled later with Terraform, but I personally
# prefer to do all API enablement in one place with gcloud.
gcloud services enable \
    --project=$PROJECT_ID \
    cloudbuild.googleapis.com \
    cloudresourcemanager.googleapis.com \
    compute.googleapis.com \
    sourcerepo.googleapis.com \
    --no-user-output-enabled \
    --quiet

echo "> Adding Cloud Build to roles/editor"
gcloud projects add-iam-policy-binding \
    "$PROJECT_ID" \
    --member="serviceAccount:$PROJECT_NUM@cloudbuild.gserviceaccount.com" \
    --role='roles/editor' \
    --condition=None \
    --no-user-output-enabled \
    --quiet

echo "> Adding Cloud Build to roles/source.admin"
gcloud projects add-iam-policy-binding \
    "$PROJECT_ID" \
    --member="serviceAccount:$PROJECT_NUM@cloudbuild.gserviceaccount.com" \
    --condition=None \
    --role='roles/source.admin' \
    --no-user-output-enabled \
    --quiet

echo "> Configuring bootstrap job"
rm -rf "./bootstrap.cloudbuild.yaml"
cat <<'EOT_BOOT' > "./bootstrap.cloudbuild.yaml"
tags:
- "mig-blue-green-bootstrapping"
steps:
- id: create_new_cloud_source_repo
  name: "gcr.io/cloud-builders/gcloud"
  script: |
    #!/bin/bash
    set -e

    echo "(Re)Creating source code repository"

    gcloud source repos delete \
        "copy-of-mig-blue-green" \
        --quiet || true

    gcloud source repos create \
        "copy-of-mig-blue-green" \
        --quiet

- id: copy_demo_source_into_new_cloud_source_repo
  name: "gcr.io/cloud-builders/gcloud"
  env:
    - "PROJECT_ID=$PROJECT_ID"
    - "PROJECT_NUMBER=$PROJECT_NUMBER"
  script: |
    #!/bin/bash
    set -e

    readonly GIT_REPO="https://github.com/GoogleCloudPlatform/cloud-build-samples.git"

    echo "Cloning demo source repo"
    mkdir /workspace/from/
    cd /workspace/from/
    git clone $GIT_REPO ./original
    cd ./original

    echo "Cloning new empty repo"
    mkdir /workspace/to/
    cd /workspace/to/
    gcloud source repos clone \
        "copy-of-mig-blue-green"
    cd ./copy-of-mig-blue-green

    echo "Making a copy"
    cp -r /workspace/from/original/mig-blue-green/* ./

    echo "Setting git identity"
    git config user.email \
        "$PROJECT_NUMBER@cloudbuild.gserviceaccount.com"
    git config user.name \
        "Cloud Build"

    echo "Commit & push"
    git add .
    git commit \
        -m "A copy of $GIT_REPO"
    git push

- id: add_pipeline_triggers
  name: "gcr.io/cloud-builders/gcloud"
  env:
    - "PROJECT_ID=$PROJECT_ID"
  script: |
    #!/bin/bash
    set -e

    echo "(Re)Creating destroy trigger"
    gcloud builds triggers delete "destroy" --quiet || true
    gcloud builds triggers create manual \
        --name="destroy" \
        --repo="https://source.developers.google.com/p/$PROJECT_ID/r/copy-of-mig-blue-green" \
        --branch="master" \
        --build-config="pipelines/destroy.cloudbuild.yaml" \
        --repo-type=CLOUD_SOURCE_REPOSITORIES \
        --quiet

    echo "(Re)Creating apply trigger"
    gcloud builds triggers delete "apply" --quiet || true
    gcloud builds triggers create cloud-source-repositories \
        --name="apply" \
        --repo="copy-of-mig-blue-green" \
        --branch-pattern="master" \
        --build-config="pipelines/apply.cloudbuild.yaml" \
        --included-files="infra/main.tfvars" \
        --quiet

EOT_BOOT

echo "> Waiting API enablement propagation"
do_with_retry "(gcloud builds list --project "$PROJECT_ID" --quiet && gcloud compute instances list --project "$PROJECT_ID" --quiet && gcloud source repos list --project "$PROJECT_ID" --quiet) > /dev/null 2>&1" > /dev/null 2>&1

echo "> Executing bootstrap job"
gcloud beta builds submit \
    --project "$PROJECT_ID" \
    --config ./bootstrap.cloudbuild.yaml \
    --no-source \
    --no-user-output-enabled \
    --quiet
rm ./bootstrap.cloudbuild.yaml

echo -e "\n${GREEN}All done. Now you can:${NC}"
echo -e "  * manually run 'apply' and 'destroy' triggers to manage deployment lifecycle"
echo -e "  * commit change to 'infra/main.tfvars' and see 'apply' pipeline trigger automatically"

echo -e "\n${GREEN}Few key links:${NC}"
echo -e "  * Dashboard: https://console.cloud.google.com/home/dashboard?project=$PROJECT_ID"
echo -e "  * Repo: https://source.cloud.google.com/$PROJECT_ID/copy-of-mig-blue-green"
echo -e "  * Cloud Build Triggers: https://console.cloud.google.com/cloud-build/triggers;region=global?project=$PROJECT_ID"
echo -e "  * Cloud Build History: https://console.cloud.google.com/cloud-build/builds?project=$PROJECT_ID"

echo -e "\n............................."

echo -e "\n${GREEN}COMPLETED!${NC}"

Cloud Build 파이프라인

apply.cloudbuild.yamldestroy.cloudbuild.yaml은 설정 스크립트가 GitOps 흐름의 리소스를 설정하는 데 사용하는 Cloud Build 구성 파일입니다. apply.cloudbuild.yaml에는 두 가지 빌드 단계가 포함됩니다.

  • tf_apply build: Terraform을 설치하는 tf_install_in_cloud_build_step 함수를 호출하는 빌드 단계입니다. tf_apply는 GitOps 흐름에 사용된 리소스를 만듭니다. tf_install_in_cloud_build_steptf_apply 함수는 bash_utils.sh에 정의되어 있으며, 빌드 단계는 source 명령어를 사용하여 이를 호출합니다.
  • describe_deployment: 부하 분산기의 IP 주소를 출력하는 describe_deployment 함수를 호출하는 빌드 단계입니다.

destroy.cloudbuild.yamltf_apply로 생성된 모든 리소스를 삭제하는 tf_destroy를 호출합니다.

tf_install_in_cloud_build_step, tf_apply, describe_deployment, tf_destroy 함수는 bash_utils.sh 파일에 정의됩니다. 빌드 구성 파일은 source 명령어를 사용하여 함수를 호출합니다.

steps:
  - id: run-terraform-apply
    name: "gcr.io/cloud-builders/gcloud"
    env:
      - "PROJECT_ID=$PROJECT_ID"
    script: |
      #!/bin/bash
      set -e
      source /workspace/lib/bash_utils.sh
      tf_install_in_cloud_build_step
      tf_apply

  - id: describe-deployment
    name: "gcr.io/cloud-builders/gcloud"
    env:
      - "PROJECT_ID=$PROJECT_ID"
    script: |
      #!/bin/bash
      set -e
      source /workspace/lib/bash_utils.sh
      describe_deployment

tags:
  - "mig-blue-green-apply"
steps:
  - id: run-terraform-destroy
    name: "gcr.io/cloud-builders/gcloud"
    env:
      - "PROJECT_ID=$PROJECT_ID"
    script: |
      #!/bin/bash
      set -e
      source /workspace/lib/bash_utils.sh
      tf_install_in_cloud_build_step
      tf_destroy

tags:
  - "mig-blue-green-destroy"

다음 코드는 bash_utils.sh에 정의된 tf_install_in_cloud_build_step 함수를 보여줍니다. 빌드 구성 파일은 이 함수를 호출하여 Terraform을 즉시 설치합니다. Terraform 상태를 기록하는 Cloud Storage 버킷을 만듭니다.

function tf_install_in_cloud_build_step {
    echo "Installing deps"
    apt update
    apt install \
        unzip \
        wget \
        -y

    echo "Manually installing Terraform"
    wget https://releases.hashicorp.com/terraform/1.3.4/terraform_1.3.4_linux_386.zip
    unzip -q terraform_1.3.4_linux_386.zip
    mv ./terraform /usr/bin/
    rm -rf terraform_1.3.4_linux_386.zip

    echo "Verifying installation"
    terraform -v

    echo "Creating Terraform state storage bucket $BUCKET_NAME"
    gcloud storage buckets create \
        "gs://$BUCKET_NAME" || echo "Already exists..."

    echo "Configure Terraform provider and state bucket"
cat <<EOT_PROVIDER_TF > "/workspace/infra/provider.tf"
terraform {
  required_version = ">= 0.13"
  backend "gcs" {
    bucket = "$BUCKET_NAME"
  }
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = ">= 3.77, < 5.0"
    }
  }
}
EOT_PROVIDER_TF

    echo "$(cat /workspace/infra/provider.tf)"
}

다음 코드 스니펫은 bash_utils.sh에 정의된 tf_apply 함수를 보여줍니다. 먼저 모든 모듈과 커스텀 라이브러리를 로드하는 terraform init을 호출한 후 terraform apply를 실행하여 main.tfvars 파일에서 변수를 로드합니다.

function tf_apply {
    echo "Running Terraform init"
    terraform \
        -chdir="$TF_CHDIR" \
        init

    echo "Running Terraform apply"
    terraform \
        -chdir="$TF_CHDIR" \
        apply \
        -auto-approve \
        -var project="$PROJECT_ID" \
        -var-file="main.tfvars"
}

다음 코드 스니펫은 bash_utils.sh에 정의된 describe_deployment 함수를 보여줍니다. gcloud compute addresses describe를 사용하여 이름으로 부하 분산기의 IP 주소를 가져와 출력합니다.

function describe_deployment {
    NS="ns1-"
    echo -e "Deployment configuration:\n$(cat infra/main.tfvars)"
    echo -e \
      "Here is how to connect to:" \
      "\n\t* active color MIG: http://$(gcloud compute addresses describe ${NS}splitter-address-name --region=us-west1 --format='value(address)')/" \
      "\n\t* blue color MIG: http://$(gcloud compute addresses describe ${NS}blue-address-name --region=us-west1 --format='value(address)')/" \
      "\n\t* green color MIG: http://$(gcloud compute addresses describe ${NS}green-address-name --region=us-west1 --format='value(address)')/"
    echo "Good luck!"
}

다음 코드 스니펫은 bash_utils.sh에 정의된 tf_destroy 함수를 보여줍니다. 모든 모듈과 커스텀 라이브러리를 로드하는 terraform init을 호출한 후 Terraform 변수를 언로드하는 terraform destroy를 실행합니다.

function tf_destroy {
    echo "Running Terraform init"
    terraform \
        -chdir="$TF_CHDIR" \
        init

    echo "Running Terraform destroy"
    terraform \
        -chdir="$TF_CHDIR" \
        destroy \
        -auto-approve \
        -var project="$PROJECT_ID" \
        -var-file="main.tfvars"
}

Terraform 템플릿

copy-of-gcp-mig-simple/infra/ 폴더에서 모든 Terraform 구성 파일과 변수를 찾을 수 있습니다.

  • main.tf: Terraform 구성 파일입니다.
  • main.tfvars: 이 파일은 Terraform 변수를 정의합니다.
  • mig/splitter/: 이 폴더에는 부하 분산기를 정의하는 모듈이 포함됩니다. mig/ 폴더에는 블루 및 그린 부하 분산기의 MIG를 정의하는 Terraform 구성 파일이 포함되어 있습니다. 블루 및 그린 MIG는 동일하므로 한 번만 정의되며 블루 및 그린 객체에서 인스턴스화됩니다. 분할기 부하 분산기의 Terraform 구성 파일은 splitter/ 폴더에 있습니다.

다음 코드 스니펫은 infra/main.tfvars의 콘텐츠를 보여줍니다. 여기에는 블루 및 그린 풀에 배포할 애플리케이션 버전을 결정하는 2개의 변수와 활성 색상에 대한 변수(블루 또는 그린)의 세 가지 변수가 포함됩니다. 이 파일을 변경하면 배포가 트리거됩니다.

MIG_VER_BLUE     = "v1"
MIG_VER_GREEN    = "v1"
MIG_ACTIVE_COLOR = "blue"

다음은 infra/main.tf의 코드 스니펫입니다. 이 스니펫에서는 다음 사항이 적용됩니다.

  • Google Cloud 프로젝트에 대한 변수가 정의됩니다.
  • Google이 Terraform 제공업체로 설정됩니다.
  • 변수는 네임스페이스에 정의됩니다. Terraform에서 생성되는 모든 객체에는 이 변수로 프리픽스가 붙어 애플리케이션의 여러 버전을 동일한 프로젝트에 배포할 수 있고 객체 이름이 서로 충돌하지 않습니다.
  • 변수 MIG_VER_BLUE, MIG_VER_BLUE, MIG_ACTIVE_COLORinfra/main.tfvars 파일의 변수에 대한 바인딩입니다.
variable "project" {
  type        = string
  description = "GCP project we are working in."
}

provider "google" {
  project = var.project
  region  = "us-west1"
  zone    = "us-west1-a"
}

variable "ns" {
  type        = string
  default     = "ns1-"
  description = "The namespace used for all resources in this plan."
}

variable "MIG_VER_BLUE" {
  type        = string
  description = "Version tag for 'blue' deployment."
}

variable "MIG_VER_GREEN" {
  type        = string
  description = "Version tag for 'green' deployment."
}

variable "MIG_ACTIVE_COLOR" {
  type        = string
  description = "Active color (blue | green)."
}

infra/main.tf의 다음 코드 스니펫은 분할기 모듈의 인스턴스화를 보여줍니다. 이 모듈은 분할기 부하 분산기가 애플리케이션을 배포할 MIG를 알 수 있도록 활성 색상을 사용합니다.

module "splitter-lb" {
  source               = "./splitter"
  project              = var.project
  ns                   = "${var.ns}splitter-"
  active_color         = var.MIG_ACTIVE_COLOR
  instance_group_blue  = module.blue.google_compute_instance_group_manager_default.instance_group
  instance_group_green = module.green.google_compute_instance_group_manager_default.instance_group
}

infra/main.tf의 다음 코드 스니펫은 블루 및 그린 MIG에 동일한 두 모듈을 정의합니다. 분할기 모듈에 정의된 색상, 네트워크, 서브네트워크를 사용합니다.

module "blue" {
  source                               = "./mig"
  project                              = var.project
  app_version                          = var.MIG_VER_BLUE
  ns                                   = var.ns
  color                                = "blue"
  google_compute_network               = module.splitter-lb.google_compute_network
  google_compute_subnetwork            = module.splitter-lb.google_compute_subnetwork_default
  google_compute_subnetwork_proxy_only = module.splitter-lb.google_compute_subnetwork_proxy_only
}

module "green" {
  source                               = "./mig"
  project                              = var.project
  app_version                          = var.MIG_VER_GREEN
  ns                                   = var.ns
  color                                = "green"
  google_compute_network               = module.splitter-lb.google_compute_network
  google_compute_subnetwork            = module.splitter-lb.google_compute_subnetwork_default
  google_compute_subnetwork_proxy_only = module.splitter-lb.google_compute_subnetwork_proxy_only
}

splitter/main.tf 파일은 분할기 MIG에 대해 생성된 객체를 정의합니다. 다음은 그린과 블루 MIG 간에 전환하는 로직이 포함된 splitter/main.tf의 코드 스니펫입니다. 두 가지 백엔드 리전(var.instance_group_blue 또는 var.instance_group_green)으로 트래픽을 라우팅할 수 있는 google_compute_region_backend_service 서비스가 지원됩니다. capacity_scaler는 라우팅할 트래픽 양을 정의합니다.

다음 코드는 트래픽의 100%를 지정된 색상으로 라우팅하지만 카나리아 배포를 위해 이 코드를 업데이트하여 트래픽을 일부 사용자에게 라우팅할 수 있습니다.

resource "google_compute_region_backend_service" "default" {
  name                  = local.l7-xlb-backend-service
  region                = "us-west1"
  load_balancing_scheme = "EXTERNAL_MANAGED"
  health_checks         = [google_compute_region_health_check.default.id]
  protocol              = "HTTP"
  session_affinity      = "NONE"
  timeout_sec           = 30
  backend {
    group           = var.instance_group_blue
    balancing_mode  = "UTILIZATION"
    capacity_scaler = var.active_color == "blue" ? 1 : 0
  }
  backend {
    group           = var.instance_group_green
    balancing_mode  = "UTILIZATION"
    capacity_scaler = var.active_color == "green" ? 1 : 0
  }
}

mig/main.tf 파일은 블루 및 그린 MIG와 관련된 객체를 정의합니다. 이 파일의 다음 코드 스니펫은 VM 풀을 만드는 데 사용되는 Compute Engine 인스턴스 템플릿을 정의합니다. 이 인스턴스 템플릿에는 Terraform 수명 주기 속성이 create_before_destroy로 설정되어 있습니다. 이는 풀 버전을 업데이트할 때 이전 버전의 풀에서 템플릿을 계속 사용 중인 경우 템플릿을 사용하여 새 버전의 풀을 만들 수 없기 때문입니다. 하지만 새 템플릿을 만들기 전에 이전 버전의 풀이 삭제되면 풀이 다운되는 기간이 있습니다. 이 시나리오를 피하기 위해 Terraform 수명 주기를 create_before_destroy로 설정하여 이전 버전이 폐기되기 전에 최신 버전의 VM 풀이 먼저 생성되도록 합니다.

resource "google_compute_instance_template" "default" {
  name = local.l7-xlb-backend-template
  disk {
    auto_delete  = true
    boot         = true
    device_name  = "persistent-disk-0"
    mode         = "READ_WRITE"
    source_image = "projects/debian-cloud/global/images/family/debian-10"
    type         = "PERSISTENT"
  }
  labels = {
    managed-by-cnrm = "true"
  }
  machine_type = "n1-standard-1"
  metadata = {
    startup-script = <<EOF
    #! /bin/bash
    sudo apt-get update
    sudo apt-get install apache2 -y
    sudo a2ensite default-ssl
    sudo a2enmod ssl
    vm_hostname="$(curl -H "Metadata-Flavor:Google" \
    http://169.254.169.254/computeMetadata/v1/instance/name)"
    sudo echo "<html><body style='font-family: Arial; margin: 64px; background-color: light${var.color};'><h3>Hello, World!<br><br>version: ${var.app_version}<br>ns: ${var.ns}<br>hostname: $vm_hostname</h3></body></html>" | \
    tee /var/www/html/index.html
    sudo systemctl restart apache2
    EOF
  }
  network_interface {
    access_config {
      network_tier = "PREMIUM"
    }
    network    = var.google_compute_network.id
    subnetwork = var.google_compute_subnetwork.id
  }
  region = "us-west1"
  scheduling {
    automatic_restart   = true
    on_host_maintenance = "MIGRATE"
    provisioning_model  = "STANDARD"
  }
  tags = ["load-balanced-backend"]

  # NOTE: the name of this resource must be unique for every update;
  #       this is wy we have a app_version in the name; this way
  #       new resource has a different name vs old one and both can
  #       exists at the same time
  lifecycle {
    create_before_destroy = true
  }
}

삭제

이 튜토리얼에서 사용된 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 리소스가 포함된 프로젝트를 삭제하거나 프로젝트를 유지하고 개별 리소스를 삭제하세요.

개별 리소스 삭제

  1. 적용 트리거로 생성된 Compute Engine 리소스를 삭제합니다.

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

      트리거 페이지 열기

    2. 트리거 테이블에서 폐기 트리거에 해당하는 행을 찾고 실행을 클릭합니다. 트리거 실행이 완료되면 적용 트리거로 생성된 리소스가 삭제됩니다.

  2. 터미널 창에서 다음 명령어를 실행하여 부트스트랩 중에 생성된 리소스를 삭제합니다.

    bash <(curl https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-build-samples/main/mig-blue-green/teardown.sh)
    

프로젝트 삭제

    Google Cloud 프로젝트를 삭제합니다.

    gcloud projects delete PROJECT_ID

다음 단계