HAQM Web Services ブログ

AWS CodeBuild を利用した CI の高速化: 並列テスト実行が利用可能に

AWS CodeBuild で並列テスト実行のサポートが開始されたことをお知らせします。これにより、テストスイートを同時に実行し、ビルド時間を大幅に短縮できます。

この記事のために作成したデモプロジェクトでは、環境をプロビジョニングする時間を含め、テストの合計時間が 35 分から 6 分に短縮されました。AWS マネジメントコンソールの次の 2 つのスクリーンショットは、その差を示しています。

テストスイートの順次実行

CodeBuild の並列テストの結果

テストスイートの並列実行

CodeBuild の並列テストの結果

継続的インテグレーション (CI) を大規模に実行する際に、テスト時間が非常に長くかかることは大きな課題となります。プロジェクトの複雑さが増し、チームの規模が大きくなるにつれて、包括的なテストスイートの実行に必要な時間が大幅に長くなり、パイプラインの実行時間の長期化につながる可能性があります。これにより、新機能やバグ修正の提供が遅れるだけでなく、タスクを進める前にビルドの結果を待たなければならないため、デベロッパーの生産性が低下します。私は実行に最大 60 分かかったパイプラインを経験したことがありますが、最後のステップで失敗し、全体の再実行が必要となり、さらなる遅延が生じました。このような長いサイクルは、CI プロセスにおけるデベロッパーの信頼を損ない、フラストレーションを生じさせ、最終的にはソフトウェア配信サイクル全体のスピードダウンにつながる可能性があります。さらに、テストが長時間実行されることで、リソースの競合、コンピューティング能力の無駄な使用を理由とするコストの増加、開発プロセスの全体的な効率の低下が生じる可能性があります。

CodeBuild における並列テスト実行により、複数のビルドコンピューティング環境で同時にテストを実行できるようになりました。この機能は、各ビルドノードがテストスイートのサブセットを個別に実行するシャーディングアプローチを実装します。CodeBuild は、現在のノード番号とノードの合計数を識別する環境変数を提供します。この環境変数は、各ノードが実行するテストを決定するために使用されます。ビルド時にコントロールビルドノードやノード間の調整は行われません。各ノードは独立して動作し、テストの割り当てられた部分を実行します。

テスト分割を有効にするには、buildspec.xmlbatch fanout セクションを設定し、必要な並列処理レベルと他の関連パラメータを指定します。さらに、適切なテストコマンドと選択した分割方法とともに、ビルドステップで codebuild-tests-run ユーティリティを使用します。

テストは、指定したシャーディング戦略に基づいて分割されます。codebuild-tests-run は、次の 2 つのシャーディング戦略を提供します:

  • 均等な分散。 この戦略は、テストファイルをアルファベット順に並べ替え、並列テスト環境全体にわたってチャンクで均等に分散します。テストファイルの名前または数量が変更されると、シャード間でファイルが再割り当てされる可能性があります。
  • 安定した分散。 この戦略は、一貫性のあるハッシュアルゴリズムを使用して、シャード間でのテストの分散を修正します。新しいファイルが追加または削除されても、ファイルのシャードへの既存の割り当ては維持されます。

CodeBuild は、テストを並列実行する際におけるテストレポートの自動マージをサポートします。自動テストレポートマージにより、CodeBuild はテストレポートを単一のテストサマリーに統合し、結果分析を簡素化します。マージされたレポートには、合格/不合格の集約されたステータス、テスト期間、および失敗の詳細が含まれるため、手動でのレポートを処理する必要性が軽減されます。マージされた結果は、CodeBuild コンソールで表示したり、AWS コマンドラインインターフェイス (AWS CLI) を使用して取得したり、テスト分析を効率化するために他のレポートツールと統合したりできます。

仕組みを見てみましょう
プロジェクトで並列テストを実装する方法を説明します。このデモでは、数百のテストを含む非常に基本的な Python プロジェクトを作成しました。迅速に進めるために、コマンドラインで HAQM Q Developer に 1 個のプロジェクトと 1,800 件のテストケースを作成するよう指示しました。各テストケースは個別のファイルに含まれており、1 秒で完了します。すべてのテストを順番に実行するには、環境をプロビジョニングする時間を除いて 30 分かかります。

このデモでは、10 個のコンピューティング環境でテストスイートを並列実行し、スイートの実行にかかる時間を測定します。

これを実行するために、プロジェクトに buildspec.yml ファイルを追加しました。

version: 0.2

batch:
  fast-fail: false
  build-fanout:
    parallelism: 10 # ten runtime environments 
    ignore-failure: false

phases:
  install:
    commands:
      - echo 'Installing Python dependencies'
      - dnf install -y python3 python3-pip
      - pip3 install --upgrade pip
      - pip3 install pytest
  build:
    commands:
      - echo 'Running Python Tests'
      - |
         codebuild-tests-run \
          --test-command 'python -m pytest --junitxml=report/test_report.xml' \
          --files-search "codebuild-glob-search 'tests/test_*.py'" \
          --sharding-strategy 'equal-distribution'
  post_build:
    commands:
      - echo "Test execution completed"

reports:
  pytest_reports:
    files:
      - "*.xml"
    base-directory: "report"
    file-format: JUNITXML 

YAML ファイルには、言及しておくべき部分が 3 つあります。

まず、batch の下に build-fanout セクションがあります。parallelism コマンドは、並列実行するテスト環境の数を CodeBuild に伝えます。ignore-failure コマンドは、ファンアウトビルドタスクの失敗を無視できるかどうかを示します。

次に、プリインストールされた codebuild-tests-run コマンドを使用してテストを実行します。

このコマンドは、テストファイルの完全なリストを受け取り、現在のノードで実行する必要があるテストを決定します。

  • 上記で説明したように、均等な分散と安定した分散のいずれかを選択するには、sharding-strategy 引数を使用します。
  • 実行の候補であるすべてのファイルを渡すには、files-search 引数を使用します。パフォーマンス上の理由から、提供されている codebuild-glob-search コマンドを使用することをお勧めしますが、find(1) などの任意のファイル検索ツールも機能します。
  • test-command 引数を使用して、シャードで実行する実際のテストコマンドを渡します。

最後に、reports セクションは、各ノードでテストレポートを収集してマージするよう CodeBuild に指示します。

その後、CodeBuild コンソールを開いて、プロジェクトと、このプロジェクトのバッチビルド構成を作成します。ここでは新しいことは何もないので、詳細は割愛します。開始するためのすべての詳細はドキュメントに記載されています。  並列テストはバッチビルドで機能します。バッチで実行するようにプロジェクトを設定してください

CodeBuild: バッチビルドを作成する

これで、テストスイートの実行をトリガーする準備ができました。GitHub リポジトリに新しいコードをコミットするか、またはコンソールでビルドをトリガーできます。

CodeBuild: 新しいビルドをトリガーする

数分後、ビルドのさまざまなステップのステータスレポートが表示されます。これには、各テスト環境またはシャードのステータスが含まれます。

CodeBuild: ステータス

テストが完了したら、[レポート] タブを選択して、マージされたテストレポートにアクセスします。

CodeBuild: テストレポート

[レポート] セクションでは、すべてのシャードのすべてのテストデータを集約し、すべてのビルドの履歴を保持します。[レポート履歴] セクションで最新のビルドを選択して、詳細レポートにアクセスします。

CodeBuild: テストレポート

想定どおり、1,800 件のテストケースについて、集約されたステータスと個別のステータスを確認できます。このデモでは、すべてが合格し、レポートは緑色です。

デモプロジェクトの 1,800 件の各テストは 1 秒で完了します。このテストスイートを順番に実行したところ、完了までに 35 分かかりました。テストスイートを 10 個のコンピューティング環境で並列実行すると、環境をプロビジョニングする時間を含めて 6 分で完了しました。並列実行にかかった時間は、順次実行にかかった時間の 17.1% でした。実際の数値はプロジェクトによって異なります。

その他の情報
この新しい機能は、すべてのテストフレームワークと互換性があります。ドキュメントには、Django、Elixir、Go、Java (Maven)、Javascript (Jest)、Kotlin、PHPUnit、Pytest、Ruby (Cucumber)、Ruby (RSpec) 用の例が含まれています

スペース区切りのリストを受け入れないテストフレームワークの場合、codebuild-tests-run CLI は、CODEBUILD_CURRENT_SHARD_FILES 環境変数を通じて柔軟な代替手段を提供します。この変数には、現在のビルドシャードのテストファイルパスの改行区切りリストが含まれます。これを使用して、さまざまなテストフレームワークの要件に適応し、テストファイル名をフォーマットできます。

独自のシャーディングスクリプトを記述し、各ビルドで自動的に設定される CODEBUILD_BATCH_BUILD_IDENTIFIER 環境変数を使用することで、テストを環境間で分割する方法をさらにカスタマイズできます。この手法を用いて、フレームワーク固有の並列化または最適化を実装できます。

料金と利用可能なリージョン
並列テスト実行により、これまで必要だった時間と比べて大幅に短時間でテストスイートを完了できるようになり、開発サイクルが加速し、チームの生産性が高まります。この記事の説明のために作成したデモプロジェクトで費やされた時間は、順次ビルドの 18.7% でした。

並列テスト実行は、CodeBuild が提供する 3 つのコンピューティングモード、すなわち、オンデマンド、リザーブドキャパシティ、AWS Lambda コンピューティングのすべてでご利用いただけます。

この機能は、CodeBuild が提供されているすべての AWS リージョンで今すぐご利用いただけます。使用するコンピューティングリソースについての標準の CodeBuild 料金以外の追加コストはかかりません。

今すぐ CodeBuild で並列テスト実行をぜひお試しください。詳細を確認し、テストの並列化を開始するには、AWS CodeBuild ドキュメントにアクセスしてください。

– seb

PS: デモアプリケーションとそのテストスイートを作成するために使用したプロンプトは次のとおりです:「I’m writing a blog post to announce codebuild parallel testing.Write a very simple python app that has hundreds of tests, each test in a separate test file.Each test takes one second to complete.」


ニュースブログはいかがでしたか? こちらの 1 分間のアンケートにぜひご協力ください!

(このアンケートは外部企業に委託して行われます。AWS は、AWS プライバシー通知に記載されているとおりにお客様の情報を取り扱います。AWS は、このアンケートを通じて収集したデータを所有し、収集した情報をアンケートの回答者と共有することはありません)

原文はこちらです。