[アップデート] VPC Flow LogsでECSに関する情報を記録できるようになりました

ECSサービス間の通信を把握する際に
2024.05.20

どのECSタスクからの通信なのかログから判断したい

こんにちは、のんピ(@non____97)です。

皆さんはどのECSタスクからの通信なのかログから判断したいなと思ったことはありますか? 私はあります。

ECS上で複数のコンテナを動かしている場合、VPC Flow Logsに記録されているIPアドレスのみでどのECSタスクからの通信なのかを判断するのは非常に骨が折れます。加えて、タスクが入れ替わるとIPアドレスも入れ替わるので、ログ出力時点でどのタスクがどのIPアドレスを使用しているのか把握しておく必要があります。

今回、アップデートにより、VPC Flow LogsでECSに関する情報を記録できるようになりました。

AWS Blogsにも投稿されていますね。

実際に試してみたので紹介します。

いきなりまとめ

  • 送信元のECSクラスターやECSサービス、ECSタスク、コンテナIDなどの情報をVPC Flow Logsに記録できるようになった
  • VPC Flow Logs設定時点でECSクラスターがVPC内に存在している必要がある
  • 検証をした限り、VPC Flow Logsで記録されるコンテナIDは、実際の送信元のコンテナIDではない可能性がある

取得できるようになった情報

取得できるようになった情報は以下のとおりです。

フィールド名 説明
ecs-cluster-arn トラフィックの発信元ECS タスクのECS クラスターARN
サブスクリプションに含めるには、ecs:ListClusters の権限が必要
ecs-cluster-name トラフィックの発信元ECS タスクのECS クラスター名
サブスクリプションに含めるには、ecs:ListClusters の権限が必要
ecs-container-instance-arn トラフィックの発信元ECS タスクのECSコンテナインスタンスARN
Fargate の場合、このフィールドは「-」になる
サブスクリプションに含めるには、ecs:ListClusters および ecs:ListContainerInstances を呼び出す権限が必要
ecs-container-instance-id トラフィックの発信元ECS タスクのECS コンテナインスタンスID
Fargate の場合、このフィールドは「-」になる
サブスクリプションに含めるには、ecs:ListClusters および ecs:ListContainerInstances を呼び出す権限が必要
ecs-container-id トラフィックの発信元ECS タスク上のコンテナのDocker ランタイム ID
ECS タスクに 1 つ以上のコンテナがある場合に最初のコンテナの Docker runtie IDを示す
サブスクリプションに含めるには、ecs:ListClusters を呼び出す権限が必要
ecs-second-container-id トラフィックの発信元ECS タスク上のコンテナのDocker ランタイム ID
ECS タスクに 1 つ以上のコンテナがある場合に2つ目ののコンテナの Docker runtie IDを示す
サブスクリプションに含めるには、ecs:ListClusters を呼び出す権限が必要
ecs-service-name トラフィックの発信元ECS タスクのECS サービス名
ECS タスクが ECS サービスによって実行されたものではない場合、このフィールドは「-」になる
サブスクリプションに含めるには、ecs:ListClusters および ecs:ListServices を呼び出す権限が必要
ecs-task-definition-arn トラフィックの発信元ECS タスクのECS タスク定義の ARN
サブスクリプションに含めるには、ecs:ListClusters および ecs:ListTaskDefinitions を呼び出す権限が必要
ecs-task-arn トラフィックの発信元ECS タスクのARN
サブスクリプションに含めるには、ecs:ListClusters および ecs:ListTasks を呼び出す権限が必要
ecs-task-id トラフィックの発信元ECS タスクのID
サブスクリプションに含めるには、ecs:ListClusters および ecs:ListTasks を呼び出す権限が必要

いずれもParquetのデータタイプはSTRINGです。

ecs-cluster-arnecs-cluster-nameecs-container-instance-arnecs-container-instance-idなど情報が部分的に重複するものもあるので、どのフィールドを記録するかはお好みで良いでしょう。

2024/5/27追記 ここから

AWS公式ドキュメントにもECS関連のログについて追記ありました。

ECS関連のログについての制限は以下のとおりです。

  • ECSフィールドをを含むVPC Flow Logsの設定をするには、アカウントに少なくとも1つのECS クラスターが含まれている必要がある
  • 記録されるECSタスクがVPC Flow Logsのサブスクリプションの所有者でない場合、ECSフィールドは記録されない
  • VPC/SubnetのリソースレベルでECSフィールドを持つVPC Flow Logsサブスクリプションを作成する場合、ECS以外のENIで生成されたトラフィックも配信される
    • ECSフィールドの値は、ECS以外のIPトラフィックに対して-になる
  • ecs-container-idecs-second-container-idはVPC Flow LogsサービスがECSイベントストリームから受信した順に並べられる
    • ECSのコンソールやDescribeTask API 呼び出しで表示される順序と同じであることは保証されない
    • タスクの実行中にコンテナがSTOPPEDステータスになった場合、ログに表示され続ける可能性がある
  • ECSメタデータとIPトラフィックログは2つの異なるソースから計算する
    • 上流の依存関係から必要な情報をすべて取得すると、すぐにECSトラフィックの計算を開始する
    • 新しいタスクを開始すると、以下1と2の条件に当てはまるタイミングでECSフィールドの計算を開始します。
    1. 基礎となるネットワークインターフェースのIPトラフィックを受信したとき
    2. タスクが実行中であることを示すECSタスクのメタデータを含むECSイベントを受信したときに
    • タスクを停止した後、以下1と2の条件に当てはまるタイミングでECSフィールドの計算を開始する
    1. 基礎となるネットワークインターフェースのIPトラフィックを受信しなくなったとき、または1日以上遅延したIPトラフィックを受信したとき
    2. タスクが実行中でなくなったことを示すECSタスクのメタデータを含むECSイベントを受信したとき
  • awsvpcのECSタスクのみ記録される

通常のIPトラフィックログとECS関連の情報は異なるソースから計算すると言うのはポイントですね。

そのため、VPC Flow Logsで出力されるECS関連のフィールドの値は、実際のコンテナの通信内容に依存せずENI単位で固定の値が出力されることになります。

2024/5/27追記 ここまで

やってみた

検証環境

実際に試してみます。

検証環境は以下のとおりです。

VPC Flow LogsでECSに関する情報を記録できるようになりました検証環境構成図

以下の通信をチェックします。

  • ECSクラスター間の通信
  • ECS on FargateのECSクラスターからインターネットへの通信

ECSクラスター間の通信をするためにService Connectを使って名前解決をするようにしています。

検証環境はAWS CDKでデプロイしました。使用したコードは以下リポジトリに保存しています。

注意点はVPC Flow Logs設定時点でVPC内にECSクラスターが存在しない場合はCaller is not authorized to obtain ECS field(s). Failed with error: {A minimum of 1 ECS Cluster is required to Create Flow Logs with ECS Fields}とエラーになる点です。

フローログを作成できません

無理矢理感ありますが、今回は以下のようにECSクラスターの作成が完了したら、VPC Flow Logsを作成するようにしています。

./lib/ecs-stack.ts

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import { EcsProps } from "../parameter/index";
import { NetworkConstruct } from "./construct/network-construct";
import { EcsFargateConstruct } from "./construct/ecs-fargate-construct";

export interface EcsStackProps extends cdk.StackProps, EcsProps {}

export class EcsStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: EcsStackProps) {
    super(scope, id, props);

    const networkConstruct = new NetworkConstruct(this, "NetworkConstruct", {
      ...props.network,
    });

    const privateDnsNamespace =
      new cdk.aws_servicediscovery.PrivateDnsNamespace(
        this,
        "PrivateDnsNamespace",
        {
          name: "local",
          vpc: networkConstruct.vpc,
        }
      );

    const ecsFargateConstruct = new EcsFargateConstruct(
      this,
      "EcsFargateConstruct",
      {
        networkConstruct,
        privateDnsNamespace,
        discoveryName: "ecs-fargate",
      }
    );
    ecsFargateConstruct.node.addDependency(privateDnsNamespace);

    const ecsFargateConstruct2 = new EcsFargateConstruct(
      this,
      "EcsFargateConstruct2",
      {
        networkConstruct,
        privateDnsNamespace,
        discoveryName: "ecs-fargate2",
      }
    );

    ecsFargateConstruct2.node.addDependency(privateDnsNamespace);
    ecsFargateConstruct2.node.addDependency(ecsFargateConstruct);

    networkConstruct.addFlowLog(ecsFargateConstruct2);
  }
}

./lib/construct/network-construct.ts

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";
import { NetworkProperty, LifecycleRule } from "../../parameter/index";
import { LogBucketConstruct } from "./log-bucket-construct";

export interface NetworkConstructProps extends NetworkProperty {}

export class NetworkConstruct extends Construct {
  readonly vpc: cdk.aws_ec2.IVpc;
  readonly flowLogsOptions?: cdk.aws_ec2.S3DestinationOptions;
  readonly flowLogsTrafficType?: cdk.aws_ec2.FlowLogTrafficType;
  readonly flowLogsLifecycleRules?: LifecycleRule[];

  constructor(scope: Construct, id: string, props: NetworkConstructProps) {
    super(scope, id);

    // VPC
    const vpc = new cdk.aws_ec2.Vpc(this, "Default", {
      ipAddresses: cdk.aws_ec2.IpAddresses.cidr(props.vpcCidr),
      enableDnsHostnames: true,
      enableDnsSupport: true,
      natGateways: props.natGateways,
      maxAzs: props.maxAzs,
      subnetConfiguration: props.subnetConfigurations,
      gatewayEndpoints: {
        S3: {
          service: cdk.aws_ec2.GatewayVpcEndpointAwsService.S3,
        },
      },
    });
    this.vpc = vpc;

    this.flowLogsOptions = props.vpcFlowLogs.options;
    this.flowLogsTrafficType = props.vpcFlowLogs.trafficType;
    this.flowLogsLifecycleRules = props.vpcFlowLogs.lifecycleRules;
  }

  // VPC Flow Logs
  addFlowLog = (dependencyConstruct?: Construct) => {
    const flowLogsBucketConstruct = new LogBucketConstruct(
      this,
      "FlowLogsBucket",
      {
        lifecycleRules: this.flowLogsLifecycleRules,
      }
    );

    const flowLogs = this.vpc.addFlowLog("FlowLogsToS3", {
      destination: cdk.aws_ec2.FlowLogDestination.toS3(
        flowLogsBucketConstruct.bucket,
        undefined,
        this.flowLogsOptions
      ),
      trafficType: this.flowLogsTrafficType,
      maxAggregationInterval:
        cdk.aws_ec2.FlowLogMaxAggregationInterval.TEN_MINUTES,
      logFormat: [
        cdk.aws_ec2.LogFormat.VERSION,
        cdk.aws_ec2.LogFormat.ACCOUNT_ID,
        cdk.aws_ec2.LogFormat.INTERFACE_ID,
        cdk.aws_ec2.LogFormat.SRC_ADDR,
        cdk.aws_ec2.LogFormat.DST_ADDR,
        cdk.aws_ec2.LogFormat.SRC_PORT,
        cdk.aws_ec2.LogFormat.DST_PORT,
        cdk.aws_ec2.LogFormat.PROTOCOL,
        cdk.aws_ec2.LogFormat.PACKETS,
        cdk.aws_ec2.LogFormat.BYTES,
        cdk.aws_ec2.LogFormat.START_TIMESTAMP,
        cdk.aws_ec2.LogFormat.END_TIMESTAMP,
        cdk.aws_ec2.LogFormat.ACTION,
        cdk.aws_ec2.LogFormat.LOG_STATUS,
        cdk.aws_ec2.LogFormat.VPC_ID,
        cdk.aws_ec2.LogFormat.SUBNET_ID,
        cdk.aws_ec2.LogFormat.INSTANCE_ID,
        cdk.aws_ec2.LogFormat.TCP_FLAGS,
        cdk.aws_ec2.LogFormat.TRAFFIC_TYPE,
        cdk.aws_ec2.LogFormat.PKT_SRC_ADDR,
        cdk.aws_ec2.LogFormat.PKT_DST_ADDR,
        cdk.aws_ec2.LogFormat.REGION,
        cdk.aws_ec2.LogFormat.AZ_ID,
        cdk.aws_ec2.LogFormat.SUBLOCATION_TYPE,
        cdk.aws_ec2.LogFormat.SUBLOCATION_ID,
        cdk.aws_ec2.LogFormat.PKT_SRC_AWS_SERVICE,
        cdk.aws_ec2.LogFormat.PKT_DST_AWS_SERVICE,
        cdk.aws_ec2.LogFormat.FLOW_DIRECTION,
        cdk.aws_ec2.LogFormat.TRAFFIC_PATH,
      ],
    });

    const cfnFlowLogs = flowLogs.node.defaultChild as cdk.aws_ec2.CfnFlowLog;
    cfnFlowLogs.logFormat +=
      "  \
      ${ecs-cluster-name} \
      ${ecs-cluster-arn} \
      ${ecs-container-instance-id} \
      ${ecs-container-instance-arn} \
      ${ecs-service-name} \
      ${ecs-task-definition-arn} \
      ${ecs-task-id} \
      ${ecs-task-arn} \
      ${ecs-container-id} \
      ${ecs-second-container-id}";

    if (dependencyConstruct) {
      flowLogs.node.addDependency(dependencyConstruct);
    }
  };
}

動作確認

動作確認です。

ECSクラスター間の通信を試します。

2つ目のECSクラスターのNginxコンテナにECS Execをして、1つ目のECSクラスターで登録したecs-fargate.localにcurlをします。

$ cluster_name="EcsStack-EcsFargateConstruct2Cluster15146226-YluNfbr7sUDT"

$ task_id=$(
  aws ecs list-tasks \
    --cluster "${cluster_name}" \
    --query 'taskArns[0]' \
    --output text \
  |  sed 's/.*'"${cluster_name}"'\///'
)

$ aws ecs execute-command \
  --cluster "${cluster_name}" \
  --task "${task_id}" \
  --container NginxContainer \
  --interactive \
  --command "/bin/sh"

$ cat /etc/hosts
127.0.0.1 localhost
10.10.0.17 ip-10-10-0-17.ec2.internal
127.255.0.1 ecs-fargate.local
2600:f0f0:0:0:0:0:0:1 ecs-fargate.local
127.255.0.2 ecs-fargate2.local
2600:f0f0:0:0:0:0:0:2 ecs-fargate2.local

$ curl -v http://ecs-fargate.local
*   Trying 127.255.0.1:80...
* Connected to ecs-fargate.local (127.255.0.1) port 80 (#0)
> GET / HTTP/1.1
> Host: ecs-fargate.local
> User-Agent: curl/7.88.1
> Accept: */*
> 
< HTTP/1.1 200 OK
< server: envoy
< date: Sun, 19 May 2024 22:35:39 GMT
< content-type: text/html
< content-length: 615
< last-modified: Tue, 23 Apr 2024 14:04:32 GMT
< etag: "6627bff0-267"
< accept-ranges: bytes
< x-envoy-upstream-service-time: 2
< 
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
* Connection #0 to host ecs-fargate.local left intact

通信できましたね。

続いて、ECS on FargateのECSクラスターからインターネットへの通信です。

2つ目のECSクラスターのBusyBoxコンテナにECS Execをして、8.8.8.8にpingをします。

$ cluster_name="EcsStack-EcsFargateConstruct2Cluster15146226-YluNfbr7sUDT"

$ task_id=$(
  aws ecs list-tasks \
    --cluster "${cluster_name}" \
    --query 'taskArns[0]' \
    --output text \
  |  sed 's/.*'"${cluster_name}"'\///'
)

$ aws ecs execute-command \
  --cluster "${cluster_name}" \
  --task "${task_id}" \
  --container BusyboxContainer \
  --interactive \
  --command "/bin/sh"

$ ping -c 4 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=116 time=1.500 ms
64 bytes from 8.8.8.8: seq=1 ttl=116 time=1.481 ms
64 bytes from 8.8.8.8: seq=2 ttl=116 time=1.461 ms
64 bytes from 8.8.8.8: seq=3 ttl=116 time=1.450 ms

--- 8.8.8.8 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 1.450/1.473/1.500 ms

こちらも問題なくできました。

AthenaでS3バケットに出力されたVPC Flow Logsに対してクエリを叩いてみましょう。

以下のDDLを実行してテーブルを作成します。

CREATE EXTERNAL TABLE `vpc_flow_logs_partition_projection` (
  `version` int, 
  `resource_type` string, 
  `account_id` string, 
  `srcaddr` string, 
  `dstaddr` string, 
  `srcport` int, 
  `dstport` int, 
  `protocol` bigint, 
  `packets` bigint, 
  `bytes` bigint, 
  `start` bigint, 
  `end` bigint, 
  `action` string,
  `log_status` string,
  `vpc_id` string,
  `subnet_id` string,
  `instance_id` string,
  `tcp_flags` int,
  `type` string,
  `pkt_srcaddr` string,
  `pkt_dstaddr` string,
  `region` string,
  `az_id` string,
  `sublocation_type` string,
  `sublocation_id` string,
  `pkt_src_aws_service` string,
  `pkt_dst_aws_service` string,
  `flow_direction` string,
  `traffic_path` int,
  `ecs_cluster_name` string,
  `ecs_cluster_arn` string,
  `ecs_container_instance_id` string,
  `ecs_container_instance_arn` string,
  `ecs_service_name` string,
  `ecs_task_definition_arn` string,
  `ecs_task_id` string,
  `ecs_task_arn` string,
  `ecs_container_id` string,
  `ecs-second-container-id` string
)
PARTITIONED BY (
  `aws_account_id` string,
  `aws_region` string,
  `datehour` string
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ' '
LOCATION
  's3://ecsstack-networkconstructflowlogsbucketd21f5feb-eiw7koyoqmeb/AWSLogs/'
TBLPROPERTIES (
  'projection.enabled'='true', 
  'has_encrypted_data'='true', 
  "skip.header.line.count"="1",
  'projection.aws_account_id.type'='enum', 
  'projection.aws_account_id.values'='<AWSアカウントID>', 
  'projection.aws_region.type'='enum', 
  'projection.aws_region.values'='us-east-1,ap-northeast-1', 
  'projection.datehour.type'='date', 
  'projection.datehour.interval'='1',
  'projection.datehour.interval.unit'='HOURS',
  'projection.datehour.range'='NOW-1YEARS,NOW',
  'projection.datehour.format'='yyyy/MM/dd/HH', 
  'storage.location.template'='s3://ecsstack-networkconstructflowlogsbucketd21f5feb-eiw7koyoqmeb/AWSLogs/${aws_account_id}/vpcflowlogs/${aws_region}/${datehour}'
)

テーブル作成完了後、各パターンの通信のログを確認します。

まず、ECSクラスター間の通信のログを確認するクエリを叩きます。

ECSクラスター間の通信のログを確認するクエリ

SELECT *
FROM 
  vpc_flow_logs_partition_projection
WHERE
  aws_account_id='<AWSアカウントID>' and
  aws_region='us-east-1' and
  datehour='2024/05/19/22' and
  action='ACCEPT' and
  (dstport=80 or srcport=80) and
  start>1716158100

ECSクラスター間の通信のログを確認するクエリ実行結果

"version","resource_type","account_id","srcaddr","dstaddr","srcport","dstport","protocol","packets","bytes","start","end","action","log_status","vpc_id","subnet_id","instance_id","tcp_flags","type","pkt_srcaddr","pkt_dstaddr","region","az_id","sublocation_type","sublocation_id","pkt_src_aws_service","pkt_dst_aws_service","flow_direction","traffic_path","ecs_cluster_name","ecs_cluster_arn","ecs_container_instance_id","ecs_container_instance_arn","ecs_service_name","ecs_task_definition_arn","ecs_task_id","ecs_task_arn","ecs_container_id","ecs-second-container-id","aws_account_id","aws_region","datehour"
"7","<AWSアカウントID>","eni-0783b0c994717f9a4","10.10.0.20","10.10.0.17","80","49512","6","3","1020","1716158123","1716158151","ACCEPT","OK","vpc-0926fc3c811765f21","subnet-043f08d86c8ca1516","-","18","IPv4","10.10.0.20","10.10.0.17","us-east-1","use1-az6","-","-","-","-","ingress",,"EcsStack-EcsFargateConstruct2Cluster15146226-YluNfbr7sUDT","arn:aws:ecs:us-east-1:<AWSアカウントID>:cluster/EcsStack-EcsFargateConstruct2Cluster15146226-YluNfbr7sUDT","-","-","EcsStack-EcsFargateConstruct2ServiceD9DCAB71-uVoQu1GEBKtI","arn:aws:ecs:us-east-1:<AWSアカウントID>:task-definition/EcsStackEcsFargateConstruct2TaskDefinition4E5DC27B:1","88316020871d4ec7a200c1ce6d25273d","arn:aws:ecs:us-east-1:<AWSアカウントID>:task/EcsStack-EcsFargateConstruct2Cluster15146226-YluNfbr7sUDT/88316020871d4ec7a200c1ce6d25273d","88316020871d4ec7a200c1ce6d25273d-3472355196","-","<AWSアカウントID>","us-east-1","2024/05/19/22"
"7","<AWSアカウントID>","eni-0783b0c994717f9a4","10.10.0.17","10.10.0.20","49512","80","6","4","413","1716158123","1716158151","ACCEPT","OK","vpc-0926fc3c811765f21","subnet-043f08d86c8ca1516","-","2","IPv4","10.10.0.17","10.10.0.20","us-east-1","use1-az6","-","-","-","-","egress","1","EcsStack-EcsFargateConstruct2Cluster15146226-YluNfbr7sUDT","arn:aws:ecs:us-east-1:<AWSアカウントID>:cluster/EcsStack-EcsFargateConstruct2Cluster15146226-YluNfbr7sUDT","-","-","EcsStack-EcsFargateConstruct2ServiceD9DCAB71-uVoQu1GEBKtI","arn:aws:ecs:us-east-1:<AWSアカウントID>:task-definition/EcsStackEcsFargateConstruct2TaskDefinition4E5DC27B:1","88316020871d4ec7a200c1ce6d25273d","arn:aws:ecs:us-east-1:<AWSアカウントID>:task/EcsStack-EcsFargateConstruct2Cluster15146226-YluNfbr7sUDT/88316020871d4ec7a200c1ce6d25273d","88316020871d4ec7a200c1ce6d25273d-3472355196","-","<AWSアカウントID>","us-east-1","2024/05/19/22"
"7","<AWSアカウントID>","eni-06d53ec12e8eb2e45","10.10.0.20","10.10.0.17","80","49512","6","3","1020","1716158149","1716158173","ACCEPT","OK","vpc-0926fc3c811765f21","subnet-043f08d86c8ca1516","-","18","IPv4","10.10.0.20","10.10.0.17","us-east-1","use1-az6","-","-","-","-","egress","1","EcsStack-EcsFargateConstructCluster1F80E083-QjhlCq9gvCwG","arn:aws:ecs:us-east-1:<AWSアカウントID>:cluster/EcsStack-EcsFargateConstructCluster1F80E083-QjhlCq9gvCwG","-","-","EcsStack-EcsFargateConstructService485E693B-zJq54DZ1cojO","arn:aws:ecs:us-east-1:<AWSアカウントID>:task-definition/EcsStackEcsFargateConstructTaskDefinition9DD364BE:9","d077e9006cdb442dbaddcd059c7b9ba9","arn:aws:ecs:us-east-1:<AWSアカウントID>:task/EcsStack-EcsFargateConstructCluster1F80E083-QjhlCq9gvCwG/d077e9006cdb442dbaddcd059c7b9ba9","d077e9006cdb442dbaddcd059c7b9ba9-3254704778","d077e9006cdb442dbaddcd059c7b9ba9-3472355196","<AWSアカウントID>","us-east-1","2024/05/19/22"
"7","<AWSアカウントID>","eni-06d53ec12e8eb2e45","10.10.0.17","10.10.0.20","49512","80","6","4","413","1716158149","1716158173","ACCEPT","OK","vpc-0926fc3c811765f21","subnet-043f08d86c8ca1516","-","2","IPv4","10.10.0.17","10.10.0.20","us-east-1","use1-az6","-","-","-","-","ingress",,"EcsStack-EcsFargateConstructCluster1F80E083-QjhlCq9gvCwG","arn:aws:ecs:us-east-1:<AWSアカウントID>:cluster/EcsStack-EcsFargateConstructCluster1F80E083-QjhlCq9gvCwG","-","-","EcsStack-EcsFargateConstructService485E693B-zJq54DZ1cojO","arn:aws:ecs:us-east-1:<AWSアカウントID>:task-definition/EcsStackEcsFargateConstructTaskDefinition9DD364BE:9","d077e9006cdb442dbaddcd059c7b9ba9","arn:aws:ecs:us-east-1:<AWSアカウントID>:task/EcsStack-EcsFargateConstructCluster1F80E083-QjhlCq9gvCwG/d077e9006cdb442dbaddcd059c7b9ba9","d077e9006cdb442dbaddcd059c7b9ba9-3254704778","d077e9006cdb442dbaddcd059c7b9ba9-3472355196","<AWSアカウントID>","us-east-1","2024/05/19/22"

ECSクラスターやECSサービス、ECSタスク、コンテナIDが記録されていますね。

通信はEcsStack-EcsFargateConstruct2Cluster15146226-YluNfbr7sUDTからEcsStack-EcsFargateConstructCluster1F80E083-QjhlCq9gvCwGに対してリクエストをしました。各フィールドに記録されているのは通信の送信元の情報であることが分かります。

一点、リクエストのecs_container_id88316020871d4ec7a200c1ce6d25273d-3472355196となっているのが気になります。こちらのIDはBusyBoxのコンテナのIDです。リクエストはNginxのコンテナから行っています。もしかすると、タスク内のどのコンテナからの通信なのかをログのみで正確に把握することは難しいかもしれません。

ecs-second-container-idについては、タスク内のどのコンテナからの通信なのかが明らかであるならば敢えて記録する必要もないのではと考えます。送信元の考えられる候補としてECSタスク内のコンテナIDを最大2つ返しているのでしょうか。その仮説が正しいのであれば、リクエストの際にecs-second-container-idにNginxのコンテナIDが記録されないのが気になります。

ログが欠損しているのかと思い、時間を空けて2,3回試しましたが結果は変わりありませんでした。

スタックを再作成してリトライすると、ecs-container-idの値は存在しないがecs-second-container-idは存在するといったこともありました。ecs-second-container-idに記録されているコンテナIDは確かにECS Exec先のNginxコンテナですが、挙動がちょっと怪しいですね。

ECSのタスク一覧

ecs-container-idが存在しない

`ecs-container-id`の値は存在しないが`ecs-second-container-id`は存在する

"version","resource_type","account_id","srcaddr","dstaddr","srcport","dstport","protocol","packets","bytes","start","end","action","log_status","vpc_id","subnet_id","instance_id","tcp_flags","type","pkt_srcaddr","pkt_dstaddr","region","az_id","sublocation_type","sublocation_id","pkt_src_aws_service","pkt_dst_aws_service","flow_direction","traffic_path","ecs_cluster_name","ecs_cluster_arn","ecs_container_instance_id","ecs_container_instance_arn","ecs_service_name","ecs_task_definition_arn","ecs_task_id","ecs_task_arn","ecs_container_id","ecs-second-container-id","aws_account_id","aws_region","datehour"
"7","<AWSアカウントID>","eni-0062fcb196a46928b","10.10.0.5","10.10.0.25","54760","80","6","4","413","1716179320","1716179348","ACCEPT","OK","vpc-0152760ee7636a7d9","subnet-02b4eee8c1bfa66f3","-","2","IPv4","10.10.0.5","10.10.0.25","us-east-1","use1-az6","-","-","-","-","egress","1","EcsStack-EcsFargateConstruct2Cluster15146226-YuTtwu0ZQtR5","arn:aws:ecs:us-east-1:<AWSアカウントID>:cluster/EcsStack-EcsFargateConstruct2Cluster15146226-YuTtwu0ZQtR5","-","-","EcsStack-EcsFargateConstruct2ServiceD9DCAB71-SenET0heWI5p","arn:aws:ecs:us-east-1:<AWSアカウントID>:task-definition/EcsStackEcsFargateConstruct2TaskDefinition4E5DC27B:2","25b9b1a127694afdbc9e0def558fab6c","arn:aws:ecs:us-east-1:<AWSアカウントID>:task/EcsStack-EcsFargateConstruct2Cluster15146226-YuTtwu0ZQtR5/25b9b1a127694afdbc9e0def558fab6c","-","25b9b1a127694afdbc9e0def558fab6c-3254704778","<AWSアカウントID>","us-east-1","2024/05/20/04"
"7","<AWSアカウントID>","eni-0062fcb196a46928b","10.10.0.25","10.10.0.5","80","54760","6","3","1020","1716179320","1716179348","ACCEPT","OK","vpc-0152760ee7636a7d9","subnet-02b4eee8c1bfa66f3","-","18","IPv4","10.10.0.25","10.10.0.5","us-east-1","use1-az6","-","-","-","-","ingress",,"EcsStack-EcsFargateConstruct2Cluster15146226-YuTtwu0ZQtR5","arn:aws:ecs:us-east-1:<AWSアカウントID>:cluster/EcsStack-EcsFargateConstruct2Cluster15146226-YuTtwu0ZQtR5","-","-","EcsStack-EcsFargateConstruct2ServiceD9DCAB71-SenET0heWI5p","arn:aws:ecs:us-east-1:<AWSアカウントID>:task-definition/EcsStackEcsFargateConstruct2TaskDefinition4E5DC27B:2","25b9b1a127694afdbc9e0def558fab6c","arn:aws:ecs:us-east-1:<AWSアカウントID>:task/EcsStack-EcsFargateConstruct2Cluster15146226-YuTtwu0ZQtR5/25b9b1a127694afdbc9e0def558fab6c","-","25b9b1a127694afdbc9e0def558fab6c-3254704778","<AWSアカウントID>","us-east-1","2024/05/20/04"
"7","<AWSアカウントID>","eni-0d78b1d149ced2e6a","10.10.0.25","10.10.0.5","80","54760","6","3","1020","1716179309","1716179319","ACCEPT","OK","vpc-0152760ee7636a7d9","subnet-02b4eee8c1bfa66f3","-","18","IPv4","10.10.0.25","10.10.0.5","us-east-1","use1-az6","-","-","-","-","egress","1","EcsStack-EcsFargateConstructCluster1F80E083-Uewmoz48cw3t","arn:aws:ecs:us-east-1:<AWSアカウントID>:cluster/EcsStack-EcsFargateConstructCluster1F80E083-Uewmoz48cw3t","-","-","EcsStack-EcsFargateConstructService485E693B-foIQw9wUCkpI","arn:aws:ecs:us-east-1:<AWSアカウントID>:task-definition/EcsStackEcsFargateConstructTaskDefinition9DD364BE:10","e6d7d4244d6e4bc39566c31a027a6f61","arn:aws:ecs:us-east-1:<AWSアカウントID>:task/EcsStack-EcsFargateConstructCluster1F80E083-Uewmoz48cw3t/e6d7d4244d6e4bc39566c31a027a6f61","e6d7d4244d6e4bc39566c31a027a6f61-3472355196","e6d7d4244d6e4bc39566c31a027a6f61-3254704778","<AWSアカウントID>","us-east-1","2024/05/20/04"
"7","<AWSアカウントID>","eni-0d78b1d149ced2e6a","10.10.0.5","10.10.0.25","54760","80","6","4","413","1716179309","1716179319","ACCEPT","OK","vpc-0152760ee7636a7d9","subnet-02b4eee8c1bfa66f3","-","2","IPv4","10.10.0.5","10.10.0.25","us-east-1","use1-az6","-","-","-","-","ingress",,"EcsStack-EcsFargateConstructCluster1F80E083-Uewmoz48cw3t","arn:aws:ecs:us-east-1:<AWSアカウントID>:cluster/EcsStack-EcsFargateConstructCluster1F80E083-Uewmoz48cw3t","-","-","EcsStack-EcsFargateConstructService485E693B-foIQw9wUCkpI","arn:aws:ecs:us-east-1:<AWSアカウントID>:task-definition/EcsStackEcsFargateConstructTaskDefinition9DD364BE:10","e6d7d4244d6e4bc39566c31a027a6f61","arn:aws:ecs:us-east-1:<AWSアカウントID>:task/EcsStack-EcsFargateConstructCluster1F80E083-Uewmoz48cw3t/e6d7d4244d6e4bc39566c31a027a6f61","e6d7d4244d6e4bc39566c31a027a6f61-3472355196","e6d7d4244d6e4bc39566c31a027a6f61-3254704778","<AWSアカウントID>","us-east-1","2024/05/20/04"

コンテナIDはあくまで参考程度に捉えておくと良さそうです。

次にECS on FargateのECSクラスターからインターネットへの通信のログを確認するクエリです。

ECS on FargateのECSクラスターからインターネットへの通信のログを確認するクエリ

SELECT *
FROM 
  vpc_flow_logs_partition_projection
WHERE
  aws_account_id='<AWSアカウントID>' and
  aws_region='us-east-1' and
  datehour='2024/05/19/21' and
  action='ACCEPT' and
  (dstaddr='8.8.8.8' or srcaddr='8.8.8.8')
LIMIT 10

ECS on FargateのECSクラスターからインターネットへの通信のログを確認するクエリ実行結果

"version","resource_type","account_id","srcaddr","dstaddr","srcport","dstport","protocol","packets","bytes","start","end","action","log_status","vpc_id","subnet_id","instance_id","tcp_flags","type","pkt_srcaddr","pkt_dstaddr","region","az_id","sublocation_type","sublocation_id","pkt_src_aws_service","pkt_dst_aws_service","flow_direction","traffic_path","ecs_cluster_name","ecs_cluster_arn","ecs_container_instance_id","ecs_container_instance_arn","ecs_service_name","ecs_task_definition_arn","ecs_task_id","ecs_task_arn","ecs_container_id","ecs-second-container-id","aws_account_id","aws_region","datehour"
"7","<AWSアカウントID>","eni-0783b0c994717f9a4","10.10.0.17","8.8.8.8","0","0","1","4","336","1716154161","1716154185","ACCEPT","OK","vpc-0926fc3c811765f21","subnet-043f08d86c8ca1516","-","0","IPv4","10.10.0.17","8.8.8.8","us-east-1","use1-az6","-","-","-","-","egress","8","EcsStack-EcsFargateConstruct2Cluster15146226-YluNfbr7sUDT","arn:aws:ecs:us-east-1:<AWSアカウントID>:cluster/EcsStack-EcsFargateConstruct2Cluster15146226-YluNfbr7sUDT","-","-","EcsStack-EcsFargateConstruct2ServiceD9DCAB71-uVoQu1GEBKtI","arn:aws:ecs:us-east-1:<AWSアカウントID>:task-definition/EcsStackEcsFargateConstruct2TaskDefinition4E5DC27B:1","88316020871d4ec7a200c1ce6d25273d","arn:aws:ecs:us-east-1:<AWSアカウントID>:task/EcsStack-EcsFargateConstruct2Cluster15146226-YluNfbr7sUDT/88316020871d4ec7a200c1ce6d25273d","88316020871d4ec7a200c1ce6d25273d-3472355196","-","<AWSアカウントID>","us-east-1","2024/05/19/21"
"7","<AWSアカウントID>","eni-0783b0c994717f9a4","8.8.8.8","10.10.0.17","0","0","1","4","336","1716154161","1716154185","ACCEPT","OK","vpc-0926fc3c811765f21","subnet-043f08d86c8ca1516","-","0","IPv4","8.8.8.8","10.10.0.17","us-east-1","use1-az6","-","-","-","-","ingress",,"EcsStack-EcsFargateConstruct2Cluster15146226-YluNfbr7sUDT","arn:aws:ecs:us-east-1:<AWSアカウントID>:cluster/EcsStack-EcsFargateConstruct2Cluster15146226-YluNfbr7sUDT","-","-","EcsStack-EcsFargateConstruct2ServiceD9DCAB71-uVoQu1GEBKtI","arn:aws:ecs:us-east-1:<AWSアカウントID>:task-definition/EcsStackEcsFargateConstruct2TaskDefinition4E5DC27B:1","88316020871d4ec7a200c1ce6d25273d","arn:aws:ecs:us-east-1:<AWSアカウントID>:task/EcsStack-EcsFargateConstruct2Cluster15146226-YluNfbr7sUDT/88316020871d4ec7a200c1ce6d25273d","88316020871d4ec7a200c1ce6d25273d-3472355196","-","<AWSアカウントID>","us-east-1","2024/05/19/21"

ECS Exec先のBusyBoxコンテナのIDやECSタスクなどが記録されています。レスポンスについてはECS関連の情報は記録されないかと思いましたが、記録されていますね。

ECSサービス間の通信を把握する際に

VPC Flow LogsでECSに関する情報を記録できるようになったアップデートを紹介しました。

ECSサービス間の通信を把握する時に役立ちそうですね。

この記事が誰かの助けになれば幸いです。

以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!