Cloud Build を使用した GitOps スタイルの継続的デリバリー


このページでは、ホストされるプロダクトと一般的な GitOps の手法のみを使用して、Google Cloud で継続的インテグレーションとデリバリー(CI / CD)パイプラインを作成する方法について説明します。

Google のエンジニアは、長い間、構成ファイルとデプロイ ファイルをプライマリ ソースコード リポジトリに保存してきました。この手法は、Site Reliability Engineering, Chapter 8(Beyer 他 2016 年)に掲載されており、Kelsey Hightower の Google Cloud Next '17 基調講演で説明されました。

GitOps の重要な要素は「コードとしての環境」という考え方です。Git リポジトリに保存されたファイル(Kubernetes マニフェストなど)を使用してデプロイを宣言的に記述します。

このチュートリアルで作成する CI / CD パイプラインでは、commit されたコードからコンテナ イメージを自動的にビルドして Artifact Registry に格納します。さらに、Git リポジトリの Kubernetes マニフェストを更新し、そのマニフェストを使用してアプリケーションを Google Kubernetes Engine にデプロイします。

CI / CD パイプラインのアーキテクチャ

このチュートリアルでは、2 つの Git リポジトリを使用します。

  • app リポジトリ: アプリケーション自体のソースコードが含まれます。
  • env リポジトリ: Kubernetes Deployment のマニフェストが含まれます。

app リポジトリに変更を push すると、Cloud Build パイプラインはテストを実施し、コンテナ イメージを構築して、Artifact Registry に push します。イメージを push した後、Cloud Build は、Deployment マニフェストを更新して env リポジトリに push します。これにより、マニフェストを GKE クラスタに適用する別の Cloud Build パイプラインがトリガーされ、正常終了した場合は、マニフェストが env リポジトリの別のブランチに格納されます。

ライフサイクルと用途が異なるため、app リポジトリと env リポジトリは別々にしておきます。app リポジトリの主なユーザーは実際の人間であり、このリポジトリは特定のアプリケーション専用です。env リポジトリの主なユーザーは自動化されたシステム(Cloud Build など)であり、このリポジトリは複数のアプリケーションで共有される場合があります。env リポジトリにはいくつかのブランチがあり、それぞれ特定の環境にマッピングし(このチュートリアルでは本番環境のみを使用します)、特定のコンテナ イメージを参照しますが、app リポジトリはこれとは異なります。

このチュートリアルを終了すると、次のことを簡単に行うことができるシステムが完成します。

  • Cloud Build の履歴を確認して、失敗したデプロイと成功したデプロイを区別する。
  • env リポジトリの production ブランチを確認して、現在使用されているマニフェストにアクセスする。
  • 対応する Cloud Build ビルドを再実行して、以前のバージョンにロールバックする。

CI / CD パイプラインのフロー

このチュートリアルについて

このチュートリアルでは、Cloud Source Repositories を使用して Git リポジトリをホストしていますが、GitHub、Bitbucket、GitLab などの他のサードパーティ製品でも同じ結果が得られます。

このパイプラインでは、デプロイ前に検証メカニズムは実装されません。GitHub、Bitbucket、GitLab を使用する場合は、それに応じて pull リクエストを使用するようにパイプラインを変更できます。

高度なデプロイ パターン(Blue/Green、Canary Analysis、マルチクラウドなど)を実装するチームには Spinnaker をおすすめしますが、小規模な組織やプロジェクトでは CI / CD 戦略の正常完了に、それらの機能セットを必要としない場合があります。このチュートリアルでは、ツールを使用して GKE でホストされているアプリケーションに適した CI / CD パイプラインを作成する方法について説明します。

わかりやすくするため、このチュートリアルでは env リポジトリで単一の環境(本番環境)を使用しますが、必要に応じて拡張して複数の環境にデプロイすることもできます。

目標

  • Cloud Source Repositories で Git リポジトリを作成する。
  • Cloud Build でコンテナ イメージを作成し、Container Registry に保存する。
  • CI パイプラインを作成する。
  • CD パイプラインを作成する。
  • CI / CD パイプラインをテストする。

費用

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

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

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

始める前に

  1. Google Cloud プロジェクトを選択または作成します。

    [リソースの管理] に移動

  2. プロジェクトに対する課金を有効にします。

    課金を有効にする

  3. このチュートリアルに記載されているコマンドを実行するために、Cloud Shell を開きます。Cloud Shell は Google Cloud のインタラクティブなシェル環境であり、ウェブブラウザからプロジェクトやリソースを管理できます。

    Cloud Shell に移動

  4. たとえば、gcloud config get-value project コマンドで選択したプロジェクトの ID が返されない場合は、プロジェクトが使用されるように Cloud Shell を構成します。

    gcloud config set project [PROJECT_ID]
    
  5. Cloud Shell で、必要な API を有効にします。

    gcloud services enable container.googleapis.com \
        cloudbuild.googleapis.com \
        sourcerepo.googleapis.com \
        artifactregistry.googleapis.com
    
  6. us-central1 リージョンに my-repository という名前の Artifact Registry Docker リポジトリを作成し、コンテナ イメージを保存します。

    gcloud artifacts repositories create my-repository \
      --repository-format=docker \
      --location=us-central1
    
  7. Cloud Shell で、このチュートリアルのサンプル アプリケーションをデプロイするために使用する GKE クラスタを作成します。

    Autopilot

    hello-cloudbuild という名前の Autopilot クラスタを作成します。

    gcloud container clusters create-auto hello-cloudbuild \
        --region us-central1
    

    標準

    hello-cloudbuild という名前の 1 ノードの Standard クラスタを作成します。

    gcloud container clusters create hello-cloudbuild \
        --num-nodes 1 --region us-central1
    
  8. Git を Cloud Shell で使用したことがない場合は、名前とメールアドレスを使用して Git を構成します。Git は名前とメールアドレスを使用して、Cloud Shell で作成する commit の作成者を識別します。

    git config --global user.email "YOUR_EMAIL_ADDRESS"
    git config --global user.name "YOUR_NAME"
    

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

Cloud Source Repositories で Git リポジトリを作成する

このセクションでは、このチュートリアルで使用する 2 つの Git リポジトリ(app と env)を作成し、いくつかのサンプルコードを使用してアプリ 1 を初期化します。

  1. Cloud Shell で、2 つの Git リポジトリを作成します。

    gcloud source repos create hello-cloudbuild-app
    gcloud source repos create hello-cloudbuild-env
    
  2. GitHub からサンプルコードのクローンを作成します。

    cd ~
    git clone https://github.com/GoogleCloudPlatform/gke-gitops-tutorial-cloudbuild \
        hello-cloudbuild-app
    
  3. Cloud Source Repositories をリモートとして構成します。

    cd ~/hello-cloudbuild-app
    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.7-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 で次のコマンドを使用して、最新の commit に基づいて Cloud Build ビルドを作成します。

    cd ~/hello-cloudbuild-app
    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 に push するように Cloud Build を構成します。Cloud Source Repositories に新しい commit を push すると、このパイプラインが自動的にトリガーされます。コードに含まれる cloudbuild.yaml ファイルは、パイプラインの構成です。

steps:
# This step runs the unit tests on the app
- name: 'python:3.7-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 と入力します。
    • [イベント] で [ブランチに push する] を選択します。
    • [ソース] で、[リポジトリ] として hello-cloudbuild-app を選択し、[ブランチ] として ^master$ を選択します。
    • [ビルド構成] で [Cloud Build 構成ファイル] を選択します。
    • [Cloud Build 構成ファイルの場所] フィールドで、/ の後に「cloudbuild.yaml」と入力します。
  4. [作成] をクリックして、ビルドトリガーを保存します。

    ヒント: 多数のプロジェクトにビルドトリガーを作成する必要がある場合は、Build Triggers API を使用できます。

  5. Cloud Shell でアプリケーション コードを Cloud Source Repositories に push して、Cloud Build で CI パイプラインをトリガーします。

    cd ~/hello-cloudbuild-app
    git push google master
    
  6. Cloud Build コンソールを開きます。

    Cloud Build に移動

    最近実行し、終了したビルドが表示されます。ビルドをクリックすると、その実行を追跡してログを確認できます。

継続的デリバリー パイプラインを作成する

Cloud Build は継続的デリバリー パイプラインにも使用されます。commit が hello-cloudbuild-env リポジトリの candidate ブランチに push されるたびにパイプラインが実行されます。パイプラインは、新しいバージョンのマニフェストを Kubernetes クラスタに適用し、正常に終了した場合はマニフェストを production ブランチにコピーします。このプロセスには次のような特徴があります。

  • candidate ブランチはデプロイの試行の履歴です。
  • production ブランチは、成功したデプロイの履歴です。
  • Cloud Build には、成功したデプロイと失敗したデプロイのビューがあります。
  • Cloud Build では、対応するビルドを再実行して、以前のデプロイにロールバックできます。また、ロールバックによって、production ブランチが更新され、デプロイの履歴が正確に反映されます。

継続的インテグレーション パイプラインを変更して、hello-cloudbuild-env リポジトリの candidate ブランチを更新し、継続的デリバリー パイプラインをトリガーします。

Cloud Build に GKE へのアクセス権を付与する

Kubernetes クラスタにアプリケーションをデプロイするには、Cloud Build に Kubernetes Engine デベロッパーの Identity and Access Management ロールが必要です。

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

Console

  1. Google Cloud Console で、Cloud Build の [設定] ページを開きます。

    Cloud Build の設定を開く

    [サービス アカウントの権限] ページが表示されます。

    サービス アカウント権限ページのスクリーンショット

  2. Kubernetes Engine デベロッパー ロールのステータスを [有効化] に設定します。

hello-cloudbuild-env リポジトリを初期化する

hello-cloudbuild-env リポジトリを 2 つのブランチ(production と candidate)と、デプロイ プロセスを記述した Cloud Build 構成ファイルで初期化する必要があります。

  1. Cloud Shell で hello-cloudbuild-env リポジトリのクローンを作成し、production ブランチを作成します。

    cd ~
    gcloud source repos clone hello-cloudbuild-env
    cd ~/hello-cloudbuild-env
    git checkout -b production
    
  2. hello-cloudbuild-app リポジトリから入手できる cloudbuild-delivery.yaml ファイルをコピーして変更してから commit します。

    cd ~/hello-cloudbuild-env
    cp ~/hello-cloudbuild-app/cloudbuild-delivery.yaml ~/hello-cloudbuild-env/cloudbuild.yaml
    git add .
    git commit -m "Create cloudbuild.yaml for deployment"
    

    cloudbuild-delivery.yaml ファイルには、Cloud Build で実行されるデプロイ プロセスが記述されています。次の 2 つのステップがあります。

    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 で使用できるように push します。

    git checkout -b candidate
    git push origin production
    git push origin candidate
    
  4. hello-cloudbuild-env リポジトリの Cloud Build サービス アカウントに Source Repository 書き込みの 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 ブランチへの push によってトリガーされるように、Cloud Build を構成します。

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

    [トリガー] に移動

  2. [トリガーを作成] をクリックします。

  3. 次のオプションを入力します。

    • [名前] フィールドに hello-cloudbuild-deploy と入力します。
    • [イベント] で [ブランチに push する] を選択します。
    • [ソース] で、[リポジトリ] として hello-cloudbuild-env を選択し、[ブランチ] として ^candidate$ を選択します。
    • [構成] で [Cloud Build 構成ファイル(YAML または JSON)] を選択します。
    • [Cloud Build 構成ファイルの場所] フィールドで、/ の後に「cloudbuild.yaml」と入力します。
  4. [作成] をクリックします。

継続的デリバリー パイプラインをトリガーするように継続的インテグレーション パイプラインを変更する

このセクションでは、Kubernetes マニフェストの新しいバージョンを生成して hello-cloudbuild-env リポジトリに push し、継続的デリバリー パイプラインをトリガーする手順を継続的インテグレーション パイプラインに追加します。

  1. cloudbuild.yaml ファイルを cloudbuild-trigger-cd.yaml ファイルの拡張した例に置き換えます。

    cd ~/hello-cloudbuild-app
    cp cloudbuild-trigger-cd.yaml cloudbuild.yaml
    

    cloudbuild-trigger-cd.yaml は、cloudbuild.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. 変更を commit して Cloud Source Repositories に push します。

    cd ~/hello-cloudbuild-app
    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 リポジトリに push され、これにより、継続的デリバリー パイプラインがトリガーされます。

  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 ~/hello-cloudbuild-app
    sed -i 's/Hello World/Hello Cloud Build/g' app.py
    sed -i 's/Hello World/Hello Cloud Build/g' test_app.py
    
  4. 変更を commit して Cloud Source Repositories に push します。

    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. 利用可能な 2 番目に新しいビルドをクリックします。

  3. [再ビルド] をクリックします。

  4. ビルドが完了したら、ブラウザでアプリケーションを再読み込みします。「Hello World!」が再表示されます。

クリーンアップ

このチュートリアルで使用したリソースについて、Google Cloud アカウントに課金されないようにするには、リソースを含むプロジェクトを削除するか、プロジェクトを維持して個々のリソースを削除します。

  1. Google Cloud コンソールで、[リソースの管理] ページに移動します。

    [リソースの管理] に移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

リソースを削除する

このチュートリアルで使用した Google Cloud プロジェクトを残しておく場合は、個々のリソースを削除します。

  1. ローカルの Git リポジトリを削除します。

    cd ~
    rm -rf ~/hello-cloudbuild-app
    rm -rf ~/hello-cloudbuild-env
    
  2. Cloud Source Repositories 内の Git リポジトリを削除します。

    gcloud source repos delete hello-cloudbuild-app --quiet
    gcloud source repos delete hello-cloudbuild-env --quiet
    
  3. Cloud Build のトリガーを削除します。

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

      [トリガー] に移動

    2. トリガーごとに、その他アイコン()、[削除] の順にクリックします。

  4. Artifact Registry の Docker リポジトリを削除します。

    gcloud artifacts repositories delete my-repository \
        --location=us-central1
    
  5. GKE に接続するために Cloud Build に付与された権限を削除します。

    PROJECT_NUMBER="$(gcloud projects describe ${PROJECT_ID} \
        --format='get(projectNumber)')"
    gcloud projects remove-iam-policy-binding ${PROJECT_NUMBER} \
        --member=serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
        --role=roles/container.developer
    
  6. GKE クラスタを削除します。

    gcloud container clusters delete hello-cloudbuild \
       --region us-central1
    

次のステップ