AWSのサービスの中には、CloudWatch Logs(以下CWL)にしかログを吐き出せないものが結構ある。
これはこれで使いではあるが、S3にログを集約したい、といった場合は、Kinesis Data Firehose(以下KFH)を用いたサブスクリプションフィルターを設定する必要があり、結構面倒くさい(しかも、Kinesis系のサブスクリプションフィルターだけマネジメントコンソールから設定出来なかったりする)。
面倒くさいステップが少しでも腹落ちするよう、トンネル開通に例えて、一連の方法をまとめておく。
(手順自体は公式に準ずる)。
イメージ
人 --> 入り口 --> ( === トンネル === ) --> 出口
こういうイメージで、
ログレコード --> CWLサブスクリプションフィルター --> ( === KFH === ) --> S3
これを作ります。
ステップバイステップ
1. 入り口と出口を作る
- 何でもいいのでCWLロググループを一つ選ぶ。
これがトンネルの入り口となる。
量を試したければVPC Flow Logsが使いやすい。
ここでは、ABC順で最初に目に付いたGlueのジョブアウトプット(/aws-glue/jobs/output
)を使うことにする。
- 最終目的地用のS3バケットを作成する。
これがトンネルの出口となる。
% aws s3api create-bucket --bucket kfhtest2021 --create-bucket-configuration LocationConstraint=ap-northeast-1
{
"Location": "http://kfhtest2021.s3.amazonaws.com/"
}
2. 施工許可証を作る
- KFHストリーム用のIAMロールを作成する。
これがトンネルを作るためのいわば施工許可証になる。
マネジメントコンソールだとKFHストリーム作成時に自動作成してくれるが、IAMロール・信頼関係・IAMポリシーの相互関係を改めて整理するのに意外にちょうどよかったので、あえて公式通りCLI方式で書いておく。
まずはKFHがこのロールをAssumeRoleするための信頼関係をJSONファイルで用意する。
{
"Statement": {
"Effect": "Allow",
"Principal": { "Service": "firehose.amazonaws.com" },
"Action": "sts:AssumeRole",
"Condition": { "StringEquals": { "sts:ExternalId":"AWSアカウントID" } }
}
}
次にKFHに何の権限が許可されるかを記述したIAMポリシーを同じくJSONファイルで用意する(バケット名はここではkfhtest2021
)。余談だが、このIAMポリシーは後続のIAMロール専用の、いわゆるインラインポリシーという位置づけになる。
{
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:AbortMultipartUpload",
"s3:GetBucketLocation",
"s3:GetObject",
"s3:ListBucket",
"s3:ListBucketMultipartUploads",
"s3:PutObject" ],
"Resource": [
"arn:aws:s3:::kfhtest2021",
"arn:aws:s3:::kfhtest2021/*" ]
}
]
}
IAMロールを作る。信頼関係はこの時紐付ける。
% aws iam create-role \
--role-name FirehosetoS3Role \
--assume-role-policy-document file://~/trust.json
{
"Role": {
"Path": "/",
"RoleName": "FirehosetoS3Role",
"RoleId": "AROAQH2ZXUWM2YY5UEUHI",
"Arn": "arn:aws:iam::AWSアカウントID:role/FirehosetoS3Role",
"CreateDate": "2021-02-03T12:25:39+00:00",
"AssumeRolePolicyDocument": {
"Statement": {
"Effect": "Allow",
"Principal": {
"Service": "firehose.amazonaws.com"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "AWSアカウントID"
}
}
}
}
}
}
作ったIAMロールにIAMポリシーを紐付けて完成。
% aws iam put-role-policy --role-name FirehosetoS3Role \
--policy-name Permissions-Policy-For-Firehose \
--policy-document file://~/policy-kfh.json
これで、「何ができるか(IAMポリシー)」を表面に、「誰がこの権限を使うことができるか(信頼関係)」を裏面に書いた、"施工許可証"であるところのIAMロールができた。
この後作るKFHストリームにこの施工許可証を渡してやれば、KFHストリームは表面に書かれた権限でもって、出口であるS3バケットに向かって堂々と掘り抜けることができることになる(許可証の利用許可は裏面に書いてある)。
3. トンネルを掘る
- CWLとS3の間を繋ぐ、KFHストリームを作成する。
これが実際にログストリームが通過するトンネルになる。
一点注意点は、KFHの圧縮オプションを無効にすること。CWLはそれ自身がGZIP圧縮するので、二重に圧縮がかかってしまい後で使う時に無駄に悩む羽目になる。まあ、デフォルトは「圧縮なし」なので、以下の通りやれば特に問題はない。
% aws firehose create-delivery-stream \
--delivery-stream-name 'my-delivery-stream' \
--s3-destination-configuration \
'{"RoleARN": "arn:aws:iam::AWSアカウントID:role/FirehosetoS3Role", "BucketARN": "arn:aws:s3:::kfhtest2021"}'
{
"DeliveryStreamARN": "arn:aws:firehose:ap-northeast-1:AWSアカウントID:deliverystream/my-delivery-stream"
}
ここまででトンネルは完成。出口にも繋がっている。
最後に、"トンネル利用許可証"を用意した上で、入口を開通させる作業が残っている。
4. トンネル利用許可証を作る
- CWLがKFHを使うためのIAMロールを作成する。
先程作った建築許可証はKFHストリームがS3という"出口"に掘り抜けるためのものだが、今回作るのは入り口であるCWLロググループがKFHストリームという"トンネル"を使っていいよという、いわばトンネル利用許可証である。
やることはほぼ同じ。
まずは信頼関係。
{
"Statement": {
"Effect": "Allow",
"Principal": { "Service": "logs.ap-northeast-1.amazonaws.com" },
"Action": "sts:AssumeRole"
}
}
次いでIAMポリシー。
{
"Statement":[
{
"Effect":"Allow",
"Action":["firehose:*"],
"Resource":["arn:aws:firehose:ap-northeast-1:AWSアカウントID:*"]
}
]
}
IAMロール作成。
% aws iam create-role \
--role-name CWLtoKinesisFirehoseRole \
--assume-role-policy-document file://~/trust-cwl.json
{
"Role": {
"Path": "/",
"RoleName": "CWLtoKinesisFirehoseRole",
"RoleId": "AROAQH2ZXUWM6SCQSYNSV",
"Arn": "arn:aws:iam::AWSアカウントID:role/CWLtoKinesisFirehoseRole",
"CreateDate": "2021-02-03T13:29:15+00:00",
"AssumeRolePolicyDocument": {
"Statement": {
"Effect": "Allow",
"Principal": {
"Service": "logs.ap-northeast-1.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
}
}
}
最後にIAMロールとIAMポリシーを紐付けて完成。
% aws iam put-role-policy --role-name CWLtoKinesisFirehoseRole \
--policy-name Permissions-Policy-For-CWL \
--policy-document file://~/policy-cwl.json
利用許可証ができ、CWLがトンネルであるところのKFHストリームに接続する準備ができた。
5. 入り口とトンネルを繋ぐ
- 準備したKFHストリームを指定して、CWLロググループのサブスクリプションフィルターを作成する。
これで、入り口 -> トンネル -> 出口が繋がり、ログがS3バケットに出力され始める。
まず、先程作ったトンネル(KFHストリーム)の識別名であるところのARNを取得する。
% aws firehose describe-delivery-stream --delivery-stream-name "my-delivery-stream" --query 'DeliveryStreamDescription.DeliveryStreamARN'
"arn:aws:firehose:ap-northeast-1:AWSアカウントID:deliverystream/my-delivery-stream"
トンネル利用許可証(IAMロール)の識別名であるARNも取得。
% aws iam get-role --role-name CWLtoKinesisFirehoseRole --query 'Role.Arn'
"arn:aws:iam::AWSアカウントID:role/CWLtoKinesisFirehoseRole"
最後に、これらの情報をかき集めてサブスクリプションフィルターを作る。
フィルターパターンはログによりまちまちだが、ここでは全件出力するので""で指定。
% aws logs put-subscription-filter \
--log-group-name "/aws-glue/jobs/output" \
--filter-name "Destination" \
--filter-pattern "" \
--destination-arn "arn:aws:firehose:ap-northeast-1:AWSアカウントID:deliverystream/my-delivery-stream" \
--role-arn "arn:aws:iam::AWSアカウントID:role/CWLtoKinesisFirehoseRole"
ようやく、目指すサブスクリプションフィルターが作成された。
6. 動確
- あとはログを出力させ、S3に吐き出されることを確認するだけ。
今回はGlueジョブのOutputログなので、Output出力の設定をしたジョブを適当に走らせてみる。
% aws glue start-job-run --job-name job_orcl2s3
待つことしばし。
KFHストリーム経由でS3バケットにログ出力されたことが分かる。
せっかくなので、S3 Selectでクエリーしてみる。
CWLのログはJSONフォーマット、GZIP圧縮なので、以下の通り指定する。
大項目 | 小項目 | 値 |
---|---|---|
入力設定 | 形式 | JSON |
圧縮 | GZIP | |
出力設定 | 形式 | JSON |
...複数行が結合されているように見えるが、いったんそれは措くとして、とりあえず"トンネル"は無事開通したようだ。
余談
2020年のアップデートで、これまで1つだった、ロググループあたりのサブスクリプションフィルター数が「2」に増えた。
KFH経由でS3に送りつつ、Elasticsearch Serviceにも送る、みたいなことが、fluentdのようなログルーターを介さずともCWL単体でできるようになったことになる。