
AWS CloudTrailのログをGrafana Lokiに取り込んで可視化してみた
## はじめに
AWS CloudTrailのログをGrafanaで見たいな〜ってことありますよね?
一番簡単な方法としてはAthenaデータソースを利用する方法です。
これであればCloudTrailの証跡がS3に保存されているため、簡単にAmazon Athenaを使ってクエリすることができます。
しかし、毎度クエリ書くのが面倒だな〜とか、Athenaの料金が心配だな〜とか思うことがあったりなかったり。
そこで、偶然手元にGrafana Cloudの環境があったのでLokiに取り込めないかな?と思い、やってみました。
構成図
最終的な全体像はこんな感じになります。

今回はCloudTrailの証跡はS3に保存している前提で話を進めます。
S3にイベントが保存されると、それをトリガーにSQSにデータが送信されます。
CloudTrailのログにリアルタイム性は求めていないので、ある程度ログが溜まってからLambdaで処理するようにしています。
最後は、Lokiに保存されたデータをいい感じにフィルタリングしてGrafanaで見やすくします。
最終的なダッシュボード
最終的には次のようなシンプルなダッシュボードを作成します。
シンプルに表形式にしているだけですが、カラムごとにフィルタができたり、日本時間で時間軸を調整できるなど、クエリを書かなくても調査できる汎用性の高い必要十分なダッシュボードにしています。

lambda-promtail
最も重要なのはS3のデータをLokiに登録する方法です。
何かいい方法ないかな〜と探していたらGrafana Labsの公式ドキュメントにログエージェント (lambda-promtailというLambda関数)を作ってLokiに送信する仕組みが紹介されていました。
早速リポジトリを覗いて使い方を見てみます。 Terraformでデプロイできるようなので今回はTerraformでデプロイします。
Terraform
汎用性の高い構成となっているので、CloudTrail用に編集しています。
サンプルコードは以下に配置しています。
ラベルはいくつかLmabda側で作成してくれるようなので、追加のラベルは作成していません。
作成されるリソース
Terraformで以下のリソースが作成されます。
| リソース | 説明 |
|---|---|
| S3バケット | Lambda zipを格納 |
| IAM Role | Lambda実行用(S3読み取り、SQS操作、CloudWatch Logs書き込み権限) |
| Lambda関数 | lambda-promtail(Lokiにデータを転送するための関数) |
| SQS Queue | S3イベントのバッファ用 |
| SQS DLQ | 失敗時のリカバリ用(14日間保持) |
| CloudWatch Logs | Lambda用ロググループ(14日間保持) |
| S3 Bucket Notification | CloudTrailバケットへのイベント通知設定 |
| Lambda Event Source Mapping | SQS → Lambda のトリガー設定 |
デプロイ手順
terraform.tfvarsを作成
まずはterraform.tfvarsを作成します。
cp terraform.tfvars.example terraform.tfvars
自身の環境に合わせて証跡が保存されているS3バケット名、Grafana LokiのURL、ユーザー名、パスワードを設定してください。
# ------------------------------------------------------------------------------
# 基本設定
# ------------------------------------------------------------------------------
aws_region = "ap-northeast-1"
prefix = "lambda-promtail"
# ------------------------------------------------------------------------------
# Loki設定
# ------------------------------------------------------------------------------
# Grafana Cloudでurl, username, APIトークンを確認
write_address = "https://your-loki-url/loki/api/v1/push"
username = "your-username"
password = "your-password"
# ------------------------------------------------------------------------------
# CloudTrail S3バケット設定
# ------------------------------------------------------------------------------
cloudtrail_bucket_name = "your-cloudtrail-bucket-name"
lambda-promtail.zipのダウンロード
プリビルドのzipファイルをローカルにダウンロードします。
Terraformのデプロイ時にこのzipファイルを使ってLambda関数を作成します。
curl -L -o lambda-promtail.zip "https://grafanalabs-cf-templates.s3.amazonaws.com/lambda-promtail/lambda-promtail.zip"
デプロイ
準備が整ったらデプロイします。
terraform init
terraform plan
terraform apply
デプロイが完了すると、CloudTrailログがLokiに転送され始めます。
GrafanaのExplorerを使ってログが正しく転送されているか確認しましょう。
Grafana ExplorerでLokiのデータソースを選択して、次のクエリを実行します。
{__aws_log_type="s3_cloudtrail"}
クエリを実行すると次のようなデータが表示されるはずです。

データが表示されれば、lambda-promtailによるログ転送は正常に動作しています。
ただし、この時点では1つのカラムにCloudTrailのログ情報が詰め込まれています。
人間が読みやすい形ではないので整形する必要がありますね。
ダッシュボード作成
ダッシュボードを作成して、人間が読みやすい形に整形していきます。
Lambdaでデータを挿入する際にJSONをパースしてラベル付けしても良さそうですが、それだとコードを修正する必要があるので、他の方法でやってみます。
GrafanaにはTransformationsという機能があり、Grafana側でSQLを叩くような感じでデータを加工することができます。
この機能を使って、JSONのログをテーブル形式にしていきます。
Transformations
パネルを作成して先ほどのLokiデータソースを選択します。
次のクエリを実行して、先ほどと同じようにデータを表示します。
{__aws_log_type="s3_cloudtrail"}
| json
次にクエリの右にある「Transformations」タブを選択します。
Add another transformationをクリックして、Extract fieldsを選択します。

Extract fieldsはデータソースを選択し、そこから様々な形式でコンテンツを抽出します。
今回の場合だとlabelsフィールドをパースしたいので次のように設定します。
| 設定項目 | 値 |
|---|---|
| Source | labels |
| Format | JSON |
| Replace all fields | ON |
| Keep time | OFF |
Sourceはlabelsに設定します。
labelsフィールドにはCloudTrailのログがJSON形式で格納されているため、このフィールドを抽出対象として指定します。

Organize fields by name
Extract fieldsでJSONを表形式に変換できたので、さらにTransformationを追加してフィールドを整理します。
先ほどと同じようにAdd another transformationからOrganize fields by nameを追加します。
Organize fields by nameはパネル内の単一のクエリによって返されるフィールドの名前変更、並べ替え、非表示を行うことができます。
今回はCloudTrailログの中から必要なフィールドを選択して日本語で分かりやすく表示します。
以下、CloudTrailのコンソールを参考に最低限必要な情報を抽出しました。
| 元のフィールド名 | リネーム後 |
|---|---|
| eventTime | イベント時間 |
| eventName | イベント名 |
| eventID | イベントID |
| eventSource | イベントソース |
| eventType | イベントタイプ |
| awsRegion | リージョン |
| sourceIPAddress | ソースIP |
| errorCode | エラーコード |
| errorMessage | エラーメッセージ |
| readOnly | 読み取り専用 |
| userIdentity_sessionContext_sessionIssuer_userName | ユーザー名 |
| userIdentity_accessKeyId | アクセスキー |
実際の画面だとこんな感じです。
フィールド名の左にある目のマークを押すと表示/非表示を切り替えられます。
また、右側の入力欄で表示名を変更できます。

フィルター
これでかなりいい感じになりましたが、実際CloudTrailのログを調査するとなるともうひと工夫欲しいところです。

ということで、カラムごとにフィルタリングできるようにしましょう。
パネルの編集から Table > Column filter を有効にします。
有効にするとカラム名の右にフィルタリングマークが表示されます。

これでカラムごとにフィルタリンできるようになりました。

これで調査の捗るダッシュボードの完成です。
特定のイベントの監視やトレンドを調査したい場合は別途パネルを作成してみてください。
Drilldownでもっと簡単にログの可視化をしてみる
Grafana Drilldownを使うと先ほどまでやっていたことがもっと簡単に実現できます。
Drilldownはクエリ不要で直感的にデータを探索できる機能です。
ダッシュボードの作り込みは不要だよ、サクッと可視化したいよ!という方にはこちらがおすすめです。
左のメニュー一覧から Drilldown > Logs を選択してデータソースにCloudTrailのログを取り込んだLokiを選択します。
表示される show logsをクリックします。

Exploreの時と同じようにログが表示されます。
画面左下を見ると、何やらフィールドを選択できるようになっています。

フィールドが一覧で表示されているので、必要なフィールドにチェックを入れるだけで右側のテーブルに追加されていきます。
順番もフィールドをドラッグ&ドロップで好きに入れ替えられます。
また標準でフィルター機能もあります。

ここに辿り着くまで1分ほど...
一切クエリを書くことなくここまで可視化できました。
もはやこれでよくないか?と思ってしまいますね
Drilldownは他のテレメトリデータと連携した調査に向いている機能だと思っていましたが、ログ単体でもこれだけ機能が揃っているので、ダッシュボードの作り込みをする必要がないならこれで十分な気がします。
ただし、データソースとして選択できるのはLoki, Tempo, MimirなどGrafana Stackに限られます。
おわりに
今回はlambda-promtailを使ってCloudTrailログをLokiに転送し、GrafanaのTransformationsで見やすく表示する方法を試してみました。
GrafanaにはAthenaのデータソースがあるし、わざわざLokiにする必要なんてあるかな?と思っていましたが、Grafanaのリッチなダッシュボードにより検索性が向上したように思います。
また、当然ですがLokiはDrilldownやAssistantなどGrafanaの機能と親和性が高いです。これらの機能を有効活用することでよりログの調査が捗ることは間違いありません。
Grafana Cloudであれば無料枠で50GBまでログを保管できるので、面白そうだなと思った方はぜひ試してみてください!










