もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

circleCIを並列実行して、実行時間を短縮する

はじめに

こんばんは。

今回は、circleCIの実行時間を短縮した備忘録です。

テストコードが多くなると、ciでテストを回すのにかなりの時間がかかります。

ぼくがお仕事を頂いている企業様でも12〜15分(テストのjobは8〜10分)程度の実行時間でした。

ここに課題を感じていたので、ciの並列実行を行い、実行時間の短縮を目指しました。

参考サイト

本題

キモとなるのは parallelism になります。

今回 parallelism で指定した数分、コンテナが並列で実行されます。

こちらを6に指定して、並列で6台が実行されるように変更しました。

並列で実行されるにあたり、各コンテナで実行されるテストを指定してあげる必要がありました。

それを可能にするのが circleci tests globcircleci tests split になります。

glob で実行したいテストのパスを取得し、 split でコンテナ毎に振り分けます。

以下のような感じです。

circleci tests glob "./tests/TestCase/**/*Test.php" | circleci tests split

また、 split によってコンテナ毎に実行してほしいテストのパスが振り分けられますが、これを phpunitで実行するために、 phpunitxmlをつくってあげます。

circleci tests glob "./tests/TestCase/**/*Test.php" | circleci tests split | xargs php ./generateXml.php

generateXml.php

<?php

$basePath = '/home/circleci/project';
$files = array_slice($argv, 1);
$xmlFileStringData = [];
foreach ($files as $file) {
    $xmlFileStringData[] = "<file>{$basePath}/{$file}</file>";
}
$testFileString = implode("\n", $xmlFileStringData);
$template = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="{$basePath}/tests/bootstrap.php">
    <testsuites>
        <testsuite name="Test Suite">
            {$testFileString}
        </testsuite>
    </testsuites>
</phpunit>
XML;

file_put_contents("/tmp/ci_phpunit.xml", $template);

generateXml.php で作成したxmlファイルを指定して、phpunitを実行します

./vendor/bin/phpunit --configuration /tmp/ci_phpunit.xml

これで、コンテナ毎に割り当てられたテストをphpunitが実行してくれます。

実行すると、こんな感じで、コンテナが parallelism の数分実行してくれます。

f:id:kojirooooocks:20210117234927p:plain

つなげて書くと以下みたいな感じです。

      - run:
          name: Execute UnitTest
          command: |
            circleci tests glob "./tests/TestCase/**/*Test.php" | circleci tests split | xargs php ./generateXml.php
            ./vendor/bin/phpunit --configuration /tmp/ci_phpunit.xml
      - store_artifacts:
          name: Store Artifact
          path: logs/cli-error.log

テスト実行時にエラーログを artifactに登録されるようにしているのですが、そちらも複数のコンテナ毎に登録されます。

f:id:kojirooooocks:20210117234940p:plain

今回の対応で、実行時間を大体5〜6分に抑えることができました。

また、resource classをデフォルトの mediumからsmallに落とすことで、並列実行しつつ消費クレジットも落とすことができました。

終わりに

結果的には大成功という感じでした。

他にも 歴史のあるレポジトリのため chckoutに1分程度時間がかかっているので、そこを shallow cloneなどを用いてあげれば、更に短縮が見込めそうです。

現場からは以上です。