🦈

【JAWS DAYSハンズオン】Serverlessのパフォーマンスチューニング やってみた

2024/03/31に公開

はじめに

JAWS-UGによるイベントJAWS DAYS 2024[1]に参加してきました。
そこでハンズオンイベントのServerlessのパフォーマンスチューニングを試してみました。

題材

今回 seike460さんのリポジトリでハンズオンを行いました。

目的

Serverlessのパフォーマンスチューニングを学ぶため。

Serverlessの長所はスケーラビリティが高いことです。
サーバーレスをただただ使えばスケーラビリティが提供できるわけではありません。
今回の題材では以下のような、ボトルネックがあるシステムを使用します。

  • 仕様上どうしても重い処理が入ってしまう
  • 非常に多くのデータ量
  • コントロールできないアクセス

上記の問題を改善して、改善の前後で何が変化するのか、解説していきたいと思います。

なお今回行うハンズオンは高速化の入口のような内容になります。

前置き

X-Rayについて

X-Ray[2]はアプリケーションレイヤを対象としてレスポンスタイムやレスポンスステータスなどの情報を収集します。
特にマイクロサービスのように多くのサービスが呼び出し合う際に、アプリケーションのパフォーマンス問題やそのボトルネックについて特定するのに役立ちます。
また、CloudWatchを使用することでインフラレイヤでのパフォーマンスを確認できます。

X-RayとCloud Watchの比較表

AWSのX-RayとCloudWatchは、アプリケーションのパフォーマンスとエラーの診断に役立つツールですが、それぞれ異なる機能と利点を持っています。以下は、それらの主な違いを比較した表です。

サービス名 AWS X-Ray Amazon CloudWatch
主な目的 アプリケーションのトレースとデバッグを支援し、パフォーマンスのボトルネックを特定する メトリクス、ログ、アラームを提供して、アプリケーションの監視とアラートを設定する
使用レイヤ アプリケーションレイヤ インフラレイヤ
無料利用枠 提供しない 基本的なモニタリングメトリクス、カスタムメトリクス、ログ、アラームなどが無料利用枠に含まれる

今回はX-Rayでログを取集します。
CloudWatchは以下の理由で状況を追いづらいため、使用しません。

  • 今回はサーバーレス構成であり、複数のAWSサービスがまたがっていて、それらのサービスは疎結合になっている。
  • CloudWatchでは、異なるサービスからのログやメトリクスを一箇所で収集する。
  • しかし、アプリケーション全体のフローを追いかけるには、複数のサービス間で関連するイベントを結びつける必要があり状況を追いづらい
  • そのためシステムの端から端まで理解するのは難しい

したがって、X-Rayを使用します。

SAMについて

正式名称はAWS Serverless Application Modelです。
機能を一言でいうと、CloudFormationの拡張機能でサーバーレスアプリケーション[3]を構築できる便利な機能です。
専用のCLIが用意されており、基本的にはこのCLIを使い、SAMを利用します。

メリット

CloudFormationに比べ、API GatewayやLambdaといったサーバレスなサービスの設定を容易に定義可能
ローカルでの構築、テスト、およびデバッグが可能です。

DynamoDBについて

気になる方は以下の記事を見てみて下さい。
DynamoDBの基本についてまとめてみた【初心者向け】

本題

環境構築

環境構築を行います。
SAMを使用するため、簡単に構築できます。

初期環境構築

初回リクエスト実行

負荷試験ツール artilleryでテストを行います
負荷テストツールArtilleryについて

何もチューニングしないでリクエストを実行した結果は以下の通りでした。500エラーが発生しているのがわかると思います。

http.codes.200: ................................................................ 258
http.codes.502: ................................................................ 207
http.downloaded_bytes: ......................................................... 7968
http.request_rate: ............................................................. 44/sec
http.requests: ................................................................. 440
http.response_time:
  min: ......................................................................... 24
  max: ......................................................................... 2352
  mean: ........................................................................ 1009.7
  median: ...................................................................... 1556.5
  p95: ......................................................................... 2186.8
  p99: ......................................................................... 2276.1
http.responses: ................................................................ 465

■ 読み方
応答時間: min、max、mean、median、p95、p99は応答時間の指標です。特にmean(平均応答時間)とp95(95パーセンタイル応答時間)が重要です。 これらの値が低いほど、システムの応答性が良いことを示します。

ボトルネックの原因の特定

まずは現状を調べていきます。

X-Rayの画面に移動して、ボトルネックを特定してみます。

特定結果

  1. DynamoDBの乱暴なScan
    • データ量が多くなれば多くなるほど、動作に影響が出てきます
    • ソースコードを見てみると、じつはこのプログラムはDynamoDBを全Scanしている事がわかりました。
      const params = {
        TableName: tableName,
      };
      const command = new ScanCommand(params);
      const data = await dynamoDBClient.send(command);
      
    • DAX等を使用して(お金がかる)高速化する事も出来ますが、
    • 正しくQueryすることでDynamoDBの負荷を下げます。
  2. 同期処理
    • 今回はsetTimeout(Sleep)が入っていますが、ここは購入処理を行う別システムにリクエストしていると読み替えてください。
      const promises = event.Records.map(async (record) => {
          // 購入処理により1秒遅延
          await new Promise(resolve => setTimeout(resolve, 1000)); 
          const message = JSON.parse(record.body)
      
    • 加えて同期処理のため、レスポンスが返ってくるまで、処理が終わりません。
    • そこで非同期化を行うことにします。

パフォーマンス改善

パフォーマンスチューニングを実行します。
SAMを使用するため、簡単に構築できます。(2回目)
新しくSQSが追加され、使用するJavaSriptに修正が加えられています。

パフォーマンス改善後リクエスト実行

改善された結果以下のようになりました。
500エラーがなくなりました。

■改善前
http.codes.200: ................................................................ 258
http.codes.502: ................................................................ 207

■改善後
http.codes.200: ................................................................ 1756
http.codes.202: ................................................................ 1844

http.response_timeの記録

■改善前
http.response_time:
  min: ......................................................................... 24
  max: ......................................................................... 2352
  mean: ........................................................................ 1009.7
  median: ...................................................................... 1556.5
  p95: ......................................................................... 2186.8
  p99: ......................................................................... 2276.1
http.responses: ................................................................ 465

■改善後
http.response_time:
  min: ......................................................................... 22
  max: ......................................................................... 1203
  mean: ........................................................................ 62.4
  median: ...................................................................... 44.3
  p95: ......................................................................... 89.1
  p99: ......................................................................... 1022.7

APIキャッシュ

API Gatewayのキャッシュの設定を行ってテストを実行してみます。
DynamoDBの読込の負荷が減ることが確認できました。

APIキャッシュにより、minが0になっていることが確認できると思います。

■キャッシュ前
http.response_time:
  min: ......................................................................... 22
  max: ......................................................................... 1203
  mean: ........................................................................ 62.4
  median: ...................................................................... 44.3
  p95: ......................................................................... 89.1
  p99: ......................................................................... 1022.7
http.responses: ................................................................ 3600

■キャッシュ後
http.response_time:
  min: ......................................................................... 0
  max: ......................................................................... 705
  mean: ........................................................................ 33.4
  median: ...................................................................... 27.9
  p95: ......................................................................... 70.1
  p99: ......................................................................... 138.4
http.responses: ................................................................ 3600

まとめ

今回はServerlessのパフォーマンスチューニングを行ってみました。

SQSやJavaSriptコードの修正、APIキャッシュを使用することで、レスポンス速度がどんどん速くなりました

様々なチューニングを試してみたので今度はDAXとかも使ってチューニングをやってみようと思います。

Amazon DynamoDB Accelerator (DAX)ってなんだろ?

脚注
  1. JAWS DAYS 2024 イベントページ ↩︎

  2. What is AWS X-Ray?
    AWS X-Ray とは ↩︎

  3. そもそも 「サーバーレス」 とは
    サーバレスとは、サーバーの準備・運用が不要であることを意味します。 ↩︎

Discussion