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

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

Google のエンジニアは、長い間、構成ファイルとデプロイ ファイルをプライマリ ソースコード リポジトリに保存してきました。この手法は、Site Reliability Engineering, Chapter 8Beyer 他 2016 年)に記載されており、Kelsey Hightower の Google Cloud Next '17 基調講演で説明されました。「GitOps」という用語自体は、Weaveworks によって考案されました。GitOps の重要な要素は「コードとしての環境」という考え方です。Git リポジトリに格納されたファイル(Kubernetes マニフェストなど)を使用してデプロイを宣言的に記述します。

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

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

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

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

app リポジトリに変更を push すると、Cloud Build パイプラインはテストを実施し、コンテナ イメージを構築して、Container 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 リポジトリで単一の環境(本番環境)を使用しますが、必要に応じて拡張して複数の環境にデプロイすることもできます。

料金

始める前に

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

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

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

    課金を有効にする

  3. このチュートリアルに記載されているコマンドを実行するために、Cloud Shell を開きます。

    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 \
        containeranalysis.googleapis.com
    
  6. Cloud Shell で、このチュートリアルのサンプル アプリケーションをデプロイするために使用する GKE クラスタを作成します。

    gcloud container clusters create hello-cloudbuild \
        --num-nodes 1 --zone us-central1-b
    
  7. 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 でコンテナ イメージを作成し、Container Registry に保存できます。

  1. Cloud Shell で次のコマンドを使用して、最新の commit に基づいて Cloud Build ビルドを作成します。

    cd ~/hello-cloudbuild-app
    COMMIT_ID="$(git rev-parse --short=7 HEAD)"
    gcloud builds submit --tag="gcr.io/${PROJECT_ID}/hello-cloudbuild:${COMMIT_ID}" .
    

    このコマンドを実行すると、Cloud Build はコンテナ イメージの作成によって生成されたログをターミナルにストリーミングします。

  2. ビルドが完了したら、新しいコンテナ イメージが実際に Container Registry で利用できることを確認します。

    Container Registry に移動

    Container Registry 内の hello-cloudbuild イメージ

継続的インテグレーション パイプラインを作成する

このセクションでは、小規模な単体テストを自動的に実施し、コンテナ イメージを構築して Container 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'
  - 'gcr.io/$PROJECT_ID/hello-cloudbuild:$SHORT_SHA'
  - '.'

# This step pushes the image to Container Registry
# The PROJECT_ID and SHORT_SHA variables are automatically
# replaced by Cloud Build.
- name: 'gcr.io/cloud-builders/docker'
  id: Push
  args:
  - 'push'
  - 'gcr.io/$PROJECT_ID/hello-cloudbuild:$SHORT_SHA'
  1. Cloud Build の [トリガー] ページを開きます。

    [トリガー] に移動

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

  3. ソースとして [Cloud Source Repositories] を選択し、[続行] をクリックします。

  4. hello-cloudbuild-app リポジトリを選択し、[続行] をクリックします。

  5. [トリガー設定] 画面で、以下のパラメータを入力します。

    • 名前: hello-cloudbuild
    • ブランチ(正規表現): master
    • ビルド構成: cloudbuild.yaml
  6. [トリガーを作成] をクリックします。

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

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

    cd ~/hello-cloudbuild-app
    git push google master
    
  8. 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 にコンテナ デベロッパーの IAM 役割が必要です。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

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_ZONE=us-central1-b'
      - '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. ソースとして [Cloud Source Repositories] を選択し、[続行] をクリックします。

  4. hello-cloudbuild-env リポジトリを選択し、[続行] をクリックします。

  5. [トリガー設定] 画面で、以下のパラメータを入力します。

    • 名前: hello-cloudbuild-deploy
    • ブランチ(正規表現): candidate
    • ビルド構成: cloudbuild.yaml
  6. [トリガーを作成] をクリックします。

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

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

  1. アプリ リポジトリ用の cloudbuild.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 gcr.io/${PROJECT_ID}/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 Platform アカウントに課金されないようにする手順は次のとおりです。

  1. GCP Console で [プロジェクト] ページに移動します。

    プロジェクト ページに移動

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

リソースを削除する

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

  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. Container Registry 内のイメージを削除します。

    gcloud beta container images list-tags \
        gcr.io/${PROJECT_ID}/hello-cloudbuild \
        --format="value(tags)" | \
        xargs -I {} gcloud beta container images delete \
        --force-delete-tags --quiet \
        gcr.io/${PROJECT_ID}/hello-cloudbuild:{}
    
  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 \
        --zone us-central1-b
    

次のステップ

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Kubernetes Engine のチュートリアル