🚨

【Google Cloud】認証が必要な Compute プロダクトの死活監視をする方法

2023/12/07に公開

本記事は GCP(Google Cloud Platform) Advent Calendar 2023 の 5 日目の記事です.

昨年までは,機械学習で研究をしている大学 4 年生でしたが,2023 年 4 月からは新卒でエンジニアとして働いています.
現在は,MLOps エンジニア[1]をしています.

昨年は 『数独・ナンプレを画像から解く AI を作る』という記事を書いたので,興味があればぜひご覧ください.

https://qiita.com/a5chin/items/6d35283a54a1022f9b24

1. 結論

Google Cloud の機能である Synthetic Monitoring(合成モニタリング)を使うことで認証付きの Cloud Run に対して死活監視[2]をすることができました.
Synthecic Monitoring は Cloud Monitoring と JavaScript で動かす Cloud Functions の計 2 つを使用します.

synthetic_monitoring.png
Synthetic Monitoring を使った死活監視のアーキテクチャ

実際に,上記アーキテクチャをもとに Terraform[3]を用いて実装してみたので,ぜひご覧ください.
再現コマンドも記載しています↓

https://github.com/a5chin/synthetic-sample/tree/main

1.1. Synthetic Monitoring とは

https://cloud.google.com/monitoring/uptime-checks/introduction?hl=ja

合成モニターを使用すると、テスト内容と一連のテストを定義できます。たとえば、アプリケーションのログインページ、e コマースストアのチェックアウト プロセス、アプリケーションがサードパーティ サービスに対して行う API 呼び出しをテストできます。

Synthetic Monitoring は合成モニタリングと訳すようです.
上記ドキュメントによると,Synthetic Monitoring とは,テスト内容をカスタムした Google Cloud の Uptime Check(死活監視[2:1])をできるということです.

2. 導入

Google Cloud が提供している Compute プロダクト[4]は以下の 5 種類存在します.

  • Compute Engine
  • Google Kubernetes Engine
  • Cloud Run ← 本記事ではこのプロダクトに焦点を当てて死活監視[2:2]をします
  • App Engine
  • Cloud Functions

compare.png
Google Cloud における Compute プロダクトの比較

2.1. 要件を満たさなかったアーキテクチャ

実務において,認証が必要な Cloud Run に対して死活監視[2:3]が求められました.
しかし,以下は認証がない Cloud Run に対しての死活監視[2:4]アーキテクチャの例です.

認証が必要な Cloud Run に対する死活監視[2:5]のベストプラクティスに関する情報がドキュメントに明示されていないため,本記事ではその手法を紹介します.
monitoring.png
一般的な死活監視 のアーキテクチャ例

3. 実装

以下の公式ドキュメントを参考に,実装をしていきます.
Synthetic Monitoring を実装する上で,Cloud Functions に対応しているのはNode.js, TypeScript, Mochaの 3 種類ありますが,今回はNode.jsを使って実装していきます.

https://cloud.google.com/monitoring/synthetic-monitors/samples?hl=ja

3.1. 方針

synthetic_monitoring.png
Synthetic Monitoring を使った死活監視のアーキテクチャ例

先に図示した,上図アーキテクチャを作成するにあたり,以下の技術を使って認証が必要な Cloud Run に対して死活監視[2:6]を実装します.

  • Terraform[3:1]
    • Cloud Run
    • Cloud Functions
    • Cloud Monitoring
      • Uptime Check (Synthetic Monitoring)
  • JavaScript

3.2. 階層構造

Terraform[3:2]のベストプラクティスに則って,以下の様なenvironments, modulesに階層を分ける構成にして実装していきます.

https://github.com/a5chin/synthetic-sample

上記のリポジトリにおける,treeコマンドの出力を下記に示します.

.
├── environments
│   └── dev
│       ├── backend.tf
│       ├── main.tf
│       ├── provider.tf
│       ├── variables.tf
│       └── versions.tf
└── modules
    ├── main.tf
    ├── monitoring
    │   ├── bucket.tf
    │   ├── dashboard.tf
    │   ├── fuctions.tf
    │   ├── healthcheck.tf
    │   ├── policy.tf
    │   ├── src
    │   │   ├── index.js
    │   │   └── package.json
    │   └── variables.tf
    ├── run.tf
    ├── sa.tf
    └── variables.tf

3.3. ソースコード

https://github.com/a5chin/synthetic-sample

以下に主なソースコードを説明付きで,上記サンプルリポジトリから抜粋します.

index.js
const functions = require('@google-cloud/functions-framework');
const fetch = require('node-fetch');
const { runSyntheticHandler } = require('@google-cloud/synthetics-sdk-api');
const assert = require('node:assert');
const { GoogleAuth } = require('google-auth-library');
const dotenv = require('dotenv').config();

// エントリーポイント
functions.http('SyntheticFunction', runSyntheticHandler(async () => {
  const auth = new GoogleAuth();

  // 環境変数から死活監視をする URL を受け取る
  const cloudRunUrl = process.env.BASE_URL;
  const reqUrl = new URL("/api/v1/healthz", cloudRunUrl);

  const client = await auth.getIdTokenClient(reqUrl);
  const resp = client.request({url: reqUrl});
  const status = (await resp).status

  // status が 200 の時のみ healthy とする
  return await assert.equal(status, 200);
}));
modules/monitoring/fuctions.tf
resource "google_cloudfunctions2_function" "synthetic_monitoring" {
  name     = var.synthetic_settings.functions.name

  location = var.location
  project  = var.project_id

  build_config {
    runtime     = "nodejs20"

    # エントリーポイント
    entry_point = "SyntheticFunction"

    # Cloud Functions のソースコード置き場を定義する
    source {
      storage_source {
        bucket = google_storage_bucket_object.main.bucket
        object = google_storage_bucket_object.main.name
      }
    }
  }

  service_config {
    max_instance_count    = var.synthetic_settings.functions.max_instance_count
    available_memory      = var.synthetic_settings.functions.memory
    timeout_seconds       = var.synthetic_settings.functions.timeout
    service_account_email = var.run_info.invoker

    # 環境変数に死活監視対象の URI を渡す
    environment_variables = {
      BASE_URL = var.run_info.uri
    }
  }
}
modules/monitoring/healthcheck.tf
resource "google_monitoring_uptime_check_config" "synthetic_monitoring" {
  display_name = var.synthetic_settings.monitoring.name

  # どのくらいの頻度で死活監視をするか
  period  = var.synthetic_settings.monitoring.period

  # タイムアウトの時間を指定
  timeout = var.synthetic_settings.monitoring.timeout

  synthetic_monitor {
    cloud_function_v2 {
      # Clodu Run にアクセスする Cloud Functions の ID
      name = google_cloudfunctions2_function.synthetic_monitoring.id
    }
  }
}

4. 結果

以下に,構築したリソースをコンソールで表示して期待の動作をしているか確認してみます.

4.1. Cloud Run

Authentication が Require authentication となっており,API の使用には認証が必要であることがわかります.
run.png
Cloud Runの認証確認

4.2. Cloud Monitoring

Cloud Monitoring でアラートを出すためのしきい値を決めることができます.
今回は 1 分間に 1 回でもアラートがあれば Slack に通知を出すように設定をしています.
alert.png
ダッシュボードから確認したヘルスチェック結果

4.3. Synthetic Monitoring

ヘルスチェックが通っており,全てのリソースが想定通りに動いていることが確認できました.
history.png
Synthetic Monitoring パネルで確認したヘルスチェック結果

脚注
  1. ML(Machine Learning)を活用したプロダクトを世に送り出すために必要となる,モデル開発以外のすべてのエンジニアリングを行うエンジニアを指す ↩︎

  2. サーバーやその上で実行されているソフトウェアの稼働状況を継続的にチェックすることを指す ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  3. Infrastructure as Code(IaC)と呼ばれるツールの一種でインフラの構成をソースコードとして管理できる ↩︎ ↩︎ ↩︎

  4. 長谷部 光治. アプリケーションはどこで動かすべきか - 2022 春 ↩︎

GitHubで編集を提案
DeNA Engineers

Discussion