Amazon ECSログ基盤の移行:CloudWatch LogsからAthenaへ

こんにちは、L小川です。
突然ですが、みなさんはECSのログ検索には何を使っていますか?
面倒な設定が不要なCloudWatch Logsを使っている方も多いかと思います。
ただ、ログの量が多くなってくるとコストが気になるところです。
サービスの成長やユーザーの増加に伴い、気がついたらログの量もCloudWatch Logsの料金も増えていた、なんてことがあるのではないでしょうか。(弊社でもそういったケースがありました)
今回は、コスト削減・よりよい開発者体験のため、ECSのログ基盤をCloudWatch LogsからAthenaに移行した話をご紹介します。

CloudWatch Logsはログの取り込み料金が高い

ログの取り込みと保存料金について、CloudWatch LogsとFireLens・Firehose・S3で、ざっくりと比較してみます。

CloudWatch Logs FireLens・Firehose・S3
取り込み $0.76/GB $0.076/GB
保存 $0.033/GB $0.025/GB

Firehoseログ取り込み料金内訳( Amazon Data Firehose の料金 – ストリーミングデータパイプライン – Amazon Web Services )

  • 最初の 500 TB/月:$0.036
  • Amazon S3 配信の動的パーティショニング
    • 動的パーティショニングを通じて処理される GB あたり:$0.032
    • 配信された 1,000 S3 オブジェクトあたり:$0.008

S3へのPUTやFirehoseでのLambda実行などの料金は反映していないので正確なものではありませんが、ログの保存先をCloudWatch LogsからS3に移行することでコスト削減できることは間違いありません。

構成

ログ参照基盤の構成図

最終的に上記の構成となりました。

S3の日付パーティションはFirehoseでJST時刻にすべし

当初はFireLensから直接S3にログを転送していましたが、S3の日付パーティションがUTC時刻で区切られる仕様のため、Athenaでの検索が使いづらくなってしまいました。FireLensコンテナの環境変数や設定ファイルでJSTに変えようと試したものの、結果は変わりませんでした。Firehoseでは日付パーティションのタイムゾーンを指定できるので、「もっと早く気づいていれば……!」と思わずにいられませんでした。

また、Athenaは小さいサイズのファイルが多いとパフォーマンスが低下するということで、128MB以上のファイルサイズにすることが推奨されています。

Amazon Athena のパフォーマンスチューニング Tips トップ 10 | Amazon Web Services ブログ

Firehoseにはそのあたりもイイ感じに処理してくれる「バッファサイズ」「バッファ間隔」という設定があるので、Athenaを使うならFirehoseもセットで導入したいところです。

各種サービスの設定例

ECS

ログ取得対象コンテナ

「AWS Firelens 経由で Firehose にログをエクスポートする」を選択します。

ECSログ収集対象コンテナ設定

FireLensコンテナ

Dockerイメージ

AWSが提供しているfluentbitのdockerイメージ「public.ecr.aws/aws-observability/aws-for-fluent-bit:init-latest」を指定します。

FireLensのログ

FireLensのログ出力先は下記の理由からCloudWatch Logsにしました。

  • FireLensのログは起動・終了・エラー時にしか出力されない = ログの量は少なく、コスト面での懸念なし
  • 設定ミスによる起動エラーはすぐに確認したい
  • FireLensのログもFirehose→S3→Athenaにすると、FireLens用に各種リソースを作るのが面倒
不要なログを除外

FireLensからは以下のようなログが出力されます。(ECS on EC2の場合)
コンテナから出力されたログは”log”に格納されています。

S3に配置した設定ファイルで不要なログ情報を除外します。
環境変数のキーにaws_fluent_bit_init_s3_{連番}と指定することで、S3に配置したFluent Bit設定ファイルを読み込み、FireLensの挙動を変更できます。

環境変数 キー
設定値 aws_fluent_bit_init_s3_1 S3に配置した設定ファイルのarn

ECSタスクロール

Firehose・S3の各種権限を追加します。

Lambda

以下はFirehoseに送られたログを構文処理するLambdaのサンプルです。

partitionKey

FireLensから転送されたログから環境名とコンテナ名を取得し、recordのmetadataにpartitionKeyを格納することで、環境・コンテナごとのS3フォルダにログを転送できるようにしています。

※ FirehoseのS3 バケットプレフィックスに「!{partitionKeyFromLambda:environment}/!{partitionKeyFromLambda:container}/!{timestamp:yyyy/MM/dd}/」を設定します。(後述)

上記のLambdaには記載されていませんが、ログ検索でよく使うIPアドレスやステータスコード等の項目を取り出してpayloadに格納すれば、Athenaのクエリで検索しやすくなります。
※Athenaのテーブルに対応するカラムを作成しておく必要があります。

Firehose

レコードを変換および転換

上述のログ処理用Lambdaを指定します。

Firehose設定画面:レコード変換および転換

送信先の設定

  • 動的パーティション:有効
  • S3 バケットプレフィックス:!{partitionKeyFromLambda:environment}/!{partitionKeyFromLambda:container}/!{timestamp:yyyy/MM/dd}/
  • S3 バケットエラー出力プレフィックス:{環境名}/firehose-error/!{firehose:error-output-type}/!{timestamp:yyyy/MM/dd}/
  • S3 バケットと S3 エラー出力プレフィックスタイムゾーン:Asia/Tokyo

Firehose設定画面:送信先の設定

Athenaでは小さいサイズのファイルが多数あるとパフォーマンスが落ちるとのことで、128MBのバッファサイズが推奨されています。まずは推奨の値を設定してみて、その後様子を見ながら必要に応じて調整していくつもりです。

Athena

データベースを作成します。

データベース名はケバブケースではエラーになってしまったので、スネークケースで作成しました。

Athena には、毎日増えるログのパーティション管理を自動化できる パーティション射影 という機能があります。
次に、このパーティション射影を使うテーブルを作成します。

projection.enabled=’true’:このテーブルがパーティション射影を使うことを示します

projection.partition_date.format=’yyyy/MM/dd’:S3のフォルダ名の形式を指定します

storage.location.template:パーティション毎の実データの場所を定義します

ROW FORMAT SERDE ‘org.openx.data.jsonserde.JsonSerDe’:1行1JSONのログであることを示し、指定したカラムに値を自動的にマッピングできるようにします

元のログメッセージは log_original というカラムにそのまま保存しています。
これは、コンテナ起動時のログを Lambda の構文処理対象から外していることや、想定外のフォーマットでログの取り扱いに失敗した場合でも、元データを確認できるようにするためです。

Athenaレコード一覧画面キャプチャ

つまずいたこと

S3にログファイルはあるのにログがAthenaに出てこない

S3にログファイルは置かれているのに、その内容がAthenaの検索結果に出てこないという事象が起きていました。
調べたところS3に置かれたログ・ファイルのフォーマットが原因でした。
Athenaのテーブル作成SQLのROW FORMAT SERDEで指定するorg.openx.data.jsonserde.JsonSerDeでは、以下のような1行 = 1JSONが想定されています。

OpenX JSON SerDe – Amazon Athena

FireLensからFirehose経由でS3に置かれたファイルは、一行に複数のJSONが連なっていたため、JSONパースエラーが起き、Athenaでの検索結果が空となっていました。

Firehoseで実行するLambdaでログデータに改行を加え、1行1JSONとしたところログがAthenaで表示できるようになりました。

本運用で発覚したFirehoseのスロットリング

本番環境のログをFireLensで転送するようにしたところ、以下のログがFireLensコンテナに出ていました。

AWSサポートに聞いたところ、発生していた事象とその対応策について教えてもらえました。

FireLens→Firehose転送時にスロットリングが発生し、FireLens側でリトライしていたようです。
元々、Firehoseのスループット制限値であるBytesPerSecondLimitの上限は1MiB/秒に設定されています。

Amazon Data Firehose のクォータ – Amazon Data Firehose

スロットリングが発生するとFirehoseのBytesPerSecondLimitは自動でその上限を引き上げてくれます。下図にあるように、InComminBytes(上)のピークに合わせてBytesPerSecondLimit(下)の値が段階的に増加しています。

Firehose InCommingBytesの増加

Firehose BytesPerSecondLimitの増加

この上限値の自動引き上げは一時的なものではなく、一度上がった上限値はそのまま維持されています。
ログ転送に失敗してもFluent Bitの機能でリトライしてくれるのですが、そのデフォルト値は1となっています。スループット制限値の上限を自動で引き上げている間にスロットリングが発生し、リトライ数の上限に達してしまう可能性もあるため、ECSタスク定義 > 各コンテナのログ設定でRetry_Limitの値を増やしました。

Retry_Limitの追加

上記の設定を反映したタスクを起動した後、FireLensコンテナの設定ファイルで設定が反映されていることを確認できました。

終わりに

今回はAthenaを利用するための具体的な設定方法や実際につまず いた話を共有しました。
Athenaに移行することでコスト削減だけでなく、ログ周りの情報が整理でき、調査時間の短縮が期待できます。
移行検討の際、この記事が参考になれば幸いです。
今後はログ出力指針も整備して、より効果的にログを活用できるようにしていければと考えています。

新卒・中途エンジニア募集中!

100%自社内開発!学術に特化した自社サービスで設計からリリースまでフルスタックなスキルを身につけませんか?
テレワーク主体ですがコミュニケーションも活発でチームのつながりも大切にしている環境です。

カジュアル面談からお気軽にお越しください!

採用情報はこちら

アトラス採用情報。新卒・中途エンジニア募集中!