こんにちは!Martimです!
今回は、Amazon Data Firehose から S3 への配信が失敗したとき、エラーログがどこに記録されるのかを、Terraform で検証環境を作って実際に再現して確かめた結果を共有していきます!
目次
- はじめに
- 検証したかったこと
- 検証環境の構成
- Firehose の設定ポイント
- 検証手順
- 検証結果
- ハマりどころ・正直な感想
- 後片付け
- 参考
- おわりに
はじめに
皆さん、こんな経験はありませんか?
「S3への配信が失敗しているはずなのに、エラーログが出ない…」
「設定が間違っているのかな? それとも、そもそも出ない仕様?」
「見たときは何も出ていなかった。じゃあ、出ない仕様ってことで…いいのかな?」
実は、以前に同じような内容の検証をしたときは、「エラーログは出ない」という結論になっていました。当時の細かい経緯までは把握できていないのですが、ログが確認できなかったために検証失敗として処理されていたようです。ところが、公式ドキュメントを確認してからやり直すと、あっさり結論が変わりました! 原因はバッファの5分を待っていなかったことではないか、と考えています(あくまで推測です)。
この記事は、個人で調べて試した内容をまとめたものです。検証したのは東京リージョン・S3送信型・1構成のみなので、その範囲の話として読んでください。
検証したかったこと
CloudWatch Logs → サブスクリプションフィルター → Firehose → S3 という構成で、S3への配信を意図的に失敗させたときに、次の点を確認しました。
- エラー記録先:Firehose の CloudWatch エラーログ設定で指定したロググループ/ログストリームに、配信エラーが記録されるか
- 記録内容:どんな内容が記録されるのか
-
出力先の違い:配信失敗(Delivery エラー)は、
error_output_prefixで指定したerrors/配下にも出るのか - 復旧後の挙動:失敗の原因を取り除いたあと、再試行で S3 に保存されるか
→ つまり、「S3に届かないとき、どこを見れば原因がわかるのか」を確かめたかったということです。
検証環境の構成
Terraform で以下のリソースを作りました。コードは GitHub に置いています。
| リソース | 役割 |
|---|---|
| S3 バケット | 配信先。SSE-S3、パブリックアクセスブロック全有効 |
| ソース用ロググループ/ストリーム | ここに手動でテストログを投入する |
| エラーログ用ロググループ/ストリーム | Firehose のエラーログ出力先 |
| Firehose 配信ロール | S3への書き込みとログ出力の権限 |
| CloudWatch Logs → Firehose ロール | サブスクリプションフィルター用 |
| Firehose(extended_s3) | GZIP、.json.gz、タイムゾーン Asia/Tokyo |
| サブスクリプションフィルター | ソースのロググループから全件を Firehose へ |
- リージョン:
ap-northeast-1 - Terraform AWS Provider:
>= 6.44.0 - ログの保持期間は1日にしています
Firehose の設定ポイント
CloudWatch Logs のデータを Firehose で受けるときの処理を、3段の processing_configuration で組みました。
- Decompression:CloudWatch Logs のペイロード(GZIP)を解凍する
-
CloudWatchLogProcessing(
DataMessageExtraction = true):外枠のメタデータを外し、message だけを取り出す - AppendDelimiterToRecord:レコードごとに改行を付ける(NDJSON 化)
そして本題のエラーログ設定がこちらです。
cloudwatch_logging_options {
enabled = true
log_group_name = aws_cloudwatch_log_group.firehose_error.name
log_stream_name = aws_cloudwatch_log_stream.firehose_error.name
}
注意点として、Firehose の配信ロールに logs:CreateLogStream と logs:PutLogEvents を付けておかないと、エラーログそのものが書けません。公式のトラブルシューティングでも、IAMロールにCloudWatchのロググループ・ログストリームへのアクセス権が必要と案内されています。
検証手順
1. 正常系の確認(失敗させる前に)
まず、正常に S3 へ届くことを確認します。ここを飛ばすと、あとで「失敗したから出ない」のか「そもそも動いていない」のか区別がつかなくなります。
-
describe-delivery-streamで、エラーログ設定が有効で、指定ストリームが実在することを確認する -
verify-ok-001のように一意な文字列を含むログを投入する - 5〜10分後に S3 にオブジェクトができ、message が1行ずつ並んでいる(外枠が消えている)ことを確認する
2. 失敗のさせ方(2パターン)
(A) IAMロールから S3 書き込み権限を外す
Firehose 配信ロールのインラインポリシーから、s3:PutObject と s3:AbortMultipartUpload を一時的に外します。復旧は terraform apply でコード定義どおりに戻せます。
(B) バケットポリシーで Deny を付ける
Firehose 配信ロールからの s3:PutObject を、バケットポリシーで明示的に Deny します。復旧は delete-bucket-policy で外すだけです。
aws s3api put-bucket-policy \
--bucket <バケット名> \
--policy '{
"Version": "2012-10-17",
"Statement": [{
"Sid": "DenyFirehosePut",
"Effect": "Deny",
"Principal": {"AWS": "arn:aws:iam::<アカウントID>:role/<Firehose配信ロール名>"},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::<バケット名>/*"
}]
}'
3. 失敗中にログを投入して待つ
失敗状態にしたあと、verify-fail-001 〜 003 のようなログを5分おきに3〜4回投入します。
ここが一番のポイントです。 バッファ設定が
buffering_interval = 300(5分。待つ側にとっては意外と長い)なので、投入直後は Firehose がまだ S3 への書き込みを試していません。投入の約5分後にフラッシュ試行 → 失敗 → エラーログ記録、という流れになります。
4. 確認するもの
- エラーログ用ストリームの中身(
get-log-events) - ロググループ内の全ストリーム(記録先が違う可能性の確認)
- S3 の
errors/配下に何か出ていないか
検証結果
結論から言うと、(A) IAMロールの権限削除、(B) バケットポリシーの Deny のどちらでも、想定どおりの結果になりました。
エラーログ
- 指定したエラーログ用のログストリームに、配信に失敗したという報告のログが記録されました
- 記録されたのはログ投入のおよそ5分後で、バッファの時間と一致しています
- 記録されたのは「失敗した」という内容の報告だけで、失敗したレコードの中身(投入した message)はログに入っていませんでした
→ つまり、このログで分かるのは「配信に失敗している」という事実までで、「どのレコードが失敗したか」を特定する用途には向かなさそう、というのが今回の印象です(1ケースだけの感想です)。
errors/ 配下
-
正常系では、
errors/(error_output_prefix)配下にオブジェクトは何も出ていないことを確認しました - 異常系で確認できた記録は、エラーログ用ストリームのものです。配信失敗の確認先としては、エラーログ側で記録を確認できた、というのが今回の検証の範囲です
- 異常系のときに
errors/側へも出力があったかどうかは、今回は確認の範囲に含めていません
復旧後
- 原因(権限の削除/バケットポリシーの Deny)を取り除いたあと、失敗中に投入した分が S3 に保存されるかを確認しました
- 結果:S3 にオブジェクトが保存されていることを確認できました
- ただし、確認できたのはオブジェクトの存在までです。中身を1件ずつ照合したわけではない(投入したログも簡単なものでした)ので、「欠損なく届いた」とまでは言い切りません
ハマりどころ・正直な感想
前回の「出ない」という結論をひっくり返した話
冒頭で触れたとおり、前回は「出ない」で終わっていました。今回は公式ドキュメントを読んで、「エラーログは、配信を試みて失敗した結果として出るもの」と理解したうえで、5分以上待ってから確認しました。そうしたら、きちんと記録されていました。
→ つまり、「出ない」と結論づける前に、まず「そもそも配信を試みるタイミングに到達したか」を疑う価値があります。前回の原因がこれだったかは推測ですが、バッファ設定を見ていれば避けられた遠回りだったかもしれません。
待っている間にできることが少ない
5分待つあいだ、状況を確認する手段が意外と少なくて困りました(本当に少ない!)。私の場合は、コマンドを実行して、その結果を AI に貼って状況を整理してもらう、という進め方でした。なお、メトリクス(IncomingRecords や DeliveryToS3.Success など)は、AI に確認させただけで、私自身が値を見て記事にできるほどの確認はしていません。メトリクスの挙動が気になる方は、ご自身の環境でぜひ確かめてみてください。
待ち時間に慌てないよう、確認用コマンドを事前にまとめておくと楽だと思います。バッファ時間は、コードに書いた設定どおり5分のまま実施しました。
エラーログは全件が保証されるわけではない
公式ドキュメントには、配信失敗率が高い状況では、エラーログがサンプリングされて送られる旨の記載があります。今回は低頻度のログ投入だったので記録されましたが、大量に失敗した場合に全件が残るとは限らない点は、運用で頼るときの注意点です。この点は私が検証したものではなく、公式情報の紹介です。
その他の注意
- 実行ユーザーに MFA 必須の Deny ポリシーが付いていると、CLI 実行が拒否されることがあります(READMEにも注意書きを残しています)
- 公式ドキュメントによると、CloudWatch Logs に送るエラーログには少額の料金がかかります
-
terraform destroyはforce_destroy = trueでバケットごと消せますが、実行ユーザーにs3:DeleteObject権限が必要です - 検証は東京リージョン、S3送信型、1構成、低頻度投入のみです。他の送信先や大量データでの挙動は確認していません
後片付け
検証が終わったら、課金や残骸が残らないよう全リソースを削除します。
terraform destroy
参考
- Monitor Amazon Data Firehose Using CloudWatch Logs(AWS公式)
- Troubleshooting Amazon S3 - Amazon Data Firehose(AWS公式)
- aws_kinesis_firehose_delivery_stream(Terraform Registry)
おわりに
今回の検証でわかったことを、あらためてまとめます。
- S3 への配信が失敗すると、Firehose のエラーログ設定で指定したログストリームに、配信失敗の報告が記録される(今回の検証の範囲では)
- ただし、記録されるのはバッファ(今回は5分)のあとの配信試行から。投入直後に見ても出ない
- 記録されるのは失敗の報告で、失敗したレコード本体は含まれていなかった
- 配信失敗の確認は、まずエラーログ側を見る。
errors/側の挙動は今回の検証範囲外 - 原因を解消すると、S3 にオブジェクトが保存されることを確認できた(中身の全件照合まではしていません)
「出ない」と結論づける前に、待ってから見る。今回はこれだけで結論が逆転しました!
Terraformのコードはリポジトリに置いてあるので、よければ自分の検証環境で試して、5分間の待ち時間を体感してみてください。Firehose の挙動を一度自分の目で見ておくと、いざというときに「どこを見ればいいか」で迷いにくくなると思います。
それでは!よいAWSライフを!