X-Ray入門 – Fargateコンテナの受信リクエストをトレースする

目次

X-Ray入門 – Fargateコンテナの受信リクエストをトレースする

X-Rayを使用することによって、ECS/Fargateのコンテナの動作をトレースすることができます。

今回はX-Rayを使用して、Fargateコンテナの受信リクエストをトレースすることを目指します。

構築する環境

Diagram of introduction to X-Ray - tracing incoming requests for Fargate container

プライベートサブネットに、FargateタイプのECSを作成します。

Fargate上で動作するタスクですが、サイドカー構成で、1つのタスク定義の中に、2つのコンテナを含めます。
1つ目はアプリコンテナです。Python製のWebフレームワークBottleを使用した簡易アプリです。
2つ目はX -Rayデーモンコンテナです。前述のコンテナからアップロードされたトレースデータを、後述のVPCエンドポイントを経由して、X-Rayに配信します。

Fargateがプライベートサブネットを配置されているため、3つの用途のためにVPCエンドポイントを作成します。
ECRおよびS3用VPCエンドポイントは、ECRからイメージをプルするために使用します。
X-RayおよびCloudWatch Logs用のVPCエンドポイントも作成します。

EC2インスタンスを作成します。
コンテナにアクセスするためのクライアントして使用します。

2つの目的で、NATゲートウェイを作成します。
1つ目はDockerHubからX-Rayデーモンイメージを取得するためです。
2つ目はEC2インスタンスにApache Benchをインストールするためです。大量のリクエストを生成し、生成されたトレースデータを確認します。

上記の用途での使用が済めば、NATゲートウェイは不要となります。
今回はCloudFormationカスタムリソースを使用して、最終的にNATゲートウェイ等を削除するように設定します。

CloudFormationテンプレートファイル

上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。

https://github.com/awstut-an-r/awstut-fa/tree/main/074

テンプレートファイルのポイント解説

本ページは、X-Rayを使用して、Fargateコンテナをトレースすることを目的としています。

プライベートサブネットにFargateを構築する方法については、以下のページをご確認ください。

あわせて読みたい
プライベートサブネットにECS(Fargate)を作成する 【プライベートサブネットにECS(Fargate)を作成する】 以下のページでFargateタイプのECSコンテナを作成する方法をご紹介しました。 https://awstut.com/2022/01/25/int...

CloudFormationカスタムリソースを使って、NATゲートウェイ等の一時的なリソースを削除する方法については、以下のページをご確認ください。

あわせて読みたい
初期構築の時だけ使用するNATゲートウェイをCFNカスタムリソースで削除する 【初期構築の時だけ使用するリソース(NATゲートウェイ)をCFNカスタムリソースで削除する】 例えばECSを作成する際に、Dockerhubからイメージをプルする場合、インターネ...

タスク定義

Resources:
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      ContainerDefinitions:
        - Cpu: 480
          Image: !Sub "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${RepositoryName}:latest"
          MemoryReservation: 768
          Name: !Sub "${Prefix}-main-container"
          PortMappings:
            - ContainerPort: 8080
        - Cpu: 32
          Image: amazon/aws-xray-daemon
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Ref LogGroup
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: !Sub "${Prefix}-x-ray-container"
          MemoryReservation: 256
          Name: !Sub "${Prefix}-xray-container"
          PortMappings:
            - ContainerPort: 2000
              Protocol: udp
      Cpu: !Ref TaskCpu
      ExecutionRoleArn: !Ref FargateTaskExecutionRole
      Memory: !Ref TaskMemory
      NetworkMode: awsvpc
      RequiresCompatibilities:
        - FARGATE
      TaskRoleArn: !Ref TaskRole
Code language: YAML (yaml)

X-Rayのトレースデータをアップロードするために、サイドカー構成で、1つのタスク定義の中に2つのコンテナを定義します。

1つ目のコンテナはアプリコンテナです。
詳細は後述しますが、Bottleアプリは8080/tcpでHTTPリクエストを待ち受けます。
ですからPortMappingsプロパティ内の、ContainerPortプロパティに8080を指定します。
これでこのタスクに向けられたtcp/8080のトラフィックは、こちらのコンテナに振り分けられます。

2つ目のコンテナはX-Rayデーモンコンテナです。
AWS公式サイトでは、デーモンコンテナを用意する2つの方法が紹介されています。

https://docs.aws.amazon.com/ja_jp/xray/latest/devguide/xray-daemon-ecs.html

1つ目はAWS公式イメージ(amazon/aws-xray-daemon)を使用する方法です。このイメージをプルするだけで、すぐにデーモンコンテナを使用することができます。
2つ目はデーモンコンテナ用のイメージを自作する方法です。Amazon LinuxやUbuntuイメージをベースとして、オリジナルのデーモンコンテナを作成・使用することができます。
今回は前者、つまり公式イメージを使用します。

アプリコンテナからアップロードされるトレースデータは、デフォルトですと、2000/udp向けに送られます。
デーモンコンテナでこれを受け取る必要がありますので、PortMappingsプロパティはこれに合わせて設定します。

デーモンコンテナの挙動を確認するために、こちらのコンテナはログ設定を行います。
プライベートサブネット内のFargateから、CloudWatch Logsにログを配信する方法については、以下のページをご確認ください。

あわせて読みたい
プライベートサブネットのFargateコンテナのログをCloudWatch Logsに配信する 【プライベートサブネットのECS(Fargate)コンテナのログをCloudWatch Logsに配信する】 ECSタスク内で実行されているコンテナのログを収集する方法の1つに、CloudWatch ...

アプリコンテナ用イメージ

Dockerfile

FROM amazonlinux

RUN yum update -y && yum install python3 python3-pip -y

RUN pip3 install bottle
RUN pip3 install aws-xray-sdk

COPY main.py ./

CMD ["python3", "main.py"]

EXPOSE 8080
Code language: Dockerfile (dockerfile)

アプリコンテナ用のイメージは、Amazon Linux 2をベースとして作成します。

Python製WebフレームワークのBottleを使用します。
ですからPythonおよびpipをインストール後、これをインストールします。
加えてX-Ray SDK for Python(aws-xray-sdk)もインストールします。

アプリロジックを記載したPythonスクリプト(main.py)をコピーし、これを実行するように設定します。

先述の通り、アプリはtcp/8080でHTTPリクエストを待ち受けますので、このポートを公開します。

main.py

from aws_xray_sdk.core import xray_recorder
from aws_xray_sdk.ext.bottle.middleware import XRayMiddleware
from bottle import Bottle, route, run

app = Bottle()

xray_recorder.configure(service='fa-074')
plugins = ('ECSPlugin',)
xray_recorder.configure(plugins=plugins)

app.install(XRayMiddleware(xray_recorder))

@app.route('/')
def hello():
  return 'Hello X-Ray!'


if __name__ == '__main__':
  run(app=app, host='0.0.0.0', port=8080)
Code language: Python (python)

AWS公式ページでは、受信リクエストをトレースする2つの方法が紹介されています。

https://docs.aws.amazon.com/ja_jp/xray/latest/devguide/xray-sdk-python-middleware.html

1つ目はミドルウェアを使用する方法です。
3つのフレームワーク(Django、Flask、Bottle)を使用する場合は、これらにX-Ray SDK for Pythonミドルウェアを組み込むことで、受信リクエストをトレースすることができるようになります。

2つ目は手動でコードを実装する方法です。
上記以外のフレームワークを使用する場合や、任意のタイミングでセグメントを作成する場合は、この方法で受信リクエストをトレースできるようになります。

今回は1つ目の方法、つまりBottleにX-Ray SDK for Pythonミドルウェアを組み込みます。

X-Ray用VPCエンドポイント

Resources:
  XRayEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref EndpointSecurityGroup2
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.xray"
      SubnetIds:
        - !Ref ContainerSubnet
      VpcEndpointType: Interface
      VpcId: !Ref VPC

  EndpointSecurityGroup2:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub "${Prefix}-EndpointSecurityGroup2"
      GroupDescription: Allow HTTPS from ContainerSecurityGroup.
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: !Ref HTTPSPort
          ToPort: !Ref HTTPSPort
          SourceSecurityGroupId: !Ref ContainerSecurityGroup
Code language: YAML (yaml)

プライベートサブネットからVPC外のX-Rayに、データを配信するためのVPCエンドポイントです。

VPCエンドポイントに向かう通信は443/tcpを使用します。
ですからこれを許可するセキュリティグループを作成し、エンドポイントに割り当てます。
送信元にFargate用セキュリティグループを指定します。

(参考)EC2インスタンス

Resources:
  Instance:
    Type: AWS::EC2::Instance
    Properties:
      IamInstanceProfile: !Ref InstanceProfile
      ImageId: !Ref ImageId
      InstanceType: !Ref InstanceType
      NetworkInterfaces:
        - DeviceIndex: 0
          SubnetId: !Ref InstanceSubnet
          GroupSet:
            - !Ref InstanceSecurityGroup
      UserData: !Base64 |
        #!/bin/bash -xe
        yum update -y
        yum install httpd -y
Code language: YAML (yaml)

特別な設定は不要です。
UserDataプロパティで、インスタンスの初期化時にApacheをインストールするように記載します。

ユーザーデータに関する詳細につきましては、以下のページをご確認ください。

あわせて読みたい
Linuxインスタンスの初期化方法4選 【Linuxインスタンスを初期化する4つの方法】 EC2インスタンスの起動時に初期化処理を実行する方法を考えます。 EC2インスタンスを構築時に初期化する以下の4つの手法を...

環境構築

CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。

CloudFormationスタックを作成し、スタック内のリソースを確認する

CloudFormationスタックを作成します。

まずECRリポジトリ用CloudFormationスタックを作成し、作成されたECRにDockerイメージをプッシュします。
この手順の詳細は、以下のページをご確認ください。

あわせて読みたい
CloudFormationでFargate入門 【CloudFormationでFargateに入門するための構成】 AWS FargateはサーバーレスでDockerコンテナを実行することができるサービスです。今回はFargate入門ということで、C...

イメージの準備が完了した後に、残りのスタックを作成します。
ネストされたスタックの作成および各スタックの確認方法については、以下のページをご確認ください。

あわせて読みたい
CloudFormationのネストされたスタックで環境を構築する 【CloudFormationのネストされたスタックで環境を構築する方法】 CloudFormationにおけるネストされたスタックを検証します。 CloudFormationでは、スタックをネストす...

各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。

  • ECSクラスター:fa-074-cluster
  • ECSサービス:fa-074-service
  • ECRリポジトリ:fa-074
  • X-Rayサービス:fa-074
  • EC2インスタンス:i-02369a6c48035371e

作成されたリソースをAWS Management Consoleから確認します。
まずECRを確認します。

ECR Repository.

正常にECRリポジトリが作成され、アプリイメージがプッシュされていることがわかります。

続いてECSクラスター・サービスを確認します。

Detail of Fargate 1.
Detail of Fargate 2.

正常にECSクラスター・サービス・タスクが作成されています。
1つのタスクの中に、2つのコンテナが作成されており、サイドカー構成になっていることがわかります。
ECRからアプリイメージを、DockerhubからX-Rayデーモンイメージを取得し、それらからコンテナが作成されています。
タスクに割り当てられているプライベートアドレスが「10.0.3.250」ということもわかります。

X-Rayデーモンコンテナから配信されたログを確認します。

Detail of Logs from X-Ray daemon container 1.

途中エラーが発生していますが、このコンテナがプロキシサーバとして動作し、2000/udpでトレースデータを待ち受けていることがわかります。
なおこのエラーは、ECSを動作させるEC2インスタンスのIDを取得しようと試みたが、それに失敗したというものです。
これはデータプレーンのマネージドサービスであるFargateを使用していることに起因するものと考えられます。

動作確認

準備が整いましたので、EC2インスタンスにアクセスします。
インスタンスへのアクセスはSSM Session Managerを使用します。

% aws ssm start-session --target i-02369a6c48035371e

Starting session with SessionId: root-084313930da04829b
sh-4.2$
Code language: Bash (bash)

SSM Session Managerの詳細につきましては、以下のページをご確認ください。

あわせて読みたい
LinuxインスタンスにSSM Session Manager経由でアクセスする 【LinuxインスタンスにSSM Session Manager経由でアクセスする】 EC2インスタンスにSSM Session Manager経由でアクセスする構成を確認します。 Session Manager は完全...

curlコマンドを使って、タスク内のコンテナにアクセスします。

sh-4.2$ curl http://10.0.3.250:8080/
Hello X-Ray!
Code language: Bash (bash)

レスポンスが返ってきました。
Fargate上では、1つのタスクの中に2つのコンテナが動作していますが、このタスクの8080/tcp向けに通信すると、アプリコンテナが応答してくれているということです。

改めてX-Rayデーモンコンテナのログを確認します。

Detail of Logs from X-Ray daemon container 2.

1行ログが追記されています。
1セグメント分のトレースデータをX-Rayに配信した旨のログです。

X-Rayのページを確認します。

Detail of X-Ray 1.

先ほどの通信によって、X-Rayにデータが配信されていることがわかります。
X-Rayページでは、サービスマップが生成され、レイテンシやリクエスト数、エラー数の統計が表示されます。

続いて大量のリクエストを受けた時の挙動を確認します。
大量のリクエストを生成するために、Apache Benchを使用します。

まずApacheがインストールされている確認します。

sh-4.2$ yum list installed | grep httpd
generic-logos-httpd.noarch 18.0.0-4.amzn2 @amzn2-core
httpd.aarch64 2.4.54-1.amzn2 @amzn2-core
httpd-filesystem.noarch 2.4.54-1.amzn2 @amzn2-core
httpd-tools.aarch64 2.4.54-1.amzn2 @amzn2-core
Code language: Bash (bash)

ユーザーデータによって、正常にApacheがインストールされたようです。

Apache Benchを実行します。
Fargate上のコンテナ(タスク)に対して、100回のリクエストを生成します。

sh-4.2$ ab -n 100 http://10.0.3.250:8080/
This is ApacheBench, Version 2.3 <$Revision: 1901567 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 10.0.3.250 (be patient).....done


Server Software:        WSGIServer/0.2
Server Hostname:        10.0.3.250
Server Port:            8080

Document Path:          /
Document Length:        12 bytes

Concurrency Level:      1
Time taken for tests:   0.080 seconds
Complete requests:      100
Failed requests:        0
Total transferred:      22600 bytes
HTML transferred:       1200 bytes
Requests per second:    1248.72 [#/sec] (mean)
Time per request:       0.801 [ms] (mean)
Time per request:       0.801 [ms] (mean, across all concurrent requests)
Transfer rate:          275.60 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       0
Processing:     0    1   0.1      1       1
Waiting:        0    0   0.1      1       1
Total:          1    1   0.2      1       2
ERROR: The median and mean for the waiting time are more than twice the standard
       deviation apart. These results are NOT reliable.

Percentage of the requests served within a certain time (ms)
  50%      1
  66%      1
  75%      1
  80%      1
  90%      1
  95%      1
  98%      1
  99%      2
 100%      2 (longest request)
Code language: Bash (bash)

改めてデーモンコンテナのログを確認します。

Detail of Logs from X-Ray daemon container 3.

100回受けたリクエストの内、9セグメント分のトレースデータをX-Rayに配信した旨のログです。
X-Rayはサンプリング設定があり、これによってトレースデータが抽出された上で配信されたということです。
デフォルトのサンプリング設定は、以下の仕様となります。

X-Ray SDK はデフォルトで、1 秒ごとに最初のリクエストを記録し、それ以降のリクエストは 5% ずつ記録します。1 秒あたり 1 つのリクエストがリザーバです。これにより、サービスがリクエストを処理している限り、毎秒少なくとも 1 つのトレースが記録されます。5% は、リザーバサイズを超えて追加リクエストがサンプリングされるレートです。

X-Ray コンソールでのサンプリングルールを設定します

もう一度、X-Rayのページを確認します。

Detail of X-Ray 2.
Detail of X-Ray 3.

サンプリングされたデータから、サービスマップやトレースの統計情報が表示されました。
このように大量のリクエストを受けた場合は、データをサンプリングして処理します。

まとめ

X-Rayを使用して、Fargateコンテナの受信リクエストをトレースする方法を確認しました。

目次