【AWSコスト削減】RDSの監査ログをCloudWatch LogsからS3に移行して、コストを100分の1に削減できた話

# はじめに

RDS の監査ログの保存先を CloudWatch Logs から S3 に移行することで、**大幅なコスト削減** に成功しました！！！

![スクリーンショット 2024-12-06 16.46.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325392/d57217d1-b417-1ea4-432c-762fd9c754c6.png)
↑ Cost Exproler の CloudWatch `APN1-DataProcessing-Bytes (GB)` のグラフ
（12/3 にCloudWatchへのログ転送を停止、12/4 から安くなっている）

想像していたよりもやることが多く時間がかかったので、移行が完了するまでに何をしたのかをこの記事にまとめまています。

また、この施策を通じて **多くのことを学ぶことができた** ので、それについても記載します。

同様にRDSの監査ログをCloudWatch LogsからS3に移行してコスト削減をしようとしている方の参考になれば幸いです。

# 概要

移行前後の構成について、簡単に紹介します。

## 移行前: RDS の監査ログを CloudWatch Logs に保存

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325392/5a3e6899-97d7-c7fa-df46-9a2d5f0276ce.png)

RDS (この記事では、特に Aurora MySQL) の監査ログは、時間が経つと消えてしまいます。

[Aurora MySQL データベースログの概要 - Amazon Aurora](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/USER_LogAccess.MySQL.LogFileSize.html)

> Aurora MySQL は、24 時間後、またはストレージの 15% が消費されると、監査、一般、および低速クエリログを削除します。

そのため、別の場所に保存しておく必要がありますが、RDS には CloudWatch Logs にログを転送してくれる機能があります。

[Amazon CloudWatch Logs へのデータベースログの発行 - Amazon Relational Database Service](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_LogAccess.Procedural.UploadtoCloudWatch.html)

↓ `Audit log` にチェックを入れると有効になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325392/413aa035-d155-75ca-ff09-d2f886b88b8a.png)

CloudWatch Logs に保存されたログは、CloudWatch Logs Insights で分析ができます。

ログが多いと転送料金やストレージ料金がかなり高額になります。

[Amazon CloudWatch Pricing – Amazon Web Services (AWS)](https://aws.amazon.com/jp/cloudwatch/pricing/)

## 移行後: RDS の監査ログを S3 に保存

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325392/c877c92a-62df-ed2b-7ab9-a16271569875.png)

監査ログは RDS の API で取り出せるようになっています。

RDS からログを取り出して S3 に保存する Lambda を作成し、EventBridge Scheduler で一定時間ごとに実行すれば、移行前と同じように、すべてのログを保存することが可能です。

また、S3 に保存したログは Athena で分析できます。

# 移行完了までの流れ

調査開始から移行完了(CloudWatch Logs に転送するのを止める)までに、約4ヶ月かかりました。その4ヶ月で何をしたか振り返りつつ、それぞれの工程で何をする必要があったのか以下にまとめました。

## 調査・仕様の決定

なぜこの施策をやることになったかを一言で説明すると、「コストの削減のために今できそうな施策のうち、最も効果が大きそうな施策がこれだったから」です。

はじめに、監査ログの現状を調査しました。調べたことは主に以下です。

- 監査ログ関連の料金は毎月いくらかかっている？
- 監査ログはどんな形式で保存されていて、どんな情報が含まれる？
- 今 CloudWatch Logs にどれだけログが溜まっていて、1日で何GBのログが増える？
- CloudWatch Logs に保存されている監査ログは、現在どのように分析できる？

現状を把握することで、移行後の構成で満たすべき要件がわかったので、それを元に仕様を決めます。

`RDS 監査ログ S3` で検索すると、監査ログをLambdaでS3に保存することでコストの削減に成功した、という事例がいくつか出てくるのでそららを元に以下を考えていきます。[^1]

[^1]:LambdaでS3に保存する以外の方法も調査しましたが、最終的にこの方法が一番コストが抑えられ、実現できそうという結論になりました。

- 監査ログの保管期間
- 監査ログを保存する S3 のストレージクラス
- Lambda を実行する間隔

それぞれ、以下の観点で最適な設定を検討しました。

- 監査ログとしての要件を満たしている
- できるだけコストが削減できる
- できるだけ少ない工数で実現できる

最終的に「このように実装すれば、現在と同等の要件を満たしつつ、いくら削減できます」といった内容をまとめて、承認を得ることができました。

## 検証

仕様が決まったので、次にそれをどのように実装していくか考えました。その際に、いくつか懸念点が出てきました。

- Lambda でログをダウンロードするけど、メモリは足りる？
- API で RDS や S3 に何度もリクエストするけど、レートリミットに達しない？
- Lambda の実行時間は最大15分だけど、それに収まる？
- 本当に漏れなく全部のログを送れる？

これらの懸念点を解消するために、本番の実装の前にプロトタイプを作成して検証を行うことにしました。

検証用プロトタイプを作ってみることで、他にも考慮すべき点が見つかりました。特に、Lambdaについてあまり詳しくなかったので、どうすればいいか考えることが多かったです。

- Lambda の関数を記述する言語をどうするか
- CI/CD の設定はどうするか
- Lambda の成功や失敗をどうやって検知するか
- Lambda のIAMロールやIAMポリシーの設定

他のメンバーと相談して決めることが可能なこともありますが、やってみないとわからないこともあったので、とにかく試行錯誤を繰り返し、1つずつ確実に懸念を潰して、仕様の詳細を詰めていきました。

## 実装

検証でかなり仕様を細かく詰めることができたので、あとは実装していくだけですが、初めてやることが多く結構時間がかかりました。

- Lambda のコードを管理するリポジトリの作成
- Lambda を動作確認しつつ開発できる環境の整備
- GitHub Actions で CI/CD ができるように設定
- AWS の各種リソース (Lambda, S3 など) の作成

それぞれのやり方は検索したら別の記事がヒットすると思うので、詳細は省きます。

意識したこととして、 Lambda のコードは今後自分以外のメンバーも触る可能性があるので、誰が読んでも理解できるような設計や、ドキュメントの用意などを意識し、時間をかけました。

完成した Lambda をステージング環境で試した後、本番環境で定期実行をはじめました。

## 運用の準備

あとは CloudWatch Logs にログが転送されるのを止めるだけですが、止める前にやることがあります。

それは、**監査ログが欠損せずにすべて保存できている** ということを示すことです。主にやったことは以下です。

- 欠損なくすべてのログが転送できていることの確認
- Lambda の成功・失敗の検知と、失敗した時にどう対応するか決めておく

ここは少し詳細を書いておきます。

### 欠損なくすべてのログが転送できていることの確認

CloudWatch Logs に保存されているログと、今回作成したLambdaがS3に保存したログが一致していることを確認しました。

すべてのログが一致していることを確認することはできないので、実際にはランダムで数日選び、そのログが一致していることを確認しました。

CloudWatch Logs のログは CloudWatch Logs Insights で取り出すことができますが、1度に1万件までしか取得できません。監査ログは1分間で1数十万件あったので、CloudWatch Logs Insights だと比較に途方も無い時間がかかってしまいます。

そこで、CloudWatch Logs のログを S3 にエクスポートする機能があるのでそれを活用しました。
[Amazon S3 へのログデータのエクスポート - Amazon CloudWatch ログ](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/S3Export.html)

Lambda で S3 に保存したログと、CloudWatch Logs から S3 にエクスポートしたログは、どちらも Athena からクエリで分析できます。これを活用して、SQLで `EXCEPT` で差集合を求めることで、Athena 上でログが一致していることを確認できました。[^2]

[^2]: `[Lambdaのログ] EXCEPT [CloudWatch Logsのログ]` と `[CloudWatch Logsのログ] EXCEPT [Lambdaのログ]` の両方を実行する必要があります。

### Lambda の成功・失敗の検知と、失敗した時にどう対応するか決めておく

冒頭にも書いたように、RDSの監査ログは一定時間が経つと消えてしまうので、Lambda に止まっていることに気づけないとログの欠損が発生してしまいます。なので、失敗を検知して対策する必要があります。

Lambda の機能で、失敗を検知して Amazon SQS や Amazon SNS を経由して通知を送ることができます。

[Lambda 非同期呼び出しのレコードのキャプチャ - AWS Lambda](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/invocation-async-retain-records.html)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325392/4f1dd61d-a237-eaee-6d2c-fafe63b1695d.png)

今回は特に Lambda が失敗したタイミングでできるだけ早く気づきたいので、Amazon SNS で失敗したときと成功したときそれぞれ通知を DataDog に送信して、失敗の通知が来た時か成功のイベントが途切れたときに気づけるように設定をしました。

あとは、失敗した時にどう対応するかですが、失敗の原因に応じてどう対処すればいいかをドキュメントにまとめ、それをエンジニアメンバーに展開しました。

↓エンジニアメンバーに展開した時の議事録
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325392/ff28c8af-5fc5-c980-723d-055d00de49a3.png)

## 移行完了 :tada: 

問題なくログが転送できていることを確認し、もし失敗した場合の対処方法も確立した後、 CloudWatch Logsにログ転送する機能をオフにしました。

この記事の冒頭にも貼りましたが、以下のグラフの通り、12/3に停止して、12/4, 5 から小さい値になっていることが確認できました。（11/30, 12/1 が若干低いのは土日でアクセスが少ないため）

![スクリーンショット 2024-12-06 16.46.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325392/d57217d1-b417-1ea4-432c-762fd9c754c6.png)

# 学び

最後に学んだことを書いておこうと思います。

## AWSの知識

僕は現在エンジニア4年目で、これまではWebアプリケーション開発がほとんどでした。そのためAWSをはじめとしたインフラの経験が多いわけではなかったので、今回のこのコスト削減を通じて AWS の知識が増えました。

RDS, CloudWatch, Lambda, S3, Athena, IAM、そして料金を確認するために何度も使用した Cost Explorer の知識を身につけることができました。

他にも、実現したいことを AWS のドキュメントを探して解決する力や、AWS Console、AWS CLI、AWS SDK、Terraform で AWS のリソース管理など、場面に応じて使い分ける力がついたと思います。

## とにかくいろいろ試してみる

使ったことがない AWS のサービスが多く、調査に時間がかかりましたが、今振り返ってみると、完璧に調べるよりも試しに使ってみるほうが進みが早い気がしました。

実際にサービスを使ってみることで、思わぬ課題にぶつかったり、逆に懸念していたことが杞憂だったり、気づいていなかった解決方法に気づくことがあります。

特に、移行前のログが一致しているか比較する方法について、当初はローカルにログをダウンロードして比較するスクリプトを用意していましたが、CloudWatch Logs のログを試しに S3 にエクスポートしてみたら、Athena で扱い安いことに気づき、想定よりも楽にログの比較ができました。

とにかく色々試してみましょう！

## 作って終わりではない

これは学びというよりもこれまでの経験から今回意識したことですが、作って終わりではなく、作った後に自分以外のメンバーにとってわかりやすくなるようにしました。

今回実装したログを転送するシステムは、RDSを稼働し続ける限りずっと使い続けることになります。

なので、Lambdaのコードを丁寧に書いたり、ドキュメントをたくさん残したり、Lambda が失敗したときにどうすればいいか対応方法を残したりしました。

そうすることで、もし自分がいない時や退職したあとに何か問題が発生しても、対処できるようにしています。

# おわりに

ここまで読んでいただきありがとうございます。

この記事では移行のイメージしやすいように何をすればいいのかを中心に記載し、Lambda のコードの詳細など、技術的なこと・具体的なことは省きました。気になった方はこっそり質問してください。

RDS の監査ログを CloudWatch Logs に転送する機能は便利ですが、ログが多いと高額になりやすいので、コスト削減を検討されている方はぜひ試してみてもらえるとよいかと思います。

# 参考文献

- [【コスト削減】RDSの監査ログをCloudWatch logを経由せずに S3に登録 / AWS #Aurora - Qiita](https://qiita.com/taro-yamada/items/f2c19339ec37de4d6959)
- [コスト削減成功！Amazon Auroraの監査ログをS3に保存する仕組みを構築した話 - Classi開発者ブログ](https://tech.classi.jp/entry/2024/01/16/120000)