はじめに:なぜOpenTelemetry + Push方式が必要なのか
クラウドインフラの拡大に伴い、モニタリングのコストと複雑さは増大します。特に日本企業では、サードパーティライセンス費用やベンダーロックインの問題が深刻です。AWSブログで紹介されたこのアーキテクチャは、CloudWatch Metric StreamsとOpenTelemetry CollectorをLambdaで接続し、VPC内部で機密性の高いメトリクスデータを安全に収集する方法を示しています。
核心的な問い: 「PrometheusのPull方式でAPIをポーリングし続けてスロットリングが発生し、コスト爆発する前に、Push方式に移行できないか?」
この問いに対する答えが本記事です。AIが生成したコード、そのままデプロイすると起きる災厄とVercelの解決策でも強調されている通り、本番環境におけるデータパイプラインの安定性は絶対に妥協できません。

本論1:Pull vs Push — なぜPushが正解なのか
多くのチームがPrometheusのPullモデルに慣れ親しんでいます。しかし、スケールが大きくなると以下の問題が発生します。
| 項目 | Pull (Prometheus) | Push (OpenTelemetry + Metric Streams) |
|---|---|---|
| API呼び出し | 定期的なポーリング → スロットリングリスク | イベント駆動 → 必要な時だけ送信 |
| レイテンシ | 分単位 (ポーリング間隔) | サブミニット (ほぼリアルタイム) |
| コスト | API呼び出し費用 + ライセンス費用 | ライセンス無料 (Apache 2.0) |
| スケーラビリティ | スクレイプ対象増加でボトルネック | Collectorの水平スケーリング可能 |
| セキュリティ | エンドポイント露出が必要 | VPC内部で安全に受信 |
実際のアーキテクチャ (AWS公式リファレンス)
CloudWatch Metric Streams → Firehose → Lambda (Transform) → NLB → OpenTelemetry Collector (EC2)
Lambda関数が要です。FirehoseはVPC内部のエンドポイントに直接配信できないため、Lambdaが中継してNLB経由でCollectorにデータをプッシュします。
# Lambda Transform関数の例 (Python 3.9+)
import json
import base64
import requests
import os
def lambda_handler(event, context):
output = []
for record in event['records']:
# Firehoseはbase64エンコードされたデータを渡す
payload = base64.b64decode(record['data']).decode('utf-8')
metrics = json.loads(payload)
# OpenTelemetry CollectorのHTTPエンドポイントに送信
otel_endpoint = os.environ['OTEL_COLLECTOR_ENDPOINT']
try:
resp = requests.post(
f"{otel_endpoint}/v1/metrics",
json=metrics,
timeout=5
)
resp.raise_for_status()
# 成功時はFirehoseにOKを返す
output.append({
'recordId': record['recordId'],
'result': 'Ok',
'data': record['data']
})
except Exception as e:
print(f"送信失敗: {e}")
# 失敗時はFirehoseがS3バックアップに送るよう処理
output.append({
'recordId': record['recordId'],
'result': 'ProcessingFailed',
'data': record['data']
})
return {'records': output}
🚨 注意点: Lambda関数はFirehoseと同期的に動作します。つまり、Lambdaがタイムアウト(デフォルト60秒)するとストリーム全体が遅延します。
timeoutを十分に設定し、リトライロジックを必ず追加してください。

本論2:CloudFormationで一発デプロイ
AWS CLIとCloudFormationを使ってスタック全体を自動化できます。以下が主要パラメータの例です。
// parameters.json
[
{
"ParameterKey": "VpcId",
"ParameterValue": "vpc-12345678"
},
{
"ParameterKey": "SubnetIds",
"ParameterValue": "subnet-11111111,subnet-22222222"
},
{
"ParameterKey": "OtelCollectorEndpoint",
"ParameterValue": "http://internal-nlb-123456789.elb.ap-northeast-1.amazonaws.com:4318"
},
{
"ParameterKey": "StageBucketName",
"ParameterValue": "cf-stage-bucket-203918862653"
}
]
# CloudFormationスタック作成
aws cloudformation create-stack \
--stack-name cw-metrics-demo \
--template-body file://cf-packaged-file.yaml \
--parameters file://parameters.json \
--capabilities CAPABILITY_IAM
日本企業の環境での適用ポイント
- 金融機関や官公庁などセキュリティ規制が厳しい組織では、VPC内部にCollectorを置きNLB経由でのみアクセスを許可する本アーキテクチャが非常に適しています。
- ただし、LambdaがVPC内部に存在しないとNLBにアクセスできない点に注意してください。LambdaにVPC設定を追加するとコールドスタートが長くなるため、Reserved Concurrencyを設定しておくことを推奨します。
- S3バケットはバックアップ用途のみなので、実際のコストはほぼ発生しません。ただし、データ損失防止のために
ProcessingFailedパスで失敗レコードをS3に保存するよう構成してください。

まとめ:本番適用前のチェックリスト
このアーキテクチャはPushベースのモニタリングに移行したい全てのチームにとって優れた出発点です。ただし、いくつかの考慮点があります。
本技術の限界または注意点
- Lambda同期呼び出しコスト: Firehoseがレコード1MBあたりLambdaを呼び出すため、メトリクス量が多いとLambdaコストが増加する可能性があります。事前に予想コストを計算してください。
- Collector障害時のデータ損失: LambdaからCollectorへの送信中に障害が発生すると、Firehoseはリトライしますが一部データが失われる可能性があります。S3バックアップ + Dead Letter Queueの構成が必須です。
- OTLPバージョンの互換性: CloudWatch Metric StreamsはOpenTelemetry 0.7と1.0をサポートしています。Collectorのバージョンと合わせる必要があります。
次のステップとしての学習方向
- border-radiusの限界を破るCSS corner-shape、実務適用ガイド — フロントエンドのパフォーマンス最適化に興味があれば併せてご覧ください。
- AWS Distro for OpenTelemetry (ADOT) の公式ドキュメントを読み、X-RayやAMPとの連携をテストしてみてください。
- 本番適用前にカナリアデプロイで一部のメトリクスのみを先行して切り替えることを推奨します。
合わせて読みたい記事