失敗したワークフローの再実行
ワークフローを使って設定を分割する利点は他にもあります。ワークフローの途中でジョブが失敗したときは再実行が簡単になるという利点です。
これはCircleCIの開発チームで仕事をしていた時の話ですが、ビルドからデプロイまでに約50分かかるプロジェクトがありました。さらに、End to Endテストの信頼性が低かったため、時々ランダムなエラーが発生してビルドが失敗するという現象が発生していました。失敗すると開発者はビルドを再実行して、2回目は問題なく通るといった具合です。ワークフローがない場合はすべての処理が1つのビルドの中に入っているため、ランダムなエラーでビルドが失敗した際は、また最初からビルドを実行する必要があるので全体の開発スピードがスローダウンしていました。
ワークフローを使えば失敗したジョブから再実行することができます。例えば、End to Endテストを1つのジョブに切り出せば、もしランダムなエラーで失敗した場合でも、そこから再実行ができます。
失敗したジョブの再実行はCircleCIではとても簡単です。失敗したジョブのワークフロー画面へ行き、"Rerun from failed"を選択します。もし、すべてのジョブを最初から実行したい際は"Rerun from beginning"を選んでください。
CI/CDの基本は決定的、つまり実行結果が常に同じにならないといけません。再実行したら2回目は成功した、というのは本来はよくないのですが、開発の現場ではよくあることです。何度も同じジョブがランダムに失敗するときは見直しが必要ですが、なんらかの理由でたまたま失敗してしまったときはとても便利な機能なので覚えておいてください。
ワークスペース
複数のジョブに分けて管理していくと、時々ジョブの間でデータの受け渡しをしたくなる時があります。こんなときはワークスペースを使えば実現できます。よくある使い所としては、最初にコードをコンパイルして、後のジョブでそれを使うことで各ジョブでコンパイルを省略する使い方です。
ワークスペースとは、あるワークフローが実行されている間だけ有効な共有ファイルストレージと考えると分かりやすいかもしれません。ジョブ間でファイルのやりとりをしたい場合、前のジョブがファイルをこの共有ストレージにアップロードして、後のジョブはここからダウンロードすることができます。CircleCIのワークスペースではアップロードをpersistと呼びダウンロードをattachと呼びます。
では、実際にワークスペースを使ってみましょう。RailsのアプリではしばしばJavaScriptやCSSなどのアセットをプリコンパイルしたものを本番環境にデプロイすることがあります。今回のブログアプリでも同じことができるので、それを使ってワークフローの機能を試してみましょう。アセットをプリコンパイルするprecompileジョブとデプロイするdeployジョブを新たに追加して、これらのジョブ間でアセットを渡してみます。
なお、本来であればdeployステップには実際のデプロイ環境に応じたコマンドを書くのですが、実際にデプロイしようとすると解説が複雑になってしまうため、ここでは擬似的なデプロイにとどめておきます。HerokuなどにRailsアプリをデプロイする際はプリコンパイルされたアセットも一緒にデプロイするという方法があるので、今回はそれを真似てプリコンパイルされたアセットがdeployジョブに渡されていることを確認するだけにしましょう。
precompile:
...
steps:
...
- run: bundle exec rake assets:precompile RAILS_ENV=production
- persist_to_workspace:
root: public/
paths:
- assets
deploy:
...
steps:
- attach_workspace:
at: public
- run: ls -la public/assets
ここも紙面節約のために完成形の.circleci/config.ymlへのリンクを掲載しておき、ポイントだけを解説します。
- run: bundle exec rake assets:precompile RAILS_ENV=production
アセットをプリコンパイルするコマンドです。これを実行するとpublic/assetsというディレクトリにアセットが生成されます。
- persist_to_workspace:
root: public/
paths:
- assets
persist_to_workspaceを使うとワークスペースにファイルをアップロードします。rootでアップロードするファイルが存在するルートディレクトリを指定しpathsで実際にアップロードするファイルを指定します。今回の場合だとpublic配下にはassetsディレクトリの他にもファイルがありますが、今回アップロードしたいのはアセットのみなのでassetsを指定しています。
次にデプロイジョブを見てみましょう。
- attach_workspace:
at: public
attach_workspaceを使うとワークスペースにあるファイルをダウンロードすることができます。atは実際にダウンロードする場所を指定します。成功すれば precompileジョブで生成されたアセットがちゃんとdeployジョブに渡されていることが確認できます。
Fan-in/Fan-outについて
パイプラインで覚えておきたい用語の一つにFan-in/Fan-outというものがあります。Fan-inは複数のジョブが完了したあと1つのジョブを実行するパイプラインのことを言います。Fan-outは逆で1つのジョブの完了を待って、複数のジョブを実行するパイプラインです。今回の例だとinstall_dependencyからrun_test/run_lint/precompileへの流れはFan-outで、そこからdeployへの流れはFan-inになります。覚えにくい場合は、Fan-inは"すぼむ"、Fan-outは"ひろがる"、というイメージを持つと覚えやすいかもしれません。
