Compute Engine にデプロイ


このガイドでは、Cloud Build と Terraform を使用して Compute Engine マネージド インスタンス グループ(MIG)でダウンタイムなしの Blue/Green デプロイを行う方法について説明します。

Cloud Build を使用すると、さまざまな Google Cloud ランタイム(Compute Engine、Google Kubernetes EngineGKE EnterpriseCloud Run functions など)へのアプリケーションのビルドとデプロイなど、さまざまなデベロッパー プロセスを自動化できます。

Compute Engine MIG を使用すると、複数の同じ仮想マシン(VM)でアプリケーションを操作できます。自動スケーリング、自動修復、リージョン(マルチゾーン)デプロイ、自動更新などの自動化 MIG サービスを活用することで、ワークロードのスケーラビリティと高可用性を実現できます。Blue/Green 継続的デプロイモデルを使用して、ユーザー トラフィックを、ある MIG(Blue)から別の MIG(Green)に段階的に転送する方法について学習します。MIG は、どちらも本番環境で実行されます。

デザインの概要

次の図では、このドキュメントで説明するコードサンプルで使用される Blue / Green デプロイモデルを示します。

Blue/Green モデル

大まかに表現すると、このモデルには、次のコンポーネントが含まれています。

  • 2 つの Compute Engine VM プール(Blue と Green)。
  • 3 つの外部 HTTP(S) ロードバランサ:
    • Blue/Green ロードバランサ。エンドユーザーから VM インスタンスの Blue または Green プールにトラフィックを転送します。
    • QA エンジニアとデベロッパーから Blue VM インスタンス プールにトラフィックを転送する Blue ロードバランサ。
    • QA エンジニアとデベロッパーから Green インスタンス プールにトラフィックを転送する Green ロードバランサ。
  • 次の 2 セットのユーザー:
    • Blue/Green ロードバランサにアクセスできるエンドユーザー。Blue または Green のインスタンス プールを参照します。
    • 開発とテストを目的として、両方のプールセットにアクセスする必要がある QA エンジニアとデベロッパー。Blue ロードバランサと Green ロードバランサの両方にアクセスでき、ロードバランサはそれぞれ Blue インスタンス プールと Green インスタンス プールに転送されます。

Blue と Green の 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 という名前の 2 つの Cloud Build トリガーを作成します。

apply トリガーは、Cloud Source Repositories の main.tfvars という名前の Terraform ファイルにアタッチされています。このファイルには、Blue ロードバランサと Green ロードバランサを表す Terraform 変数が含まれています。

デプロイを設定するには、main.tfvars ファイルの変数を更新します。apply トリガーは、tf_apply を実行し、次のオペレーションを実行する Cloud Build パイプラインを実行します。

  • 2 つの Compute Engine MIG(1 つは Green、もう 1 つは Blue)、4 つの Compute Engine VM インスタンス(2 つの Green MIG と 2 つの Blue MIG)、3 つのロードバランサ(Blue、Green、スプリッターなど)と 3 つのパブリック IP アドレス。
  • デプロイされたアプリケーションを Blue インスタンスと Green インスタンスで表示するために使用できる IP アドレスを出力します。

destroy トリガーは手動でトリガーされ、apply トリガーによって作成されたすべてのリソースを削除します。

目標

  • Cloud Build と Terraform を使用して、Compute Engine VM インスタンス グループのバックエンドを持つ外部 HTTP(S) ロードバランサを設定します。

  • VM インスタンスで Blue/Green デプロイを実行します。

費用

このドキュメントでは、Google Cloud の次の課金対象のコンポーネントを使用します。

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。 新しい Google Cloud ユーザーは無料トライアルをご利用いただける場合があります。

このドキュメントに記載されているタスクの完了後、作成したリソースを削除すると、それ以上の請求は発生しません。詳細については、クリーンアップをご覧ください。

始める前に

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. Install the Google Cloud CLI.
  3. To initialize the gcloud CLI, run the following command:

    gcloud init
  4. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  5. Make sure that billing is enabled for your Google Cloud project.

  6. Install the Google Cloud CLI.
  7. To initialize the gcloud CLI, run the following command:

    gcloud init
  8. Create or select a Google Cloud project.

    • Create a Google Cloud project:

      gcloud projects create PROJECT_ID

      Replace PROJECT_ID with a name for the Google Cloud project you are creating.

    • Select the Google Cloud project that you created:

      gcloud config set project PROJECT_ID

      Replace PROJECT_ID with your Google Cloud project name.

  9. Make sure that billing is enabled for your Google Cloud project.

試してみる

  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. 最新のビルドをクリックします。

    [ビルドの詳細] ページが開き、3 つのビルドステップを含む Cloud Build パイプラインが表示されます。最初のビルドステップで、Cloud Source Repositories にリポジトリが作成されます。2 番目のステップでは、GitHub のサンプル リポジトリの内容のクローンを Cloud Source Repositories に作成します。3 番目のステップでは 2 つのビルドトリガーを追加します。

  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 を含む 1 つの commit が表示されます。

  7. Cloud Build の [トリガー] ページを開きます。

    [トリガー] ページを開く

  8. applydestroy という名前の 2 つのビルドトリガーが表示されます。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 を更新して、Blue を Green に置き換えます。

      sed -i'' -e 's/blue/green/g' infra/main.tfvars
      
    5. 更新したファイルを追加します。

      git add .
      
    6. ファイルを commit します。

      git commit -m "Promote green"
      
    7. ファイルを push します。

      git push
      

      infra/main.tfvars を変更すると、apply トリガーの実行がトリガーされ、デプロイが開始されます。

  10. Cloud Source Repositories を開く

    Cloud Source Repositories を開く

  11. リポジトリ リストで copy-of-gcp-mig-simple をクリックします。

    ページの下部にある [履歴] タブに、Promote green と記述された commit が表示されます。

  12. apply トリガーの実行を表示するには、Google Cloud コンソールで [ビルド履歴] ページを開きます。

    [ビルド履歴] ページを開く

  13. 最初のビルドをクリックして、[ビルドの詳細] ページを開きます。

    2 つのビルドステップを含む apply トリガー パイプラインが表示されます。最初のビルドステップでは、Terraform apply を実行して、Compute Engine とデプロイのロード バランシング リソースを作成します。2 番目のビルドステップでは、アプリケーションが実行されている IP アドレスを出力します。

  14. ブラウザで、Green MIG に対応する IP アドレスを開きます。デプロイメントを示す次のようなスクリーンショットが表示されます。

    デプロイ

  15. Compute Engine の [インスタンス グループ] ページに移動して、Blue と Green のインスタンス グループを表示します。

    [インスタンス グループ] ページを開く

  16. [VM インスタンス] ページを開き、次の 4 つの VM インスタンスを確認します。

    [VM インスタンス] ページを開く

  17. [外部 IP アドレス] ページを開き、次の 3 つのロードバランサを確認します。

    [外部 IP アドレス] ページを開く

コードについて

このコードサンプルのソースコードには以下が含まれます。

  • セットアップ スクリプトに関連するソースコード。
  • Cloud Build パイプラインに関連するソースコード。
  • Terraform テンプレートに関連するソースコード。

セットアップ スクリプト

setup.sh が、ブートストラップ プロセスを実行して Blue/Green デプロイのコンポーネントを作成するセットアップ スクリプトです。このスクリプトは、次の操作を実行します。

  • 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 には、2 つのビルドステップがあります。

  • Terraform をインストールする tf_install_in_cloud_build_step 関数を呼び出す tf_apply build ビルドステップ。tf_apply は、GitOps フローで使用されるリソースを作成します。関数 tf_install_in_cloud_build_steptf_applybash_utils.sh で定義され、ビルドステップは source コマンドを使用してこれらの関数を呼び出します。
  • ロードバランサの IP アドレスを出力する describe_deployment 関数を呼び出す describe_deployment ビルドステップ。

destroy.cloudbuild.yaml は、tf_apply によって作成されたすべてのリソースを削除する tf_destroy を呼び出します。

関数 tf_install_in_cloud_build_steptf_applydescribe_deploymenttf_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 テンプレート

すべての Terraform 構成ファイルと変数は、copy-of-gcp-mig-simple/infra/ フォルダにあります。

  • main.tf: Terraform 構成ファイルです。
  • main.tfvars: このファイルで Terraform 変数を定義します。
  • mig/splitter/: これらのフォルダには、ロードバランサを定義するモジュールが含まれています。mig/ フォルダには、Blue ロードバランサと Green ロードバランサの MIG を定義する Terraform 構成ファイルが含まれています。Blue MIG と Green MIG は、同じであるため、Blue オブジェクトと Green オブジェクト用に 1 回定義され、インスタンス化されます。スプリッター ロードバランサの Terraform 構成ファイルは、splitter/ フォルダにあります。

次のコード スニペットでは、infra/main.tfvars の内容を示します。これには、Blue プールと Green プールにデプロイするアプリケーションのバージョンを決定する 2 つの変数と、アクティブな色(Blue または Green)の変数が含まれます。このファイルに変更を加えると、デプロイがトリガーされます。

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

infra/main.tf のコード スニペットは次のとおりです。このスニペットでは:

  • Google Cloud プロジェクト用に変数が定義されます。
  • Google は Terraform プロバイダとして設定されます。
  • 名前空間用に変数が定義されます。Terraform で作成したすべてのオブジェクトには、この接頭辞が付けられるため、アプリケーションの複数のバージョンを同じプロジェクトにデプロイでき、オブジェクト名は互いに競合しません。
  • 変数 MIG_VER_BLUEMIG_VER_BLUEMIG_ACTIVE_COLOR は、infra/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 のコード スニペットでは、Blue MIG と Green MIG に対する 2 つの同じモジュールを定義します。これはスプリッター モジュールで定義されたカラー、ネットワーク、サブネットワークを取得します。

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 用に作成されるオブジェクトを定義します。splitter/main.tf のコード スニペットを次に示します。これには、Green MIG と Blue MIG を切り替えるロジックが含まれています。これは google_compute_region_backend_service サービスを基盤としており、var.instance_group_blue または var.instance_group_green の 2 つのバックエンド リージョンにトラフィックを転送できます。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 では、Blue MIG と Green 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. [トリガー] テーブルで、destroy トリガーに対応する行を見つけて、[実行] をクリックします。トリガーの実行が完了すると、apply トリガーによって作成されたリソースが削除されます。

  2. ターミナル ウィンドウで次のコマンドを実行して、ブートストラップ中に作成されたリソースを削除します。

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

プロジェクトの削除

    Delete a Google Cloud project:

    gcloud projects delete PROJECT_ID

次のステップ