TT Lab
はじめる
学ぶ 学習パス コース

CI/CDパイプライン

赤信号がすべて同じ赤信号ではない

TT Labで続きを見る

一言でいうと

パイプラインの失敗は、インフラの問題・フレーキーテスト・本物の欠陥の3つに分類されます。分類しないと、人はすべてに「もう一度実行」で対応してしまい、本物の欠陥がその中に隠れます。

なぜ必要なのか

失敗が数日に1回なら、人はログを読みます。1日に10回なら、読みません。リトライボタンが先に押され、通れば忘れられます。この習慣がついたチームでは、本物の欠陥も、2、3回リトライされているうちにたまたま通れば、そのままマージされてしまいます。赤信号がシグナルであることをやめる瞬間です。

そのため必要なのは、よりよいログではなく分類です。この失敗がどの種類なのかを先に決め、種類ごとに異なる対応をします。

分ける方法は、意外に単純です。同じコミットをもう一度実行して、結果が分かれるかです。分かれるなら不安定かインフラで、いつも同じなら本物の欠陥です。そのため、パイプラインは、「この実行がどのコミットだったのか」を結果に必ず残す必要があります。

再現できる失敗にする

不安定と本物の欠陥を分けるには、同じ条件を再び作れなければなりません。そのため、実行ごとに次のものを結果に書き残しておきます。

この3つを残すだけで、「再現できません」という報告が大きく減ります。

いつ壊れたのかを探す

失敗の原因となったコミットを探すとき、ログをさかのぼって読む方法は、コミット数に比例して時間がかかります。二分探索は、ログに定数を掛けた時間で済みます。git bisectのドキュメントは、675個のリビジョンを約10ステップ、337個を約9ステップに絞り込むと書いています。コミット1,000個を10回で絞り込むという意味です。

自動化するには、条件が1つあります。判定スクリプトが決定的でなければなりません。同じコミットで同じ答えを出さないと、二分探索は無関係なコミットを原因として指し示します。そのため、不安定なテストで二分探索を実行してはいけません。

git bisect runは、スクリプトの終了コードで判定します。規約は正確に決まっています。

0          이 커밋은 정상(good/old)
1..127     이 커밋은 문제 있음(bad/new)   단, 125 는 제외
125        판정할 수 없음 — 이 커밋은 건너뛴다(skip)
128..255   이분 탐색 자체를 중단한다

125が別に用意されている理由が重要です。ビルドがそもそも通らないコミットのように、判定できないものを「問題あり」と報告すると、原因がそちらへ誤って誘導されます。そして126と127は、POSIXシェルが「実行できない」「コマンドが見つからない」に使う値なので、125が、この用途に使える最大の値として選ばれました。判定スクリプトを書くときは、exit -1のようなものを使わないよう注意します。その値は255になり、探索をまるごと中断させます。

フィードバック時間バジェット

フィードバック時間に数値の上限を設けないと、パイプラインは静かに遅くなります。1ステップずつ増えた時間には誰も気づかず、ある日「もともと少し時間がかかるものです」になります。

上限を超えたときにすることは、決めておくのがよいです。マージをブロックするステップとしないステップを分けて遅いものを後ろへ送る、テストを分割する、あまり壊れない領域のテストを変更範囲に応じてスキップする、といった方法です。どの方法でも、何を切り捨てたかを記録する必要があります。切り捨てたものを書き残さないと、数か月後にその領域で事故が起きたとき、なぜ捕まえられなかったのかを誰も説明できません。

現場での姿

参考

次のラボですること

gitリポジトリを1つ作ってコミットを積み、途中のどこかで壊れるようにしておいたうえで、原因のコミットを二分探索で探します。まず手でgit bisect start / good / badを実行して、何ステップで絞り込めるかを数え、同じことを判定スクリプトで自動化します。スクリプトが終了コードの規約を守っているかを確認し、ビルドが通らないコミットをわざと紛れ込ませて、125でスキップしないと、原因がどのように誤って指し示されるかを自分の目で見ます。続いて、結果が分かれる判定スクリプトを入れて、二分探索が崩れるのを確認し、最後に、失敗ログを種類ごとに数えて報告する分類スクリプトを書きます。