概要
私の担当するサービスでは、CircleCIによる自動テストを行っています。
最近、実行時間が伸びてきた事もあり、CircleCIの parallelism
という並列実行数の設定項目と、Version 2.1 からの新機能 ( 2018年頃のリリースなので別に新
ではないけど )である commands
parameters
executors
を使って、今まで直列1本で行っていたCircleCIのテストのビルドを並列化して実行時間を10分→4〜5分程度に短くしてみたのでその際の情報を共有します。
いざ、ymlファイルに並列的な書き方をしようとすると、たくさんの重複記述を書く必要が出てしまい、可読性もテンションも下がってしまいます。そこで、
-
executors
項目に実行環境設定(machine: true
)をまとめて、複数のジョブで再利用する -
commands
に何度も出てくるコマンドをまとめて名前をつけて、ステップ内から呼び出す際に再利用する -
parameters
オプションを使ってcommands
のコマンドに渡すパラメータの名前や型を定義して可読性を上げる
を行い、その後
-
parallelism
を任意の数値に設定し、テスト対象のファイルをCircleCIの仕組みを使って よしなに 分割し実行する
をしました。
例 (雰囲気)
もともと、build
というjob一本で直列に実行していたものを
version: 2
jobs:
build:
machine: true
working_directory: ~/work
steps:
- checkout
- run:
name: Execute test 1
command: |
echo "You can execute any command"
- run:
name: Execute test 2
command: |
./test-runner --files \
app/tests/cases/models \
app/tests/cases/controllers
workflows:
version: 2
my-build:
jobs:
- build
↓こうした。
version: 2.1
executors:
my-executor:
machine: true
commands:
my_command_1:
steps:
- run:
name: Execute test 1
command: |
echo "You can execute any command"
my_command_2:
parameters:
target:
type: string
steps:
- run:
name: Execute test 2
command: |
./test-runner --files << parameters.target >>
jobs:
my-unittest-job:
executor: my-executor
parallelism: 3
steps:
- checkout
- my_command_1:
- my_command_2:
target: |
$(circleci tests glob \
"app/tests/cases/{models,controllers}/**/*.test.php" \
| circleci tests split --split-by=filesize
workflows:
version: 2
my-build:
jobs:
- my-unittest-job
コマンドを別途定義するので、このサンプルだと変更後の方が記述量が増えているが、実際はもっとたくさんのジョブやステップが存在しているので、この対策を行わないとコピペに次ぐコピペで大変なことになる。
parameters について
コマンド側で parameters
の中にパラメータ名と型、デフォルト値、説明書きを定義することが出来る。
また、実際のコマンド実行の中では << parameters.パラメータ名 >> という形で取り出すことが出来る。
https://circleci.com/docs/2.0/reusing-config/#parameter-syntax
commands:
# ...略...
my_command_2:
parameters:
tekitouna-parameter-name:
description: Tekitou na setsumei.
type: string
default: "FUGA"
steps:
- run:
command: echo << parameters.tekitouna-parameter-name >>
呼び出し側は、以下のように実行するコマンドにパラメータをセットすることが出来る。
jobs:
my-unittest-job:
steps:
- my_command_2:
tekitouna-parameter-name: HOGE
CircleCIの仕組みを使って よしなに 分割し実行する、件
https://circleci.com/docs/2.0/parallelism-faster-jobs/ によると、circleci tests
コマンドにて glob
を使って対象となるファイル名を取得した後に、split
に渡す事で、parallelism: 3
のように指定した並列実行の数に分割してくれる様子。
--split-by
には filesize
(ファイルのサイズでよしなに分割)とtiming
(過去の実行時間の実績を元によしなに分割)があるようで、timing
のほうが filesize
より更に最適化出来るようだが、設定が若干面倒そうだったので、今回はやめておいた。
circleci tests glob "**/*.go" | circleci tests split --split-by=filesize
サンプルでも、以下のようにテストランナーにテスト対象のファイル名を渡すようにしている。
target: |
$(circleci tests glob \
"app/tests/cases/{models,controllers}/**/*.test.php" \
| circleci tests split --split-by=filesize
苦労した点
今まで直列に、固定された順序で実施されていたため、 たまたま運良く通っていたテスト というのが存在していたが、それらが並列化したタイミングでFailするようになってしまった。そういうのが複数箇所、複数原因あり、特定してテスト側を修正するのが悲しかった。例えば↓の様に a→bの順番で実行されていたからbでrequireしなくても動いてたが、並列化によって実行グループが変わったために b で someFunc() なんて知らない、と怒られたり。
require_once "./functions.php"
someFunc();
someFunc();
参考