Cloud Build と Terraform を使用した Cloud Functions のシステムテスト

このチュートリアルでは、Cloud Functions でビルドしたアプリのエンドツーエンドのテストを自動化する方法を学習します。Cloud Build がテスト パイプラインを実行し、HashiCorp Terraform がテストの実行に必要な Google Cloud リソースの設定と破棄を行います。Cloud Build トリガーは、コードが commit されるたびにパイプラインを開始します。

アプリの設計やアーキテクチャの選択を行う際は、テスト性を考慮する必要があります。アプリが正常に動作することを検証するには、包括的なテストセット(自動化された単体テスト、統合テスト、エンドツーエンド テストなど)を作成し、定期的にテストを行います。さまざまな Cloud Functions シナリオで各カテゴリのテストを行う方法については、テストのベスト プラクティスガイドをご覧ください。

単体テストは実行環境に依存しないため、通常は簡単に作成して実施できます。ただし、統合テストとシステムテストは複雑になります。特にクラウド環境では一層難しくなります。エンドツーエンドのシステムテストは、特に Cloud Functions などのサーバーレス テクノロジーを使用するアプリで必要になります。多くの場合、これらのアプリは疎結合のイベント ドリブンで、独立してデプロイされる場合もあります。Google Cloud の実行環境で関数がイベントに正しく応答しているかどうかを検証するには、包括的なエンドツーエンド テストが不可欠になります。

アーキテクチャ

次の図に、このチュートリアルで使用するコンポーネントを示します。

ビルド プロジェクトとテスト プロジェクトから構成されたアーキテクチャ図。

このアーキテクチャは、次のコンポーネントから構成されています。

  • build プロジェクト。Cloud Build パイプラインをホストして実行します。
  • test プロジェクト。テスト中にサンプルアプリの Google Cloud リソースをホストします。
    • サーバーレス ウェブ パフォーマンス モニタリング チュートリアルで説明したアプリをサンプルアプリとして使用します。
    • サンプルアプリの Google Cloud リソースは、ビルドのイテレーションごとに作成され、破棄されます。Firestore データベースは例外です。作成された後、以降のすべてのビルドで再利用されます。

目標

  • Cloud Functions でビルドされたサンプルアプリの単体テストとエンドツーエンド テストを行う Cloud Build パイプラインを作成します。
  • ビルドから Terraform を実行して、アプリに必要な Google Cloud リソースの設定と破棄を行います。
  • 専用の Google Cloud テスト プロジェクトを使用してテスト環境を分離します。
  • Cloud Source Repositories に Git リポジトリを作成し、commit 後にエンドツーエンドのビルドを開始する Cloud Build トリガーを追加します。

費用

このチュートリアルでは、課金対象である次の Google Cloud コンポーネントを使用します。

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

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

始める前に

  1. Cloud プロジェクトを選択または作成します。これは、サンプルアプリをホストする test プロジェクトです。

    プロジェクト セレクタのページに移動

  2. test プロジェクトの Google Cloud プロジェクト ID をメモします。この ID は、次の環境設定のセクションで必要になります。
  3. プロジェクトでCloud Build、Cloud Functions、Cloud Source Repositories API を有効にします。

    API を有効にする

  4. Cloud Console で [Firestore] ページに移動します。
  5. Firestore ページに移動
  6. Firestore データベースを作成します。

    Firestore データベースの作成方法を学習する

  7. 別の Google Cloud プロジェクトを選択または作成します。これは、Cloud Build パイプラインをホストする build プロジェクトです。
  8. [リソースの管理] ページに移動
  9. Google Cloud プロジェクトで課金が有効になっていることを確認します。

    課金を有効にする方法について

環境設定

このチュートリアルでは、Cloud Shell でコマンドを実行します。Cloud Shell はシェル環境です。gcloud コマンドライン ツールなどの Cloud SDK がすでにインストールされており、現在のプロジェクトの値もすでに設定されています。Cloud Shell の初期化には数分かかることがあります。

  1. Cloud Console で build プロジェクトに選択し、Cloud Shell を開きます。

    Cloud Shell を開く

  2. 前の手順でコピーした test Google Cloud プロジェクト ID の変数を設定します。

    export TEST_PROJECT=your-test-project-id
    

    次のように置き換えます。

    • your-test-project-id: test Google Cloud プロジェクトの ID。
  3. 現在の build Google Cloud プロジェクトのプロジェクト ID とプロジェクト番号を変数として設定します。

    export BUILD_PROJECT=$(gcloud config get-value core/project)
    export BUILD_PROJECT_NUM=$(gcloud projects list) \
        --filter="$BUILD_PROJECT" --format="value(PROJECT_NUMBER)"
    
  4. デプロイ リージョンの変数を設定します。

    export REGION=us-central1
    

    このチュートリアルでは us-central1 リージョンを使用しますが、Cloud Functions を使用できる任意のリージョンに変更できます。

  5. このチュートリアルで使用するサンプルアプリのコードを格納するリポジトリのクローンを作成します。

    git clone \
        https://github.com/GoogleCloudPlatform/solutions-serverless-web-monitoring.git
    
  6. プロジェクト ディレクトリに移動します。

    cd solutions-serverless-web-monitoring
    

Terraform を使用したテスト インフラストラクチャの作成

このチュートリアルでは、Terraform を使用して、テスト プロジェクト内で Google Cloud リソースの作成と破棄を自動的に行います。ビルドごとに独立したリソースを作成すると、それぞれのテストを分離できます。テストを分離すると、ビルドが同時に行い、特定のリソースに対してテスト アサーションを行うことができます。各ビルドの最後にリソースを破棄することで、コストを最小限に抑えることができます。

このチュートリアルでは、サーバーレス ウェブ モニタリング チュートリアルで説明したアプリをデプロイします。このアプリは、Cloud Functions、Cloud Storage バケット、Pub/Sub リソース、Firestore データベースで構成されています。Terraform 構成では、これらのリソースの作成に必要な手順を定義します。Firestore データベースは Terraform によってデプロイされません。1 度作成したデータベースがすべてのテストで再利用されます。

Terraform 構成ファイル main.tf の次のサンプルコードは、Cloud Functions trace をデプロイするために必要な手順を示しています。完全な構成については、ファイル全体をご覧ください。

data "archive_file" "local_tracer_source" {
  type        = "zip"
  source_dir  = "./functions/tracer"
  output_path = "${var.local_output_path}/tracer.zip"
}

resource "google_storage_bucket_object" "gcs_tracer_source" {
  name   = "tracer.zip"
  bucket = "${google_storage_bucket.bucket_source_archives.name}"
  source = "${data.archive_file.local_tracer_source.output_path}"
}

resource "google_cloudfunctions_function" "function_tracer" {
  name = "tracer-${var.suffix}"
  project = "${var.project_id}"
  region = "${var.region}"
  available_memory_mb = "1024"
  entry_point = "trace"
  runtime = "nodejs8"
  trigger_http = "true"
  source_archive_bucket = "${google_storage_bucket.bucket_source_archives.name}"
  source_archive_object = "${google_storage_bucket_object.gcs_tracer_source.name}"
  environment_variables = {
    BUCKET_METRICS = "${google_storage_bucket.bucket_metrics.name}"
    ALLOWED_HOSTS = "${var.allowed_hosts}"
  }
}

// prevent unauthenticated invocations
resource "google_cloudfunctions_function_iam_binding" "tracer_disallow_unauthenticated" {
  project = "${var.project_id}"
  region = "${var.region}"
  cloud_function = "${google_cloudfunctions_function.function_tracer.name}"
  role = "roles/cloudfunctions.invoker"
  members = [
  ]
  depends_on = [
    google_cloudfunctions_function.function_tracer
  ]
}

このステップでは、Terraform 構成を実行してテストリソースをデプロイします。これらのリソースは、後のステップで Cloud Build によって自動的にデプロイされます。

  1. Cloud Shell で Terraform を初期化します。

    docker run -v $(pwd):/app -w /app hashicorp/terraform:0.12.0 init
    

    公開の Terraform Docker イメージを使用します。Docker は Cloud Shell にインストールされています。現在の作業ディレクトリはボリュームとしてマウントされるため、Docker コンテナは Terraform 構成ファイルを読み取ることができます。

  2. Terraform の apply コマンドを使用してリソースを作成します。

    docker run -v $(pwd):/app -w /app hashicorp/terraform:0.12.0 apply \
        --auto-approve \
        -var "project_id=$TEST_PROJECT" \
        -var "region=$REGION" \
        -var "suffix=$TEST_PROJECT"
    

    このコマンドには、テストリソースを作成する Google Cloud プロジェクトとリージョンを指定する変数が含まれています。また、このステップに名前付きのリソースを作成する際に使用される接尾辞も含まれています。後のステップで、Cloud Build が適切な接尾辞を自動的に指定します。

    オペレーションが完了するまでに数分かかります。

  3. test プロジェクトにリソースが作成されたことを確認します。

    gcloud functions list --project $TEST_PROJECT
    

    出力には、前に指定した接尾辞で終わる Cloud Functions が 3 つ表示されます。

エンドツーエンド テストの実施

このセクションでは、前のセクションでデプロイしたテスト インフラストラクチャにエンドツーエンドのテストを実施します。

次のコードスニペットでは、複数のテストを行っています。テストでは、成功と失敗の両方のシナリオを検証します。テスト パイプラインの概要は次のとおりです。

  • まず、テストで trace 関数を呼び出します。この呼び出しにより、他の機能をトリガーするアプリを介してイベントのフローが開始します。
  • 次に、各関数の動作を検証します。オブジェクトが Cloud Storage に書き込まれ、結果が Firestore に格納され、Pub/Sub アラートが生成されたことを確認します。
def test_e2e_pass():
  run_pipeline('http://www.example.com/', True)

def test_e2e_fail():
  run_pipeline('https://cloud.google.com/docs/tutorials', False)

def run_pipeline(url, should_pass):
  """Triggers the web analysis pipeline and verifies outputs of each stage.

  Args:
    url (str): The page to analyze.
    should_pass (bool): Whether the page should load within the threshold time.
  """
  trace_response = call_tracer(url)
  filename = assert_tracer_response(trace_response)
  assert_gcs_objects(filename)
  assert_firestore_doc(filename, should_pass)
  assert_pubsub_message(should_pass)

  # clean up
  delete_gcs_objects(filename)
  delete_firestore_doc(filename)

エンドツーエンド テストを実施するには、次の操作を行います。

  1. Cloud Shell で、新しい virtualenv 環境を作成します。virtualenv ユーティリティは、すでに Cloud Shell にインストールされています。

    virtualenv venv
    
  2. virtualenv 環境を有効にします。

    source venv/bin/activate
    
  3. 必要な Python ライブラリをインストールします。

    pip install -r requirements.txt
    
  4. エンドツーエンド テストを開始します。

    python -m pytest e2e/ --tfstate terraform.tfstate
    

    Terraform 状態ファイルを渡します。このファイルには、前のセクションで作成したテストリソースの詳細が含まれています。

    テストが完了するまでに数分かかることがあります。2 つのテストに合格したことを示すメッセージが表示されます。警告は無視してかまいません。

  5. Terraform の destroy コマンドを使用して、テストリソースを破棄します。

    docker run -v $(pwd):/app -w /app hashicorp/terraform:0.12.0 destroy \
        --auto-approve \
        -var "project_id=$TEST_PROJECT" \
        -var "region=$REGION" \
        -var "suffix=$TEST_PROJECT"
    
  6. リソースが破棄されたことを確認します。

    gcloud functions list --project $TEST_PROJECT
    

    前に指定した接尾辞で終わる Cloud Functions はなくなりました。

Cloud Build パイプラインの送信

このセクションでは、Cloud Build を使用してテスト パイプラインを自動化します。

Cloud Build の権限を設定する

Cloud Build サービス アカウントを使用して Cloud Build を実行します。ビルドによって実行されるシステムテストでは、Cloud Functions、Cloud Storage バケット、Pub/Sub リソース、Firestore ドキュメントを作成して操作します。これらの処理を行うには Cloud Build に以下のものが必要です。

この手順では、適切なロールを追加してから、Cloud Build サービス アカウントを追加します。

  1. Cloud Shell で、適切な IAM ロールをデフォルトの Cloud Build サービス アカウントに追加します。

    for role in cloudfunctions.developer pubsub.editor storage.admin datastore.user; do \
        gcloud projects add-iam-policy-binding $TEST_PROJECT \
        --member="serviceAccount:$BUILD_PROJECT_NUM@cloudbuild.gserviceaccount.com" \
        --role="roles/$role"; \
        done
    
  2. テスト プロジェクトの App Engine サービス アカウントの serviceAccountUser として、Cloud Build サービス アカウントを追加します。

    gcloud iam service-accounts add-iam-policy-binding \
        $TEST_PROJECT@appspot.gserviceaccount.com \
        --member="serviceAccount:$BUILD_PROJECT_NUM@cloudbuild.gserviceaccount.com" \
        --role=roles/iam.serviceAccountUser \
        --project $TEST_PROJECT
    

手動ビルドの送信

ビルドでは次の 4 つの論理タスクを実行します。

  • 単体テストの実施
  • サンプルアプリのデプロイ
  • エンドツーエンド テストの実施
  • サンプルアプリの破棄

cloudbuild.yaml ファイルの次のコードスニペットを見てみましょう。このスニペットは、Terraform を使用してサンプルアプリをデプロイし、エンドツーエンド テストを実行する個々の Cloud Build ステップを示しています。

# setup Terraform using public terraform Docker image
- id: terraform-init
  name: hashicorp/terraform:0.12.0
  args: ['init']

# deploy the required GCP resources
- id: terraform-apply
  name: hashicorp/terraform:0.12.0
  args: ['apply', '-auto-approve']
  env:
    - 'TF_VAR_project_id=$_TEST_PROJECT_ID'
    - 'TF_VAR_region=$_REGION'
    - 'TF_VAR_suffix=$BUILD_ID'

# run end-to-end tests to verify live interactions
- id: end-to-end-tests
  name: 'python:3.7-slim'
  entrypoint: /bin/sh
  args:
    - -c
    - 'pip install -r requirements.txt && python -m pytest e2e --tfstate terraform.tfstate'

手動ビルドを Cloud Build に送信し、エンドツーエンド テストを実施するには、次の操作を行います。

  • Cloud Shell で、次を入力します。

    gcloud builds submit --config cloudbuild.yaml \
        --substitutions=_REGION=$REGION,_TEST_PROJECT_ID=$TEST_PROJECT
    

    ビルドの実行には数分かかります。次のビルドステップが実行されます。

    • Cloud Build が、置換を使用して、作成するテストリソースの Google Cloud プロジェクトとリージョンを指定します。

    • Cloud Build が build Google Cloud プロジェクトでビルドを実行します。テストリソースは別の test プロジェクトに作成されます。

    ビルドログが Cloud Shell にストリーミングされるため、ビルドの進行状況を追跡できます。ログのストリーミングはビルドの完了時に終了します。最後の terraform-destroy ビルドステップが成功し、ビルドの完了を通知するメッセージが表示されます。

テストの自動化

継続的インテグレーション(CI)の基本原則は、一連の包括的な自動テストを定期的に行うことです。通常、ビルドテスト パイプラインは、共有コード リポジトリへの commit ごとに実行されます。これにより、共有リポジトリに対する各 commit がテストおよび検証され、チームが問題を早期に検出できるようになります。

以降のセクションでは、次の操作を行います。

  • Cloud Source Repositories に Git リポジトリを作成する。
  • commit ごとにエンドツーエンドのビルドを実行するように Cloud Build トリガーを追加する。
  • コードをリポジトリに push してビルドをトリガーする。

Cloud Source Repositories のリポジトリと Cloud Build トリガーを作成する

  1. Cloud Shell で、新しい Cloud Source Repositories のリポジトリを作成します。

    gcloud source repos create serverless-web-monitoring
    
  2. Cloud Console で、Cloud Build の [トリガー] ページを開きます。

    [トリガー] ページに移動

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

    [トリガーの作成] ページが開きます。

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

    • [名前] フィールドに end-to-end-tests と入力します。
    • [イベント] で [ブランチに push する] を選択します。
    • [ソース] で、[リポジトリ] として severless-web-monitoring を選択し、[ブランチ] として ^master$ を選択します。
    • [ビルド構成] で [Cloud Build 構成ファイル] を選択します。
    • [Cloud Build 構成ファイルの場所] フィールドに「cloudbuild.yaml」と入力します。
    • 変数置換を追加して、テストリソースを作成する Google Cloud リージョンを指定するには、[変数を追加] をクリックします。

      • 変数: _REGION
      • : your-test-region

        次のように置き換えます。

        • your-test-region: Cloud Shell の $REGION 変数の値。
    • 別の変数置換を追加して、テストリソースをホストするプロジェクトの ID を指定するには、[変数を追加] をクリックします。

      • 変数: _TEST_PROJECT_ID
      • : your-test-project

        次のように置き換えます。

        • your-test-project: Cloud Shell の $TEST_PROJECT 変数の値。
  5. [作成] をクリックして、ビルドトリガーを保存します。

ビルドを開始する

  1. Cloud Shell で、新しいリモートとしてリポジトリを git config に追加します。

    git remote add csr \
        https://source.developers.google.com/p/$BUILD_PROJECT/r/serverless-web-monitoring
    
  2. ビルドをトリガーするには、コードをリポジトリに push します。

    git push csr master
    
  3. 最新のビルドを一覧表示します。

    gcloud builds list --limit 3
    

    出力で、ビルドのステータスが WORKING と表示されていれば、ビルドが想定どおりにトリガーされています。

  4. 次のステップで使用するため、WORKING ビルドの ID をコピーします。

  5. ビルドログを Cloud Console にストリーミングします。

    gcloud builds log --stream build-id
    

    次のように置き換えます。

    • build-id: 前の手順でコピーした WORKING ビルドの ID。

    ログのストリーミングはビルドの完了時に終了します。最後の terraform-destroy ビルドステップが成功し、ビルドの完了を通知するメッセージが表示されます。

クリーンアップ

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

プロジェクトの削除

  1. Cloud Console で [リソースの管理] ページに移動します。

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

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

次のステップ