
【Google Cloud】Google Cloudで発生したエラーもAWSのSNSからメール通知したい
データ事業本部の川中子(かわなご)です。
今回はGoogle Cloudのエラー通知方法についてです。
ネイティブで用意されているメール通知の方法も存在するのですが、
とてもシンプルな仕様のため、要件によっては別の方法が必要になってきます。
特殊なケースではありますが、AWSのSNSを利用したメール通知の構成を検証したので、
その内容を本記事ではご紹介したいと思います。
Google Cloudにおけるメール通知方法
公式ではCloud Monitoring
を利用したメール通知方法や、
SendGrid
などのサードパーティツールを利用したメール通知方法が紹介されています。
しかしCloud Monitoring
ではメールのフォーマットが以下の形式に固定され、
エラーの内容などを柔軟に盛り込むことができない仕様になっています。
またサードパーティツールでは、そもそも利用が制限されるケースがあったり、
APIキーの管理が発生するなどの運用上のデメリットも存在します。
今回紹介する構成はある種特殊なケースではあるのですが、
AWSとGoogle Cloudのマルチクラウド環境がある場合には選択肢になると思います。
検証概要
今回構築した全体のアーキテクチャは以下の通りです。
処理の流れは次のようになります。
- HTTPリクエストでCloud Runのジョブを実行しエラー発生
- エラー情報をPub/Subトピックに送信
- Pub/SubトリガーでCloud Run Functionsが起動
- メタデータサーバーからID Tokenを取得
- AWS STSでIAMロールを引き受け
- 取得した一時認証情報を使ってAWSのSNSにメッセージをパブリッシュ
- SNS経由でメール通知
本構成で使用している認証の方法については、以前投稿したブログでも紹介しています。
詳細については以下の記事を参照してください。
検証準備
使用したリソース
検証では以下のリソースを作成しています。
Google Cloud
リソース種別 | リソース名 | 説明 |
---|---|---|
サービスアカウント | cm-kawanago-sa | AWS認証用サービスアカウント |
Pub/Subトピック | cm-kawanago-error-topic | エラー情報を受け取るトピック |
Cloud Run Functions | cm-kawanago-error-trigger | エラー発生用関数 |
Cloud Run Functions | cm-kawanago-bridge-sns | SNSパブリッシュ用関数 |
AWS
リソース種別 | リソース名 | 説明 |
---|---|---|
IAMロール | cm-kawanago-google-jwt-role | Google Cloud認証用IAMロール |
SNSトピック | cm-kawanago-notification | 通知用SNSトピック |
上記リソースの作成に加えて、Google Cloudで作成したサービスアカウントを、
AWS側で作成したIAMロールの信頼ポリシーにOIDCプロバイダーとして記載しています。
またSNSトピックのサブスクリプションに対して、通知先のメールアドレスを登録しています。
エラー発生用のCloud Run Function
今回は検証用として、意図的にエラーを発生させる関数を作成しました。
Pub/Subトピックに対してエラーメッセージをパブリッシュしています。
なお各種変数は環境変数としてデプロイ時に設定しています。
import functions_framework
from google.cloud import pubsub_v1
import json
import os
from datetime import datetime, timezone
@functions_framework.http
def error_trigger(request):
"""
HTTPリクエストを受け取り、意図的にエラー情報をPub/Subに送信する関数
"""
project_id = os.environ.get('GCP_PROJECT', '{projectID}')
topic_id = os.environ.get('PUBSUB_TOPIC', 'cm-kawanago-error-topic')
# Pub/Sub Publisher クライアントを初期化
publisher = pubsub_v1.PublisherClient()
topic_path = publisher.topic_path(project_id, topic_id)
# エラー情報を作成
error_message = {
'error_type': 'TestError',
'message': 'これはマルチクラウドエラー通知のテストです',
'severity': 'ERROR',
'source': 'cm-kawanago-error-trigger',
'timestamp': datetime.now(timezone.utc).isoformat()
}
# メッセージをJSON形式にエンコード
message_data = json.dumps(error_message, ensure_ascii=False).encode('utf-8')
try:
# Pub/Subにメッセージを送信
future = publisher.publish(topic_path, message_data)
message_id = future.result()
return {
'status': 'success',
'message': 'エラー情報をPub/Subに送信しました',
'message_id': message_id,
'topic': topic_path
}, 200
except Exception as e:
return {
'status': 'error',
'message': f'Pub/Subへの送信に失敗しました: {str(e)}'
}, 500
requirements.txt
には以下の2行だけ記載しています。
functions-framework==3.*
google-cloud-pubsub==2.*
SNSパブリッシュ用Cloud Run Functions
続いてPub/Subからメッセージを受け取り、SNSに転送する関数を作成しました。
get_google_identity_token
関数でメタデータサーバーからID Tokenを取得し、
assume_role_with_web_identity
でAWSのIAMロールをアシュームしています。
import functions_framework
import base64
import json
import os
import boto3
import urllib.request
import urllib.parse
def get_google_identity_token():
"""Google Identity Token取得"""
try:
metadata_server = 'http://metadata.google.internal/computeMetadata/v1/'
token_request_url = metadata_server + 'instance/service-accounts/default/identity'
token_request_headers = {'Metadata-Flavor': 'Google'}
audience = 'sts.amazonaws.com'
params = {'audience': audience, 'format': 'full', 'include_email': 'true'}
url = f"{token_request_url}?{urllib.parse.urlencode(params)}"
req = urllib.request.Request(url, headers=token_request_headers)
with urllib.request.urlopen(req) as response:
identity_token = response.read().decode('utf-8')
return identity_token
except Exception as e:
raise Exception(f"JWT取得エラー: {e}")
@functions_framework.cloud_event
def bridge_to_sns(cloud_event):
"""
Pub/Subからメッセージを受け取り、JWTを使ってAWS SNSに転送する関数
"""
# 環境変数から設定を取得
aws_role_arn = os.environ.get('AWS_ROLE_ARN')
sns_topic_arn = os.environ.get('SNS_TOPIC_ARN')
if not aws_role_arn or not sns_topic_arn:
print("ERROR: AWS_ROLE_ARN または SNS_TOPIC_ARN が設定されていません")
return
# Pub/Subメッセージをデコード
pubsub_message = base64.b64decode(cloud_event.data["message"]["data"]).decode('utf-8')
print(f"受信したメッセージ: {pubsub_message}")
try:
# Google CloudのService AccountからID Tokenを取得
print("Google CloudのID Tokenを取得中...")
id_token = get_google_identity_token()
print("ID Token取得成功")
# AWS STSクライアントを作成
sts_client = boto3.client('sts', region_name='us-east-1')
# AssumeRoleWithWebIdentityでIAMロールを引き受け
print(f"AWS IAMロール {aws_role_arn} を引き受け中...")
assume_role_response = sts_client.assume_role_with_web_identity(
RoleArn=aws_role_arn,
RoleSessionName='cloud-function-session',
WebIdentityToken=id_token
)
print("IAMロール引き受け成功")
# 一時認証情報を取得
credentials = assume_role_response['Credentials']
# 一時認証情報を使ってSNSクライアントを作成
sns_client = boto3.client(
'sns',
region_name='us-east-1',
aws_access_key_id=credentials['AccessKeyId'],
aws_secret_access_key=credentials['SecretAccessKey'],
aws_session_token=credentials['SessionToken']
)
# SNSにメッセージを送信
print(f"SNSトピック {sns_topic_arn} にメッセージを送信中...")
# メッセージをパース
try:
message_data = json.loads(pubsub_message)
subject = f"[{message_data.get('severity', 'ERROR')}] {message_data.get('error_type', 'Error')}"
message_body = json.dumps(message_data, ensure_ascii=False, indent=2)
except json.JSONDecodeError:
subject = "Google Cloud エラー通知"
message_body = pubsub_message
response = sns_client.publish(
TopicArn=sns_topic_arn,
Subject=subject,
Message=message_body
)
message_id = response['MessageId']
print(f"SNS送信成功: MessageId={message_id}")
except Exception as e:
print(f"エラーが発生しました: {str(e)}")
import traceback
traceback.print_exc()
raise
requirements.txt
には以下の2行を記載しています。
functions-framework==3.*
boto3==1.*
検証結果
エラーを発生させる
デプロイした関数のURLにHTTPリクエストを送信します。
curl -X POST https://asia-northeast1-{projectID}.cloudfunctions.net/cm-kawanago-error-trigger
次にPub/Subトリガーで起動したcm-kawanago-bridge-sns
のログを確認します。
受信したメッセージ: {"error_type": "TestError", "message": "これはマルチクラウドエラー通知のテストです", "severity": "ERROR", "source": "cm-kawanago-error-trigger", "timestamp": "2025-10-21T02:12:53.193984+00:00"}
Google CloudのID Tokenを取得中...
ID Token取得成功
AWS IAMロール arn:aws:iam::{accountID}:role/cm-kawanago-google-jwt-role を引き受け中...
IAMロール引き受け成功
SNSトピック arn:aws:sns:us-east-1:{accountID}:cm-kawanago-notification にメッセージを送信中...
SNS送信成功: MessageId=dda0975c-1b37-530c-9ce0-f62cf5509e4f
ログから、以下の一連の処理が正常に完了していることが分かりました。
- Pub/Subメッセージの受信
- Google CloudのID Token取得
- AWS IAMロールの引き受け
- SNSへのメッセージ送信
受信したメール
SNSトピックのサブスクリプションに登録していたアドレスに対して、
以下のような形式でエラー通知のメールが届きました。
# 件名
[ERROR] TestError
# 本文
{
"error_type": "TestError",
"message": "これはマルチクラウドエラー通知のテストです",
"severity": "ERROR",
"source": "cm-kawanago-error-trigger",
"timestamp": "2025-10-21T02:12:53.193984+00:00"
}
エラー発生元から送信しているメッセージをパースして、
想定したjson形式でメッセージが構成されています。
なおPub/SubもSNSも形式はある程度自由に設定することができるので、
もっと人間が読みやすい形でテキストを構成することも可能です。
例としては以下のようなフォーマットに整理したり、
ログを確認するコンソールのURLを記載するとより利用しやすいですね。
{ジョブ名}でエラーが発生しました。
■ 重要度
ERROR
■ エラー内容
これはマルチクラウドエラー通知のテストです
■ 詳細
・エラー種別: TestError
・発生日時: 2025-10-21 02:12:53 (UTC)
・https://ログが確認できるコンソールURL
さいごに
今回はGoogle Cloud環境で発生したエラーの通知を、
Cloud Run Functionsを経由してAWSのSNSから配信する検証をやってみました。
もしAWS環境がすでに稼働している状態であれば、
エラー通知の仕組みを使い慣れたSNSに統一して利用できるのはメリットだと思います。
複数のアカウントやプロジェクトを利用するような大規模な環境ではなおさら、
エラー通知用の環境を共通利用するメリットが大きくなりそうですが、
共有用アカウントへのアクセス制御が追加になるのでそこは注意が必要です。
またSNSへのサブスクリプション登録の際は以下の手順を踏まないと、
簡単にメール配信登録を解除できてしまうので、運用面ではこちらも注意が必要です。
少しでも参考になれば幸いです。
最後まで記事を閲覧いただきありがとうございました。