3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

X-RayでAWS SDK呼び出しをトレースしてAWS Fargate とEC2 起動タイプのレイテンシを比較する

Last updated at Posted at 2018-11-12

投稿内容は私個人の意見であり、所属企業・部門見解を代表するものではありません。

目的

AWS Fargate とEC2 起動タイプでAWS SDK呼び出しに差があるのか計測したかったため、X-Ray SDK for Node.jsでレイテンシを比較したときのメモです。

流れ

  1. 事前準備
  2. 計測用のサンプルプロジェクトの準備
  3. ローカル環境でトレース実行
  4. Dockerコンテナ化してローカル環境でトレース実行
  5. EC2 起動タイプでトレース実行
  6. AWS Fargate でトレース実行
  7. 比較

事前準備

X-Rayのサンプリングレートを100%にする

商用環境だとすべてのSDK呼び出しを取得することはパフォーマンスが劣化する恐れがあるため推奨されないが、今回はテストなのでトレースを100%取得するように設定する。デフォルトは5%となる。
https://docs.aws.amazon.com/ja_jp/xray/latest/devguide/xray-gettingstarted.html

ECRでレポジトリを作成

Node.jsのサンプルアプリケーションとX-Rayデーモン用の2つのレポジトリを作成する。ここでは「my-nodejs」と「xray」という名称でレポジトリを作成した。

ECSクラスタ作成

AWS FargateとEC2タイプのクラスタをそれぞれ作成する。
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/create_cluster.html

EC2タイプは、セキュリティグループのインバウンドルールでサンプルアプリケーションが使用する3000ポートへのアクセスを許可する。
EC2タイプは、クラスタのEC2にパブリックIPが付与される。
ネットワークモードがawsvpcの場合は、EC2タイプ/Fargateを問わずサービスの定義でパブリックIPの付与を有効にすることができる。

計測用のサンプルプロジェクトの準備

Express、express-generatorをインストール

npm install -g express
npm install -g express-generator

X-rayは、Express フレームワークおよび Restify フレームワークをサポートしている。
https://docs.aws.amazon.com/ja_jp/xray/latest/devguide/xray-sdk-nodejs-middleware.html

X-Ray SDK for Node.js は Express フレームワークおよび Restify フレームワークを使用するアプリケーションのミドルウェアを提供します

Expressプロジェクトを作成

express sampleapp

依存モジュールのインストール

cd sampleapp && npm install
npm install aws-sdk
npm install aws-xray-sdk

app.js修正

今回はSystemManagerのパラメータストアの値を取得するため、aws-sdkとaws-xray-sdkをインポートし、パラメータストアストア取得用のGetメソッドを宣言します。

var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');

//追加
var ssm_get = require('./routes/get');

var app = express();

//追加
var AWSXRay = require('aws-xray-sdk');
app.use(AWSXRay.express.openSegment('MyApp'));

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/users', usersRouter);

//追加
app.use('/get', ssm_get);
app.use(AWSXRay.express.closeSegment());

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

./routes/get.jsを作成

local実行するため、アクセスキー、シークレットキーで認証していますが、EC2やECSで動作させる場合は、IAMロールからクレデンシャルを取得するほうがセキュリティレベルが高くなる。

var express = require('express');
var router = express.Router();

var AWSXRay = require('aws-xray-sdk');
var AWS = AWSXRay.captureAWS(require('aws-sdk'));
AWS.config.loadFromPath('./credentials.json');
AWS.config.update({region: 'ap-northeast-1'});
var ssm = AWSXRay.captureAWSClient(new AWS.SSM());

router.get('/', function(req, res) {
	var params = {
	    Name: '<パラメータストアのキー>', /* required */
	    WithDecryption: true || false
	};
	ssm.getParameter(params, function(err, data) {
		if (err) {
		    console.log(err, err.stack);
		}else{
		    console.log(data);
		    res.render('ssm_get', { message: "OK" });
		}
	    });
    });

module.exports = router;

クレデンシャルを定義するファイル作成する(credentials.json)

{"accessKeyId": "<アクセスキー>", "secretAccessKey": "<シークレットアクセスキー>"}

Jade作成(./views/ssm_get.jade) ※エラーが出力されるので適当に作成。。

extends layout

block content
  h1= title
  p #{title}

ここまででテスト対象となるアプリケーションが完成。

ローカル環境でトレース実行

ここではMacOSでの手順を記載します
https://docs.aws.amazon.com/ja_jp/xray/latest/devguide/xray-daemon-local.html

X-Rayデーモンのダウンロードして解凍する。

cp ../../Downloads/aws-xray-daemon-macos-2.x.zip .

unzip aws-xray-daemon-macos-2.x.zip 
Archive:  aws-xray-daemon-macos-2.x.zip
  inflating: xray_mac                
  inflating: cfg.yaml   

X-Rayデーモンの起動

./xray_mac -o -n ap-northeast-1 &
[1] 74741
8c8590130da5:sampleapp atsum$ 2018-11-12T16:06:02+09:00 [Info] Initializing AWS X-Ray daemon 2.1.3
2018-11-12T16:06:02+09:00 [Info] Using buffer memory limit of 81 MB
2018-11-12T16:06:02+09:00 [Info] 1296 segment buffers allocated
2018-11-12T16:06:02+09:00 [Info] Using region: ap-northeast-1

-o: ローカル実行
-n: リージョン指定

サンプルアプリケーションの実行

node bin/www

getしてみる

curl localhost:3000/get

X-Rayのコンソールでトレースを確認する

image.png

Dockerコンテナ化してローカル環境でトレース実行

./routes/get.jsの編集


//コメントアウト
//AWS.config.loadFromPath('./credentials.json');                                              
//追加
AWS.config.loadFromPath('/src/credentials.json');

Dockerfileを作成

先程作成したサンプルアプリケーションをDockerコンテナ上にコピーするようにDockerファイルを作成します。

FROM node

RUN cd /src; npm install express;npm install aws-sdk;npm install aws-xray-sdk

COPY . /src

EXPOSE  3000

CMD ["node", "/src/bin/www", "&"]

ビルド実行

docker build -t my-nodejs .

コンテナ起動

docker run  -it my-nodejs:latest /bin/bash

getしてみる

node /src/bin/www &
curl localhost:49160/get

EC2 起動タイプでトレース実行

X-RayデーモンをECSで実行するためにDockerfileを作成する

FROM amazonlinux
RUN yum install -y unzip
RUN curl -o daemon.zip https://s3.dualstack.us-east-2.amazonaws.com/aws-xray-assets.us-east-2/xray-daemon/aws-xray-daemon-linux-2.x.zip
RUN unzip daemon.zip && cp xray /usr/bin/xray
ENTRYPOINT ["/usr/bin/xray", "-b", "0.0.0.0:2000"]
EXPOSE 2000/udp

X-RayデーモンのコンテナイメージをBuildする

docker build -t xray .

BuildしたDockerimageをECRにPush(サンプルアプリケーション)

$(aws ecr get-login --no-include-email --region ap-northeast-1)
docker tag my-nodejs:latest <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/my-nodejs:latest
docker push <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/my-nodejs:latest

BuildしたDockerimageをECRにPush(X-Rayデーモン)

$(aws ecr get-login --no-include-email --region ap-northeast-1)
docker tag xray:latest <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/xray:latest
docker push <AWSアカウントID>.dkr.ecr.ap-northeast-1.amazonaws.com/xray:latest

タスク定義作成

ネットワークモード:host #簡単に検証するためにhostモードを使用する
タスク実行ロール:SystemMangerのパラメータストアの値が取得できる権限が必要
コンテナの定義:サンプルアプリケーションとX-Rayデーモン用のコンテナを2つ登録する

タスク定義の例

{
  "executionRoleArn": "arn:aws:iam::<AWSアカウント>:role/ecsTaskExecutionRole",
  "containerDefinitions": [
    {
      "dnsSearchDomains": null,
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/p-test-node-ec2",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "ecs"
        }
      },
      "entryPoint": null,
      "portMappings": [],
      "command": null,
      "linuxParameters": null,
      "cpu": 0,
      "environment": [],
      "ulimits": null,
      "dnsServers": null,
      "mountPoints": [],
      "workingDirectory": null,
      "dockerSecurityOptions": null,
      "memory": null,
      "memoryReservation": null,
      "volumesFrom": [],
      "image": "<AWSアカウント>.dkr.ecr.ap-northeast-1.amazonaws.com/my-nodejs",
      "disableNetworking": null,
      "interactive": null,
      "healthCheck": null,
      "essential": true,
      "links": null,
      "hostname": null,
      "extraHosts": null,
      "pseudoTerminal": null,
      "user": null,
      "readonlyRootFilesystem": null,
      "dockerLabels": null,
      "systemControls": null,
      "privileged": null,
      "name": "my-amazonlinux"
    },
    {
      "dnsSearchDomains": null,
      "logConfiguration": null,
      "entryPoint": null,
      "portMappings": [],
      "command": null,
      "linuxParameters": null,
      "cpu": 0,
      "environment": [],
      "ulimits": null,
      "dnsServers": null,
      "mountPoints": [],
      "workingDirectory": null,
      "dockerSecurityOptions": null,
      "memory": null,
      "memoryReservation": null,
      "volumesFrom": [],
      "image": "<AWSアカウント>.dkr.ecr.ap-northeast-1.amazonaws.com/xray",
      "disableNetworking": null,
      "interactive": null,
      "healthCheck": null,
      "essential": true,
      "links": null,
      "hostname": null,
      "extraHosts": null,
      "pseudoTerminal": null,
      "user": null,
      "readonlyRootFilesystem": null,
      "dockerLabels": null,
      "systemControls": null,
      "privileged": null,
      "name": "xray-deamon"
    }
  ],
  "placementConstraints": [],
  "memory": "2048",
  "taskRoleArn": "arn:aws:iam::<AWSアカウント>:role/ecsTaskExecutionRole",
  "compatibilities": [
    "EC2"
  ],
  "taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:<AWSアカウント>:task-definition/p-test-node-ec2:8",
  "family": "p-test-node-ec2",
  "requiresAttributes": [
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "ecs.capability.execution-role-ecr-pull"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.ecr-auth"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.task-iam-role"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "ecs.capability.execution-role-awslogs"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.logging-driver.awslogs"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.task-iam-role-network-host"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19"
    }
  ],
  "requiresCompatibilities": [
    "EC2"
  ],
  "networkMode": "host",
  "cpu": "1024",
  "revision": 8,
  "status": "ACTIVE",
  "volumes": []
}

ECSサービス作成

EC2タイプのECSクラスタでサービスを定義する。
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/create-service.html
先程作成したタスク定義を指定する。
ELBオプション、サービスの検出なし。
Service Auto Scaling無効化
タスク(コンテナ)がRUNNINGになるまで待つ

getしてみる

curl <EC2のパブリックIPアドレス>:3000/get

EC2のパブリックIPアドレスは、クラスタのEC2インスタンスタブにある対象のEC2インスタンスIDをクリックしてEC2のコンソールで確認する。
image.png

X-Rayのコンソールでトレースを確認する

AWS Fargate でトレース実行

タスク定義作成

ネットワークモード:awsvpc
タスク実行ロール:SystemMangerのパラメータストアの値が取得できる権限が必要
コンテナの定義:サンプルアプリケーションとX-Rayデーモン用のコンテナを2つ登録する

テスク定義の例


{
  "executionRoleArn": "arn:aws:iam::<AWSアカウント>:role/ecsTaskExecutionRole",
  "containerDefinitions": [
    {
      "dnsSearchDomains": null,
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/p-test-node-fargate",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "ecs"
        }
      },
      "entryPoint": null,
      "portMappings": [],
      "command": null,
      "linuxParameters": null,
      "cpu": 0,
      "environment": [],
      "ulimits": null,
      "dnsServers": null,
      "mountPoints": [],
      "workingDirectory": null,
      "dockerSecurityOptions": null,
      "memory": null,
      "memoryReservation": null,
      "volumesFrom": [],
      "image": "<AWSアカウント>.dkr.ecr.ap-northeast-1.amazonaws.com/my-nodejs",
      "disableNetworking": null,
      "interactive": null,
      "healthCheck": null,
      "essential": true,
      "links": null,
      "hostname": null,
      "extraHosts": null,
      "pseudoTerminal": null,
      "user": null,
      "readonlyRootFilesystem": null,
      "dockerLabels": null,
      "systemControls": null,
      "privileged": null,
      "name": "my-amazonlinux"
    },
    {
      "dnsSearchDomains": null,
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/p-test-node-fargate",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "ecs"
        }
      },
      "entryPoint": null,
      "portMappings": [],
      "command": null,
      "linuxParameters": null,
      "cpu": 0,
      "environment": [],
      "ulimits": null,
      "dnsServers": null,
      "mountPoints": [],
      "workingDirectory": null,
      "dockerSecurityOptions": null,
      "memory": null,
      "memoryReservation": null,
      "volumesFrom": [],
      "image": "<AWSアカウント>.dkr.ecr.ap-northeast-1.amazonaws.com/xray",
      "disableNetworking": null,
      "interactive": null,
      "healthCheck": null,
      "essential": true,
      "links": null,
      "hostname": null,
      "extraHosts": null,
      "pseudoTerminal": null,
      "user": null,
      "readonlyRootFilesystem": null,
      "dockerLabels": null,
      "systemControls": null,
      "privileged": null,
      "name": "xray-daemon"
    }
  ],
  "placementConstraints": [],
  "memory": "2048",
  "taskRoleArn": "arn:aws:iam::<AWSアカウント>:role/ecsTaskExecutionRole",
  "compatibilities": [
    "EC2",
    "FARGATE"
  ],
  "taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:<AWSアカウント>:task-definition/p-test-node-fargate:5",
  "family": "p-test-node-fargate",
  "requiresAttributes": [
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "ecs.capability.execution-role-ecr-pull"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "ecs.capability.task-eni"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.ecr-auth"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.task-iam-role"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "ecs.capability.execution-role-awslogs"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.logging-driver.awslogs"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19"
    }
  ],
  "requiresCompatibilities": [
    "FARGATE"
  ],
  "networkMode": "awsvpc",
  "cpu": "1024",
  "revision": 5,
  "status": "ACTIVE",
  "volumes": []
}

ECSサービス作成

FargateのECSクラスタでサービスを定義する。
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/create-service.html
先程作成したタスク定義を指定する。
セキュリティグループ:セキュリティグループのインバウンドルールでサンプルアプリケーションが使用する3000ポートへのアクセスを許可する
パブリック IP の自動割り当て:有効
ELBオプション、サービスの検出なし。
Service Auto Scaling無効化
タスク(コンテナ)がRUNNINGになるまで待つ

getしてみる

curl <fargateのコンテナのパブリックIPアドレス>:3000/get

fargateのコンテナのパブリックIPアドレスは、クラスタのタスクタブのタスクをクリックして、Public IPを確認する。
image.png

比較

前提

EC2タイプ
インスタンスタイプ:m4.large
タスクサイズ:
タスクメモリ (MiB)2048
タスク CPU (単位):1024

Fargate
タスクサイズ:
タスクメモリ (MiB)2048
タスク CPU (単位)1024

(雑な)比較結果

image.png
_人人人人人人人人_
> そんなに差はなし <
 ̄Y^Y^Y^Y^Y^Y^Y ̄

参考

https://docs.aws.amazon.com/ja_jp/xray/latest/devguide/xray-sdk-nodejs-middleware.html
https://docs.aws.amazon.com/ja_jp/xray/latest/devguide/xray-sdk-nodejs-awssdkclients.html
https://inokara.hateblo.jp/entry/2017/05/07/195717

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?