データ分析観点から見た 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

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

Datadog 子組織の削除方法

備忘録として
Datadog の子組織の削除方法をまとめます。

手順

1. 監視や API Key, Application Key を削除

  • 監視が残っていると削除できない場合があります。
  • API Key を削除することで API Key を利用した Datadog Agent からのデータ収集ができなくなり、意図しないコストの発生を抑える意図があります。

2. サポートに連絡

サポートの状況次第ですが、おおよそ 1週間程度で削除していただけました。

以上です。

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 は実行時間に対して従量課金されるのでコストも抑制できるメリットがあります。

続きを読む

asdf で ruby の最新バージョンが見つからない時


asdf で ruby の最新バージョンを利用したかったけど、リストになくて困った時の話です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ asdf list all ruby

...
3.1.0
3.1.1
3.1.2
3.1.3
3.2.0-dev
3.2.0-preview1
3.2.0-preview2
3.2.0-preview3
3.2.0-rc1
3.2.0
3.3.0-dev
...

3.3.0 以上を利用したいのに出てこない💦

続きを読む

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')

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

Python ファイル読み込み


Python でファイル内容を読み込みして Azure OpenAI Service に読ませたい時があったのでまとめました。

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
import os
import sys
from docx import Document
from pypdf import PdfReader

# .docx
def read_docx(filepath):
doc = Document(filepath)
full_text = []
for para in doc.paragraphs:
full_text.append(para.text)
return "\n".join(full_text)

# .pdf
def read_pdf(filepath):
reader = PdfReader(filepath)
full_text = ""
for p in reader.pages:
full_text += p.extract_text()
return full_text

# .txt, .md etc...
def read_txt(filepath):
with open(filepath, 'r') as file:
content = file.read()
return content

def main(filepath):
_, ext = os.path.splitext(filepath)

if ext == '.docx':
t = read_docx(filepath)
elif ext == '.pdf':
t = read_pdf(filepath)
else:
t = read_txt(filepath)

print(t)


if __name__ == "__main__":
filepath = sys.argv[1]
main(filepath)

https://gist.github.com/kenzo0107/456439de57b3640c053cf369ca42f358

以前ファイル内容を 1 行ずつ、yaml をパース等は実施しましたので、そちらも参考まで。

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

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 推奨値を設定する
続きを読む