MNTSQ Techブログ

「MNTSQ(モンテスキュー)」のTechブログです。

ECS Exec のセッションログを専用 S3 バケットへ分離する

MNTSQ Tech Blog TOP > 記事一覧 > ECS Exec のセッションログを専用 S3 バケットへ分離する

はじめに

Amazon Linux 2 (以下 AL2) の EOL (2026 年 6 月 30 日) が近付いてくる昨今、皆様いかがお過ごしでしょうか。

弊社では SSH 踏み台として使う EC2 インスタンスを AL2 ベースで用意し、運用作業の起点として長年取り扱ってきました。運用者はここを経由して ECS タスクや各種マネージドサービスへアクセスしてきた経緯があり、単純な SSH 踏み台ではなく、運用機能を集約した実行基盤として機能してきた格好です。

前述の通り ECS 最適化 AL2 AMI の EOL が迫る状況下、これを契機にこの踏み台の処遇を決める必要が出てきました。AWS は同案内において後継として Amazon Linux 2023 (以下 AL2023) への移行を推奨しているため、これに沿えば AL2023 で素直に再構築するのが定石となります。一方、棚卸してみると踏み台はいくつもの運用基盤を兼務する構造になっており、AL2023 で再構築すれば短期的な EOL 対応は済むものの、これらの構造をそのまま AL2023 のサポート期限 (2028 年) まで持ち越すことになります。

踏み台が抱える各役割はそれぞれ別個の代替手段が出揃ってきていたため、AL2 の EOL を契機にして「踏み台ごと畳んでしまい、機能を別の代替先へ分散させる」方針を取ることにしました。本稿はその分散先のうち、運用者が ECS Exec で直接コンテナへ入る経路のセッションログを、アプリログとは別系統で取り扱う仕組みの話です。

既存構成

踏み台を経由する従来構成では、運用者の操作ログは踏み台側でまるごと取得できていました。踏み台を廃止して ECS Exec へ切り替えると、この経路が無くなります。

ECS Exec の出力先はクラスタ単位の executeCommandConfiguration で制御できます。loggingDEFAULT のまま (= 明示設定なし) だと、タスク定義側で指定された awslogs に運用者のターミナル出力が同居する格好となります。アプリログには Firelens 経由で CloudWatch Logs / S3 / Datadog の 3 系統へ流すルーティングがすでに敷かれているため、ここに ECS Exec のセッションログが乗っかると、出力先・保管期間・閲覧権限がアプリログ側の都合に縛られてしまいます。

課題感

運用者の操作ログとアプリログが同じ経路で混ざってしまうと、以下のような不便が出てきます。

  • 後から運用者の操作だけを切り出して追うのに難儀する
  • アプリログのライフサイクルに引きずられて長期保管も難しくなる

そこで運用者の操作ログを専用 S3 バケットへ独立して書き出すように整備しました。本稿ではその設計判断と実装手順を取り扱います。

設計

新たに専用 S3 バケットを設け、ECS Exec のセッションログをすべてここへ集約することにしました。

CloudWatch Logs ではなく S3 に倒す

ECS Exec の出力先は S3 と CloudWatch Logs のいずれか (あるいは両方) を選べますが、本件は S3 単独としました。理由は次のとおりです。

  • 既存の SSM Session Manager の操作ログを同じく専用 S3 バケットに集約しており、運用者の操作ログは「S3 集約してから Athena で横断的に追う」運用と既に親和性がある
  • 保管期間が長く読み出し頻度の低いログを置く先として、CloudWatch Logs より S3 のほうがコスト効率に優れる
  • ライフサイクル制御が CloudWatch Logs より自由に効き、長期保管要件に応じて低頻度アクセス向けの STANDARD_IA やアーカイブ向けの GLACIER_IR を組み合わせて吊るしで設計できる

ここでいう「S3 集約してから Athena で横断的に追う」経路は、業務時間外の不審操作を自動検知して Slack 通知する社内の仕組みである A2RM (監査回答強制マン) の動作基盤にもなっています。ECS Exec ログを同経路に乗せておくことで、将来同じ枠組みで取り扱える余地が生まれます。

https://tech.mntsq.co.jp/entry/2026/03/17/114506

クラスタ単位で S3 prefix を切る

ECS Exec ログを書き出す S3 オブジェクトキーには、クラスタ側の executeCommandConfiguration で任意の接頭辞を指定できます。本件ではこれを ${env}-${service}-${cluster_id}/ というクラスタ単位の名前空間にしてあります。複数サービスの ECS Exec ログが同じバケット内に同居するため、接頭辞をクラスタ単位で分けておかないと、後から Athena でクエリするときにフィルタ条件が複雑化します。

実装

ECS Exec ログの分離整備は次の 2 段階に分けて投入しました。

  1. 専用 S3 バケットの新設と、ECS タスクロールへの S3 関連権限の追加
  2. ECS クラスタの executeCommandConfigurationlogging = OVERRIDE に切り替え

順序依存があるため、必ず 1 を先に着地させてから 2 を投入する必要があります。クラスタ側の executeCommandConfigurationOVERRIDE に切り替えた瞬間、運用者が ECS Exec で接続する度に、タスクロールが当該 S3 バケットに対して以下の API を呼ぶようになるためです。

  • s3:GetBucketLocation
  • s3:GetEncryptionConfiguration (バケット側で s3_bucket_encryption_enabled = true を設定しているため)
  • s3:PutObject

これらに対する IAM 権限がタスクロール側に揃っていない状態でクラスタを切り替えると、運用者が ECS Exec を叩いた瞬間に AccessDenied で落ちます。踏み台廃止に向けて ECS Exec の信頼性を担保したい局面でこれが起きると本末転倒なので、IAM 整備を先に着地させてからクラスタ切替を投入する順序を踏むのが安全です。

ECS タスクロールに付与する IAM ポリシー

ECS タスクが ECS Exec のセッションを開き、その出力ログを S3 バケットへ書き出すために必要な権限を、タスクロールへアタッチするポリシーとして以下のように定義します。

IAM policy document の Terraform 定義

data "aws_iam_policy_document" "ecs_exec" {
  # SSM Agent によるセッション確立に必要
  statement {
    actions = [
      "ssmmessages:OpenDataChannel",
      "ssmmessages:OpenControlChannel",
      "ssmmessages:CreateDataChannel",
      "ssmmessages:CreateControlChannel",
    ]
    resources = ["*"]
  }

  # ECS Exec ログを専用 S3 バケットへ書き出すために必要
  statement {
    actions   = ["s3:GetBucketLocation"]
    resources = ["*"]
  }

  statement {
    actions   = ["s3:GetEncryptionConfiguration"]
    resources = [aws_s3_bucket.ecs_exec_logs.arn]
  }

  statement {
    actions   = ["s3:PutObject"]
    resources = ["${aws_s3_bucket.ecs_exec_logs.arn}/*"]
  }
}

s3:GetBucketLocation はバケットのリージョン解決に、s3:GetEncryptionConfiguration はセッション開始時のバケット暗号化設定の検証に、s3:PutObject は実際のログ書き出しにそれぞれ必要となります。s3:GetEncryptionConfiguration はバケット ARN に絞った権限とすることで、不要な走査を抑制できます。

ECS クラスタの executeCommandConfiguration

ECS クラスタの configuration.execute_command_configuration に出力先 S3 バケットと接頭辞、暗号化検証の有効化を指定します。

aws_ecs_cluster の Terraform 定義

resource "aws_ecs_cluster" "main" {
  # ... クラスタ自体の既存設定 ...

  configuration {
    execute_command_configuration {
      logging = "OVERRIDE"

      log_configuration {
        s3_bucket_name               = aws_s3_bucket.ecs_exec_logs.bucket
        s3_key_prefix                = "${var.env}-${var.service}-${var.cluster_id}/"
        s3_bucket_encryption_enabled = true
      }
    }
  }
}

logging = "OVERRIDE" で明示設定モードへ切り替え、log_configuration でその内容を与える格好です。s3_bucket_encryption_enabled = true を有効にすると、セッション開始時に SSM Agent がバケット側の暗号化設定を s3:GetEncryptionConfiguration で検証する経路に倒れます。

おわりに

本稿では、踏み台廃止に向けて運用者の ECS Exec セッションログを専用 S3 バケットへ分離した取り組みについて、設計判断と実装手順の両面から取り扱いました。

ECS Exec で運用者がコンテナ内で叩いたコマンドは、平時はあまり関心をむけられることの少ない内容です。しかし、いざ追跡や監査が必要になったときに参照先がアプリログと混ざっているか独立しているかで、後の動きやすさはずいぶん変わります。整備した瞬間に何かが大きく変わるわけではないものの、踏み台廃止のように接続経路を切り替える場面で後からじわじわ効いてくる類の作りだと思います。

ECS Exec のセッションログをアプリログとは別経路へ分離する作りは、要点さえ押さえれば素直に組めるものになっています。同じような整備に取り組む方の一助となれば幸いです。

文責:MNTSQ 株式会社 SRE 秋本

注記:この記事は文責者の過去記事と弊社内のドキュメントをもとに Claude Opus 4.7 が作成した内容を8割程度そのまま使用しています