データ分析観点から見た AWS ECS コンテナロギング

AWS ECS コンテナロギングをデータ分析観点からどの様な構成だと分析に都合が良いかの観点を記載します。

基本ログは追加のみで更新や削除はされない性質があるので、その点を考慮して登録されることを意識したアーキテクチャが望ましいです。

ECS コンテナロギングの構成

ECS → CloudWatch Logs

デフォルトではこのような構成がよく見受けられます。

graph LR

ECS-->cw_logs[CloudWatch Logs]
  • Pros:
    • リアルタイムでデータ分析が可能
    • 簡易的に設定できる
  • Cons:
    • CloudWatch Logs へのログ出力コストが掛かる
    • 他データとの統合的な分析は一手間かかる

他データとの統合的な分析は一手間かかる ?

CloudWatch Logs Insight もあり、ログのみを分析対象とする場合であれば特に問題ないですが、
他データと照合したい場合は統合的な管理が必要です。

例: 特定の時刻で ALB のログと付き合わせて、どの URI にアクセスされたかを集計したい場合

Amazon Athena CloudWatch コネクタ
Athena から CloudWatch に接続可能ですが、Lambda を介するので Lambda の一度に返せるレスポンスの制限があり、レスポンスで返せないデータは一度 S3 に退避 (spill) したりすることで完全なレスポンスを返すまでに時間を要し、パフォーマンスが悪くなります。

CloudWatch のサブスクリプションフィルターで Lambda でデータ変換し S3 保管

graph LR

cw_logs[CloudWatch Logs]--Lambda-->S3

こちらも処理量がお多い場合に Lambda のリソース制限でエラーハンドリングをする必要が出る可能性もあり、あまり利用を推奨しづらい構成です。

ECS → Kinesis Firehose → S3

graph LR

ECS-->kinesisfirehose[Kinesis Firehose]--parquet-->S3

Kinesis Firehose 経由で S3 にデータを蓄積し、Athena から参照する構成です。

  • Pros:
    • S3転送時に失敗した場合にバックアップが取れる
    • 大量リクエストがある場合、 CloudWatch Logs への put object する料金より安くすることができる
    • Athena で統合して分析可能
  • Cons:
    • Kinesis Firehose でデータをバッファリング (一定条件で溜め込む) するとリアルタイム性が損なわれる
      • ある程度コストを抑えるには、最大 15 分のバッファリングが可能なので、まとめて S3 にデータを送信ができる
    • 設定に一手間

障害等で緊急のログ調査が必要な時にリアルタイム性が損なわれた状態では運用に懸念があります。
新規の開発時に検証でログが直ちに閲覧できないストレスは想像に難くありません。
データ分析の観点で言えば、Athena で統合でき、親和性が高いですが、実運用の観点では問題がある様に見えます。

ECS → Kinesis Firehose / CloudWatch Logs ハイブリッド

graph LR

ECS-->kinesisfirehose[Kinesis Firehose]--parquet-->S3
ECS-->cw_logs[CloudWatch Logs]

ハイブリッドにすることで実運用を鑑みてリアルタイム性を担保しつつもデータ分析を Athena で統合的にデータ分析できる状態ができます。

  • Pros:
    • データ分析用のログとリアルタイムのログを担保できる
  • Cons:
    • CloudWatch Logs へのログ出力コストが掛かる
    • 設定に一手間

リクエストの規模にもよりますが、データ分析をする、と言う背景を持った上でのアーキテクチャを鑑みるとこの辺りが落とし所かなと思います。

時折見る辛い構成

graph LR

ECS--ログ-->RDS

ログデータを DB に溜め込んでいるのを時折見ます。
そのデータを元にリアルタイムにアクセスランキングを出したり、ということをやろうとしたのかな?と背景を想像したりしますが、
悪手である場合が多い印象です。

データ肥大化に対策した上で溜め込んでいるのであれば、まだ良いですが、
そうでなければ、いずれ肥大化してパフォーマンスを落としていくのが目に見えている為です。

RDS から何億レコードを一括で取ろうものなら DB リソースを消費しアプリケーション DB であれば、ユーザ影響を及ぼすことは想像に難くないです。

データ分析の観点からやって欲しくない構成です。

ログは一旦ログストレージ (例: S3)に流し、アプリケーションで利用するデータは別途担保する、というのが望ましいです。

総評

AWS ECS コンテナのロギングでの運用してみてほどよかったハイブリッド形式を紹介させていただきました。

ECS ではサイドカーで fluentbit を起動し、ログをルーティングしました。

参考: https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/firelens-using-fluentbit.html

以上
参考になれば幸いです。

RDS のテーブルデータを分析用テーブルにレプリケートする方法一覧


概要

RDS のテーブルデータを分析用テーブルにレプリケートする方法が多数あったのでその一覧をまとめます。

RDS をユーザ影響を極力低くすべく、分析用テーブルへレプリケートして、分析する方法が多々あったので私見ですが Pros/Cons をまとめます。

前提

  • アプリケーション DB を配置する AWS アカウントとデータ基盤を構築する AWS アカウントは分けます
  • Glue テーブルは iceberg 形式を採用します
続きを読む

Glue Job Bookmark 機能でなく sampleQuery を使って DB データをエクスポートしてみた


概要

Glue Job で DB データを取得していた際に Glue Job Bookmark を利用していた際に問題があったので、その際の対応を備忘録として残しておきます。

Glue Job Bookmark 機能とは?

Glue Job で DB やログ情報を取り込みしている場合、どこまで取り込んだかを記録する Bookmark 機能 があります。

DB データを毎回全てダンプするよりも差分のみ抽出(増分エクスポート: Incremental Export)でき、データの取り込み量も抑えられ、Glue Job の実行時間が短縮されます。

Glue Job は実行時間に対して従量課金されるのでコストも抑制できるメリットがあります。

続きを読む

ALB アクセスログに新たな項目が追加された

2024.05.22 より、ALB のアクセスログを Athena でクエリ実行してみると空の行が返るようになりました。
原因を調査してみるとどうやら ALB アクセスログに以下の項目が追加され、フォーマットが変更された為のようです。

  • traceability_id string
  • unknown_fields string

テーブルを再作成することで事なきを得ました。

※ ALB はパーティションして利用しており、公式とはやや異なるテーブル定義にしています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
CREATE EXTERNAL TABLE `<table name>`(
`type` string COMMENT '',
`time` string COMMENT '',
`elb` string COMMENT '',
`client_ip` string COMMENT '',
`client_port` int COMMENT '',
`target_ip` string COMMENT '',
`target_port` int COMMENT '',
`request_processing_time` double COMMENT '',
`target_processing_time` double COMMENT '',
`response_processing_time` double COMMENT '',
`elb_status_code` string COMMENT '',
`target_status_code` string COMMENT '',
`received_bytes` bigint COMMENT '',
`sent_bytes` bigint COMMENT '',
`request_verb` string COMMENT '',
`request_url` string COMMENT '',
`request_proto` string COMMENT '',
`user_agent` string COMMENT '',
`ssl_cipher` string COMMENT '',
`ssl_protocol` string COMMENT '',
`target_group_arn` string COMMENT '',
`trace_id` string COMMENT '',
`domain_name` string COMMENT '',
`chosen_cert_arn` string COMMENT '',
`matched_rule_priority` string COMMENT '',
`request_creation_time` string COMMENT '',
`actions_executed` string COMMENT '',
`redirect_url` string COMMENT '',
`lambda_error_reason` string COMMENT '',
`target_port_list` string COMMENT '',
`target_status_code_list` string COMMENT '',
`classification` string COMMENT '',
- `classification_reason` string COMMENT ''
+ `classification_reason` string COMMENT '',
+ `traceability_id` string COMMENT '',
+ `unknown_fields` string COMMENT ''
)
PARTITIONED BY (
`year` int COMMENT '',
`month` int COMMENT '',
`day` int COMMENT '')
ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
WITH SERDEPROPERTIES (
- 'input.regex' =
- '([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*):([0-9]*) ([^ ]*)[:-]([0-9]*) ([-.0-9]*) ([-.0-9]*) ([-.0-9]*) (|[-0-9]*) (-|[-0-9]*) ([-0-9]*) ([-0-9]*) \"([^ ]*) (.*) (- |[^ ]*)\" \"([^\"]*)\" ([A-Z0-9-_]+) ([A-Za-z0-9.-]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" ([-.0-9]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\" \"([^ ]*)\" \"([^\s]+?)\" \"([^\s]+)\" \"([^ ]*)\" \"([^ ]*)\"')
+ 'input.regex' =
+ '([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*):([0-9]*) ([^ ]*)[:-]([0-9]*) ([-.0-9]*) ([-.0-9]*) ([-.0-9]*) (|[-0-9]*) (-|[-0-9]*) ([-0-9]*) ([-0-9]*) \"([^ ]*) (.*) (- |[^ ]*)\" \"([^\"]*)\" ([A-Z0-9-_]+) ([A-Za-z0-9.-]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" ([-.0-9]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\" \"([^ ]*)\" \"([^\s]+?)\" \"([^\s]+)\" \"([^ ]*)\" \"([^ ]*)\" ?([^ ]*)?')
STORED AS INPUTFORMAT
'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
's3://<s3 bucket>/<lb name>/AWSLogs/<aws account id>/elasticloadbalancing/ap-northeast-1'
TBLPROPERTIES (
'classification'='csv',
'compressionType'='gzip',
'projection.day.digits'='2',
'projection.day.range'='01,31',
'projection.day.type'='integer',
'projection.enabled'='true',
'projection.month.digits'='2',
'projection.month.range'='01,12',
'projection.month.type'='integer',
'projection.year.digits'='4',
'projection.year.range'='2020,2100',
'projection.year.type'='integer',
'storage.location.template'='s3://<s3 bucket>/<lb name>/AWSLogs/<aws account id>/elasticloadbalancing/ap-northeast-1/${year}/${month}/${day}',
'typeOfData'='file')

以上
参考になれば幸いです。

AWS リソースの年間予約購入


AWS リソースの年間予約購入について備忘録です。

年間予約購入まとめ

  • Reserved Instance

    • 対象:
      • RDS
      • ElastiCache
      • RedShift
      • OpenSearch
    • 以下指定し購入
      • インスタンスタイプ
      • 個数
      • 前払い
      • 年数(弊社は 1 年指定)
    • 期限切れ通知
    • Pros:
      • どのリソースに対して購入するかが決定しやすい
  • Savings Plans

    • 対象: コンピュートリソース(EC2 専用の Savings Plans もある)
      • EC2
      • ECS
      • Lambda
    • 以下指定し購入
      • コンピュートリソースの利用量に対するコミット値
      • 年数
      • 前払い
    • 期限切れ通知
    • コミット値
      • 過去・今後の推定利用量から算出する or AWS 推奨値を設定する
    • Pros:
      • EC2 から ECS のリプレイス後も適用される
    • Cons:
      • どのリソースに対して購入したかが分かりづらい
  • CloudFront Security Bundle

    • 対象:
      • CloudFront
      • WAF
    • 以下指定し購入
      • CloudFront の利用量に対するコミット値
      • 年数
      • 前払い
    • 期限切れ通知
      • 現状ない (2023-12-07 時点)
    • コミット値
      • 過去・今後の推定利用量から算出する or AWS 推奨値を設定する
続きを読む

aws-cli で AWS 起動中のリソース一覧取得する

備忘録です。

複数 AWS アカウントで起動中のリソース一覧作りたい時によく利用しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash

profiles=(
<profile names in ~/.aws/credentials>
)

for profile in ${profiles[@]}; do
awsume $profile --session-name "kenzo.tanaka" --output-profile tmp
account_id=$(aws sts get-caller-identity --profile tmp --query 'Account' --output text)

aws ec2 --profile tmp describe-instances --filters "Name=instance-state-name,Values=running" \
| jq -r ".Reservations[].Instances[] | \"$profile,$account_id,ec2,\"+ .InstanceType +\",1,\"+ (.Tags[]|select(.Key == \"Name\").Value)"

# aws rds describe-db-clusters だと cluster を利用していない場合に instance 情報が取得できない
aws rds --profile tmp describe-db-instances \
| jq -r ".DBInstances[] | select(.DBInstanceStatus==\"available\") | \"$profile,$account_id,\"+ .Engine +\",\"+ .DBInstanceClass +\",1,\"+ .DBInstanceIdentifier"

aws elasticache --profile tmp describe-cache-clusters \
| jq -r ".CacheClusters[] | \"$profile,$account_id,\"+ .Engine +\",\"+ .CacheNodeType +\",\"+ (.NumCacheNodes|tostring) +\",\"+ .CacheClusterId"

aws redshift --profile tmp describe-clusters \
| jq -r ".Clusters[] | select(.ClusterStatus==\"available\") | \"$profile,$account_id,redshift,\"+ .NodeType +\",\"+ (.NumberOfNodes|tostring) +\",\"+ .ClusterIdentifier"
done

AWS CLI で EC2 インスタンス名一覧を取得

AWS CLI で EC2 に設定されたタグから特定のキーを指定し、その値をリストするスクリプトです。
以下は Key = Name でその値を取得しています。

EC2 インスタンス名の一覧を取得したい意図です。

1
aws ec2 describe-instances --query 'Reservations[*].Instances[*].Tags[?Key == `Name`].Value' --output text

以上
参考になれば幸いです。

asdf で terraform 複数バージョン管理

asdf で terraform 複数バージョン管理


terraform はバージョンアップ頻度が高く、
プロジェクトによってバージョン差分が生じるので
複数バージョンを管理できると運用がスムーズです。

続きを読む

SAM プロジェクトで管理する API Gateway のアクセスログを有効化する


概要

SAM プロジェクトで管理する API Gateway について
AWS Config rule: api-gw-execution-logging-enabled に対応すべく、
アクセスログを有効化した際にハマった話です。

Events.*.Type = Api で作成した API Gateway では SAM 上でアクセスログ有効化の設定ができません。

どのようにしたら API Gateway のアクセスログ有効化できるか調査しました。

続きを読む