🌐

Cloud Run のサービスメッシュを試した

2024/09/04に公開2

Cloud Run のサービスメッシュを試した

以前から GKE では Cloud Service Mesh を使ってサービスメッシュを利用することができましたが、Cloud Run でもサービスメッシュを利用できるようになりました(作成時点の2024-09-02ではプレビュー段階)。
この記事ではサービスメッシュならではの機能をサンプルアプリを使って確認します。

検証シナリオ

検証環境の構成

Istio のサンプルアプリ bookinfo を Cloud Run とサービスメッシュに移植した構成

サンプルアプリとして Istio のドキュメントに掲載されている bookinfo
(本の情報やレビューが見られる Web アプリで、マイクロサービスで実装されている)のコンテナイメージを利用します。

検証する機能

下記の4点について確認を行います。

  1. サービス間の認証、認可を自動的に行う
    • 従来の Cloud Run では ID トークンを明示的にヘッダに付与してリクエストを送信する必要があり、アプリの実装で意識する必要がありました。サービスメッシュを利用することで、アプリ側では実装しなくても自動的に付与されるようになります。
  2. 独自に設定したドメイン名でのサービス ディスカバリ
    • 宛先として Cloud Run が自動的に付与する https://servicename-abcdef1234-ab.a.run.app または https://servicename-123456789012.region.run.app 形式の URL ではなく、任意のドメイン名を付与することが可能です。
  3. オブザーバビリティ
    • Cloud Trace を使った分散トレーシングを利用することができます。マイクロサービスの呼び出しは 2 の独自ドメイン名で表示されます。
  4. 高度なトラフィック管理、フォールト インジェクション
    • 条件によるトラフィックの振り分けやエラーの注入を行うことが可能になりました。従来からある Cloud Run のリビジョンのトラフィック振り分け機能を併用することも可能です。

検証環境の構築

Cloud SDK(gcloud コマンド) は 488.0.0(2024-08-13)以降が必要です。
サービスメッシュの作成は一度ですが、Cloud Run サービスの作業はクライアント側か宛先側かによって必要な作業が異なります。クライアント側と宛先側の両方の役割を持つサービス(この例では reviews サービス)では両方の作業を行います。

メッシュの作成時 クライアント側の Cloud Run デプロイ 宛先側の Cloud Run のデプロイ
サービスメッシュの作成 - -
DNS の設定 - ※1
Cloud Run のデプロイ - --network, --subnet, --mesh オプションが必要 特別なオプション不要
ネットワーク関連の設定 - - サーバーレス NEG の作成、INTERNAL_SELF_MANAGED のバックエンドサービスの作成と NEG との紐付け、HTTPRoute の作成
必要な IAM ロール - Cloud Service Mesh Client, Cloud Run Invoker -

※1: VPC から Cloud Run への接続を Private Service Connect にて行う場合は必要。本記事では Google Private Access を用いる構成のため行わない

API の有効化

必要な API 群を有効にします。具体的な API 名はドキュメントを参照してください。

サービスメッシュの作成

YAML ファイルを作成し、それをインポートしてサービスメッシュを作成します。

mesh.yaml
name: bookinfo
gcloud network-services meshes import bookinfo \
  --source=mesh.yaml \
  --location=global

DNS の設定

サービスディスカバリに利用するドメイン名を Cloud DNS のプライベートゾーンとして登録します。今回は bookinfo.internal を使います。
作成したゾーンにワイルドカードの A レコードを作成します。宛先の IP アドレスが RFC 1918(いわゆるプライベート IP)の範囲である場合、Cloud Run サービスのサイドカーがそのリクエストをメッシュ内の適切な宛先へ転送します。したがって複数の宛先サービスがメッシュにある場合でも、サービスごとに個別の IP アドレスを割り当てる必要はありません。

gcloud dns managed-zones create bookinfo \
 --description="Domain for bookinfo.internal service mesh routes" \
 --dns-name=bookinfo.internal. --networks=meshvpc \
 --visibility=private
gcloud dns record-sets create "*.bookinfo.internal." \
 --type=A --zone="bookinfo" --rrdatas=10.0.0.1 --ttl=3600

宛先となる Cloud Run サービスのデプロイ

Cloud Run サービスのデプロイ

サービスメッシュ宛にリクエストを送信しない Cloud Run サービスは従来と同じようにデプロイすることができます。Bookinfo では、ratingsdetails サービスがあてはまります。

gcloud run deploy ratings --region=us-central1 \
--image=docker.io/istio/examples-bookinfo-ratings-v1:1.19.1

details も同様なので省略)

次にサーバーレス NEG を作り、サービスメッシュに登録します。

gcloud compute network-endpoint-groups create destination-neg-ratings \
 --region=us-central1 --network-endpoint-type=serverless \
 --cloud-run-service=ratings

gcloud compute backend-services create ratings-us-central1 --global \
 --load-balancing-scheme=INTERNAL_SELF_MANAGED

gcloud compute backend-services add-backend ratings-us-central1 --global \
 --network-endpoint-group=destination-neg-ratings \
 --network-endpoint-group-region=us-central1

HTTPRoute リソースの YAML ファイルを作り、インポートします。

http_route_ratings.yaml
name: "ratings-route"
hostnames:
  - "ratings.bookinfo.internal"
meshes:
  - "projects/PROJECT_ID/locations/global/meshes/bookinfo"
rules:
  - action:
      destinations:
        - serviceName: "projects/PROJECT_ID/locations/global/backendServices/ratings-us-central1"
gcloud network-services http-routes import ratings-route \
 --source=http_route_ratings.yaml --location=global

クライアント側の Cloud Run サービスのデプロイ

Cloud Run サービスのデプロイ

クライアント側のCloud Runサービスはいくつかのオプションを付けて作成する必要があります。

gcloud beta run deploy productpage \
 --region=us-central1 \
 --image=docker.io/istio/examples-bookinfo-productpage-v1:1.19.1 \
 --network=meshvpc \
 --subnet=us-c1 \
 --mesh="projects/PROJECT_ID/locations/global/meshes/bookinfo" \
 --port 9080 \
 --set-env-vars=DETAILS_HOSTNAME=details.bookinfo.internal,DETAILS_SERVICE_PORT=80,RATINGS_HOSTNAME=ratings.bookinfo.internal,RATINGS_SERVICE_PORT=80,REVIEWS_HOSTNAME=reviews.bookinfo.internal,REVIEWS_SERVICE_PORT=80 \
 --service-account=cloudrun-in-mesh@PROJECT_ID.iam.gserviceaccount.com

接続するVPCネットワーク(--network)とサブネット(--subnet)、サービスメッシュのID(--mesh)を指定します。
このアプリはバックエンドのサービスのホスト名とポートを環境変数で指定できるようになっているので、それぞれ指定します。ホスト名は HTTPRoute の YAML ファイル内で指定したもの、ポートは 80 とします。

IAM ロールの付与

クライアント側の Cloud Run サービスが利用するサービスアカウントに下記の IAM ロールを付与します。

  • 宛先の Cloud Run サービスに対して roles/run.invoker ロール
  • roles/trafficdirector.client ロール

動作確認

productpage のトップ画面。バックエンドサービスのURLは *.bookinfo.internal に設定されている。

productpage のサービスのURLを開くと、サンプルアプリの画面が出ます。
バックエンドのサービスへ接続する URL として、最初に設定した *.bookinfo.internal の URL が指定されていることがわかります。

ページの下にある Normal userTest user リンクから本の情報が表示されるページを開きます。

productpage の書籍情報が表示されているページ

ここまでの検証では特にアプリの実装に手は入れていませんが、動作していることがわかります。検証項目1: サービス間の認証、認可を自動的に行う、と検証項目2の独自に設定したドメイン名でのサービスディスカバリ を確認することができました。
右下に Reviews served by: null と出ているのは、アプリが環境変数 HOSTNAME を必要とする仕様ですが Cloud Run のデフォルトでは設定されないからです。

次に Cloud Trace の画面を開きます。

Cloud Trace の画面。bookinfo のトレースが記録されている

先ほどアクセスしたときのトレースが記録されており、フロントの productpage アプリからバックエンドのマイクロサービスの呼び出しがされている様子がわかります。このときの宛先は *.bookinfo.internal になっていることも確認できます。

設定を変えてみる

まずはサービスメッシュではなく Cloud Run の既存の機能で reviews アプリへのトラフィックを振り分けます。

Cloud Run のトラフィック制御機能でリビジョンにトラフィックを振り分ける

何度かリロードしてレビュー欄が切り替わることを確認する

reviews アプリはバージョン2が黒い★、バージョン3が赤い★を表示する仕様です。
何度かリロードすると意図したとおりトラフィックが振り分けられていることを確認できました。

フォールトインジェクション

次はマイクロサービスの呼び出し部分で意図的にエラーが発生するようにします。HTTPRoute の YAML ファイルに faultInjectionPolicy: 以下を追加します。この例では、1.5 秒の遅延とステータスコード 500 のエラーレスポンスが必ず返るようにしています。

http_route_ratings.yaml
name: "ratings-route"
hostnames:
  - "ratings.bookinfo.internal"
meshes:
  - "projects/PROJECT_ID/locations/global/meshes/bookinfo"
rules:
  - action:
      destinations:
        - serviceName: "projects/PROJECT_ID/locations/global/backendServices/ratings-us-central1"
+      faultInjectionPolicy:
+        delay:
+          fixedDelay: "1.5s"
+          percentage: 100
+        abort:
+          httpStatus: 500
+          percentage: 100

最初に HTTPRoute を作成したときと同じように YAML ファイルをインポートします。

gcloud network-services http-routes import ratings-route \
--source=http_route_ratings.yaml \
--location=global

その他、設定可能な項目はドキュメント を参照してください。
YAML ファイルに記載する詳細な項目のスキーマはAPI 定義ファイルschemas に載っています。

動作確認

フォールト インジェクションが有効な状態のレビュー欄。ratings サービスが利用できない表示になっている

productpage をリロードするとレビュー欄の点数が表示されておらず、ratings サービスが利用不可能になったことがわかります。

フォールト インジェクションが有効な状態の Cloud Trace

Cloud Trace を確認すると、レスポンスまで 1.5 秒かかっていること、ステータスコードが 500 になっていること、ratings サービスにはトラフィックは行っていないことがわかります。

まとめ

Cloud Run と Cloud Service Mesh を組み合わせることで、マイクロサービスの実装や運用がより簡単になります。ぜひ皆様のアプリでも検証してみてください。

参考資料

Google Cloud Japan

Discussion

karszawakarszawa

めちゃくちゃ便利そう!

複数の Google Cloud プロジェクトをまたいだ Mesh も構築できるのでしょうか?