X-Ray入門 – Fargateコンテナの受信リクエストをトレースする
X-Rayを使用することによって、ECS/Fargateのコンテナの動作をトレースすることができます。
今回はX-Rayを使用して、Fargateコンテナの受信リクエストをトレースすることを目指します。
構築する環境
プライベートサブネットに、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を構築する方法については、以下のページをご確認ください。
CloudFormationカスタムリソースを使って、NATゲートウェイ等の一時的なリソースを削除する方法については、以下のページをご確認ください。
タスク定義
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にログを配信する方法については、以下のページをご確認ください。
アプリコンテナ用イメージ
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をインストールするように記載します。
ユーザーデータに関する詳細につきましては、以下のページをご確認ください。
環境構築
CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。
CloudFormationスタックを作成し、スタック内のリソースを確認する
CloudFormationスタックを作成します。
まずECRリポジトリ用CloudFormationスタックを作成し、作成されたECRにDockerイメージをプッシュします。
この手順の詳細は、以下のページをご確認ください。
イメージの準備が完了した後に、残りのスタックを作成します。
ネストされたスタックの作成および各スタックの確認方法については、以下のページをご確認ください。
各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。
- ECSクラスター:fa-074-cluster
- ECSサービス:fa-074-service
- ECRリポジトリ:fa-074
- X-Rayサービス:fa-074
- EC2インスタンス:i-02369a6c48035371e
作成されたリソースをAWS Management Consoleから確認します。
まずECRを確認します。
正常にECRリポジトリが作成され、アプリイメージがプッシュされていることがわかります。
続いてECSクラスター・サービスを確認します。
正常にECSクラスター・サービス・タスクが作成されています。
1つのタスクの中に、2つのコンテナが作成されており、サイドカー構成になっていることがわかります。
ECRからアプリイメージを、DockerhubからX-Rayデーモンイメージを取得し、それらからコンテナが作成されています。
タスクに割り当てられているプライベートアドレスが「10.0.3.250」ということもわかります。
X-Rayデーモンコンテナから配信されたログを確認します。
途中エラーが発生していますが、このコンテナがプロキシサーバとして動作し、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の詳細につきましては、以下のページをご確認ください。
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デーモンコンテナのログを確認します。
1行ログが追記されています。
1セグメント分のトレースデータをX-Rayに配信した旨のログです。
X-Rayのページを確認します。
先ほどの通信によって、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)
改めてデーモンコンテナのログを確認します。
100回受けたリクエストの内、9セグメント分のトレースデータをX-Rayに配信した旨のログです。
X-Rayはサンプリング設定があり、これによってトレースデータが抽出された上で配信されたということです。
デフォルトのサンプリング設定は、以下の仕様となります。
X-Ray SDK はデフォルトで、1 秒ごとに最初のリクエストを記録し、それ以降のリクエストは 5% ずつ記録します。1 秒あたり 1 つのリクエストがリザーバです。これにより、サービスがリクエストを処理している限り、毎秒少なくとも 1 つのトレースが記録されます。5% は、リザーバサイズを超えて追加リクエストがサンプリングされるレートです。
X-Ray コンソールでのサンプリングルールを設定します
もう一度、X-Rayのページを確認します。
サンプリングされたデータから、サービスマップやトレースの統計情報が表示されました。
このように大量のリクエストを受けた場合は、データをサンプリングして処理します。
まとめ
X-Rayを使用して、Fargateコンテナの受信リクエストをトレースする方法を確認しました。