中間 DLT の動作まとめ

  • ECS タスクを大量に起動
  • 同じ Docker イメージからコンテナ稼働
  • 各コンテナは Controller からのソケットでメッセージが来るのを待つ
  • メッセージが来たら一斉に各コンテナで Taurus が実行される

What’s DLT

https://aws.amazon.com/solutions/implementations/distributed-load-testing-on-aws/

  • Docker イメージを ECS 上で同時起動させて対象に負荷を与えるソリューション
  • Web サイトに対しては効果的なものの、ゲームにはいまひとつか

今回は、使用されている Docker Image を分析することで他のツールとの連携や負荷試験を Docker コンテナでやる場合の手順の理解を深める

Docker Image 分析

docker image のリポジトリとタグは以下

  • public.ecr.aws/aws-solutions/distributed-load-testing-on-aws-load-tester:v3.1.1

分析手順としては以下二つ?

  • Docker Save
  • Docker Run
finch pull public.ecr.aws/aws-solutions/distributed-load-testing-on-aws-load-tester:v3.1.1

finch images
REPOSITORY                                                                  TAG           IMAGE ID        CREATED          PLATFORM       SIZE         BLOB SIZE
public.ecr.aws/aws-solutions/distributed-load-testing-on-aws-load-tester    v3.1.1        c74becc0f661    2 minutes ago    linux/amd64    3.6 GiB      1.5 GiB

イメージの DL と展開

finch save public.ecr.aws/aws-solutions/distributed-load-testing-on-aws-load-tester:v3.1.1 -o dlt.tar

イメージの実行

finch run --name dlt --rm public.ecr.aws/aws-solutions/distributed-load-testing-on-aws-load-tester:v3.1.1

そのまま実行するとスクリプトが起動してしまうので、エントリーポイントを変更する

finch run -it --rm --entrypoint "bash" public.ecr.aws/aws-solutions/distributed-load-testing-on-aws-load-tester:v3.1.1

イメージの中身調査

ls -l
total 1300
-rwxr-xr-x. 1 root root   1270 Nov 18 22:12 ecscontroller.py
-rwxr-xr-x. 1 root root   2079 Nov 18 22:12 ecslistener.py
-rw-r--r--. 1 root root  16542 Nov 18 22:19 jetty-alpn-client-9.4.34.v20201102.jar
-rw-r--r--. 1 root root  19600 Nov 18 22:19 jetty-alpn-openjdk8-client-9.4.34.v20201102.jar
-rw-r--r--. 1 root root 320564 Nov 18 22:19 jetty-client-9.4.34.v20201102.jar
-rw-r--r--. 1 root root 214251 Nov 18 22:19 jetty-http-9.4.34.v20201102.jar
-rw-r--r--. 1 root root 164646 Nov 18 22:19 jetty-io-9.4.34.v20201102.jar
-rw-r--r--. 1 root root 565135 Nov 18 22:19 jetty-util-9.4.34.v20201102.jar
-rwxr-xr-x. 1 root root   4633 Nov 18 22:12 load-test.sh

jar ファイル

jetty は Java のライブラリ JMeter は Java 上で動作するので、JMeter を使用する場合にこれ等のライブラリが必要になる ALPN (Application Layer Protocol Negotiation)は TLS 拡張の一つ

load-test.sh

  • 環境変数出力
  • SIGTERM のトラップ
  • S3バケットから シナリオ.json を DL

分析

stdbuf -i0 -o0 -e0 bzt test.json -o modules.console.disable=true | stdbuf -i0 -o0 -e0 tee -a result.tmp | sed -u -e "s|^|$TEST_ID $LIVE_DATA_ENABLED |"
  • stdbuf : 標準入出力ストリームのバッファ動作を変更して、COMMAND を実行
    • 今回はいずれも 0 を指定しているので、バッファリングしなくなる
    • バッファリング:内部のストリームバッファに書き込み後、一定の時期で一気に表示
      • 改行やバッファ溢れ等
    • プログレスバーとかやりたいなら、オフにする
  • modules.console.disable=true : コンソール表示オフにする

Taurus JSON Syntax

https://gettaurus.org/docs/ConfigSyntax/

  • execution : 負荷規模とインターバルと参照シナリオ指定
  • scenario
    • HTTP リクエストの内容記述
  • reporting
  • modules: 追加モジュール

Nunit を Taurus で実行

一旦先に、 Taurus で Nunit やるために Nunit 検討する

wget https://dot.net/v1/dotnet-install.sh
chmod 744 dotnet-install.sh
./dotnet-install.sh --channel LTS

Taurus Nunit Executor

https://qiita.com/karuakun/items/e254bcbb7dcc61a4ca5d

mkdir LoadTestSample 
cd LoadTestSample

mkdir LoadTestSample.WebApp
mkdir LoadTestSample.WebApp.Tests


dotnet new web -o LoadTestSample.WebApp
dotnet new nunit -o LoadTestSample.WebApp.Tests -f netcoreapp3.1

## sln 作成しプロジェクトを追加
dotnet new sln
dotnet sln add LoadTestSample.WebApp/LoadTestSample.WebApp.csproj
dotnet sln add LoadTestSample.WebApp.Tests/LoadTestSample.WebApp.Tests.csproj

サンプルアプリ実行

cd LoadTestSample.WebApp
dotnet run

テストファイル作成

cd LoadTestSample.WebApp.Tests/
bat UnitTest1.cs
wget https://ftp.gnu.org/gnu/ncurses/ncurses-6.4.tar.gz
tar -xvf ncurses-6.4.tar.gz
cd ncurses-6.4
./configure
make
make install
wget http://ftp.vim.org/pub/vim/unix/vim-7.4.tar.bz2
tar -xvf vim-7.4.tar.bz2
cd vim74
./configure
make
make install

テストファイル編集

using NUnit.Framework;
using System.Threading.Tasks;
using System.Net;
using System.Net.Http;

namespace LoadTestSample.WebApp.Tests
{
    public class Test1
    {
        private HttpClient _httpClient;
        [SetUp]
        public void Setup()
        {
            _httpClient = new HttpClient();
        }

        [Test]
        public async Task Test1()
        {
            var response = await _httpClient.GetAsync("https://shuaki.com");
            Assert.AreEqual(response.StatusCode, HttpStatusCode.OK);
        }
    }
}

テスト実行

dotnet test
Test run for /bzt-configs/LoadTestSample/LoadTestSample.WebApp.Tests/bin/Debug/netcoreapp3.1/LoadTestSample.WebApp.Tests.dll(.NETCoreApp,Version=v3.1)
Microsoft (R) Test Execution Command Line Tool Version 16.7.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...

A total of 1 test files matched the specified pattern.

Test Run Successful.
Total tests: 1
     Passed: 1
 Total time: 2.2304 Seconds

Taurus でテスト実行

pwd
/bzt-configs/LoadTestSample/LoadTestSample.WebApp.Tests/bin/Debug/netcoreapp3.1

ls -la | grep "LoadTestSample.WebApp.Tests.dll"
-rw-r--r--. 1 root root   7168 Jan  8 09:44 LoadTestSample.WebApp.Tests.dll

chmod 777 /bzt-configs/LoadTestSample/LoadTestSample.WebApp.Tests/bin/Debug/netcoreapp3.1/LoadTestSample.WebApp.Tests.dll

touch nunittest.yaml
execution:
- executor: nunit
  concurrency: 5
  iterations: 5
  scenario:
    script: /bzt-configs/LoadTestSample/LoadTestSample.WebApp.Tests/bin/Debug/netcoreapp3.1/LoadTestSample.WebApp.Tests.dll
bzt ./nunittest.yaml -o modules.console.disable=true
09:53:28 INFO: Taurus CLI Tool v1.16.11
09:53:28 INFO: Starting with configs: ['/root/.bzt-rc', './nunittest.yaml']
09:53:28 INFO: Configuring...
09:53:29 INFO: Artifacts dir: /tmp/artifacts
09:53:29 INFO: Preparing...
OpenBLAS WARNING - could not determine the L2 cache size on this system, assuming 256k
09:53:29 INFO: Starting...
09:53:29 INFO: Waiting for results...
09:53:29 WARNING: Failed to check for updates
09:53:54 INFO: Changed data analysis delay to 44s
09:53:54 INFO: Current: 1 vu	1 succ	0 fail	12.073 avg rt	/	Cumulative: 17.298 avg rt, 0% failures
09:53:55 WARNING: Please wait for graceful shutdown...
09:53:55 INFO: Shutting down...
09:53:56 INFO: Post-processing...
09:53:56 INFO: Test duration: 0:00:27
09:53:56 INFO: Samples count: 25, 0.00% failures
09:53:56 INFO: Average times: total 3.166, latency 0.000, connect 0.000
09:53:56 INFO: Percentiles:
+---------------+---------------+
| Percentile, % | Resp. Time, s |
+---------------+---------------+
|           0.0 |         0.082 |
|          50.0 |         0.138 |
|          90.0 |        16.088 |
|          95.0 |        19.072 |
|          99.0 |        21.936 |
|          99.9 |        21.936 |
|         100.0 |        21.936 |
+---------------+---------------+
09:53:56 INFO: Request label stats:
+-------+--------+---------+--------+-------+
| label | status |    succ | avg_rt | error |
+-------+--------+---------+--------+-------+
| Test1 |   OK   | 100.00% |  3.166 |       |
+-------+--------+---------+--------+-------+
09:53:56 INFO: Artifacts dir: /tmp/artifacts
09:53:56 INFO: Done performing with code: 0

無事実行完了 あとは Python スクリプトを見れば OK ?

Python Script を調査

ls -la| grep ".py"
-rwxr-xr-x. 1 root root     1270 Nov 18 22:12 ecscontroller.py
-rwxr-xr-x. 1 root root     2079 Nov 18 22:12 ecslistener.py
bat ecscontroller.py

あーなるほど。load-test.sh と組み合わせると見えてくる

  • controller : 全プロセスにソケットで開始を通知
  • listener : 通知を受け取ったらプログラム終了して開始
#Download python script
    python3 -u $SCRIPT $TIMEOUT &
    python3 -u $SCRIPT $IPNETWORK $IPHOSTS
root@7bcefc257b82:/bzt-configs# bat load-test.sh | grep "python" -A 10
#Download python script

if [ -z "$IPNETWORK" ]; then
	# Listener
    python3 -u $SCRIPT $TIMEOUT &
    pypid=$!
    wait $pypid
    pypid=0
else 
	# Controller
    python3 -u $SCRIPT $IPNETWORK $IPHOSTS
fi

多分、タスク起動時に Controller/Listener を分ける何かがある $IPNETWORK ? かもしれない 大したことはやってない。

中間 DLT の動作まとめ

  • ECS タスクを大量に起動
  • 同じ Docker イメージからコンテナ稼働
  • 各コンテナは Controller からのソケットでメッセージが来るのを待つ
  • メッセージが来たら一斉に各コンテナで Taurus が実行される

目的の指定

  • .NET NUnit でスクリプトを作成
  • スクリプトをビルドし、出来上がった DLL を S3(?)に配置
  • Container 上でスクリプトをDL
  • Taurus でスクリプトを実行

減らしたい作業

  • NET NUnit でスクリプトを作成
    • → FlexMatch 向けにスクリプトも作成しちゃう?
  • ビルドと S3 配置 :
    • CodeBuild でできそう
    • ついでにコンテナも実行しちゃうとか
  • Container 上でスクリプトをDL

依存関係が辛いのでスクリプト固定した方が楽ではあるが、そうすると汎用性が失われる。

手順を全部示してブログ化するとか? CodeCommit → CodeBuild → ECS Run とか?

いやもう新規に Solution 開発しちゃうのもあり

あーDocker Images として提供する? で、オプションで色々渡すという感じで

  • Docker Images A : Bastion / Controller
  • Docker images B : Taurus Executor

やっぱり無理があるから Amplify 使うしかないか

Amplify → S3 → ECS 起動 他リソース

  • ECR
  • CloudFront
  • Cognito

DLT の場合、カスタムリソースでフロントエンドアプリケーションを多分連結している

  DLTCustomResourcesCopyConsoleFiles2EBD447E:
    Type: AWS::CloudFormation::CustomResource
    Properties:
      ServiceToken:
        Fn::GetAtt:
          - DLTCustomResourceInfraCustomResourceLambdaA4053269
          - Arn
      DestBucket:
        Ref: DLTConsoleResourcesDLTCloudFrontToS3S3Bucket4FED8B63

やっぱツールか? NunitLoadTools option で別 ECR リポジトリ選択可能にすれば良いのでは?

CLI 作成

タスク

  • CLI を作成
    • run
    • upload
    • init
  • CLI で管理される CFn を作成
    • CFn
    • S3
  • Docker Image 作成 + ECR に登録

そんなもん?