概要
CI/CDは、コードの変更からユーザーへの提供までの各ステップを自動化するプラクティスです。継続的インテグレーション(CI)はすべての変更を自動でビルド・テストし、継続的デリバリー(CD)はマージされた変更がデプロイされるまでの経路を自動化します。 本ページでは、CI/CDパイプラインが提供するもの、CIを高速に保つ手法、リリースの自動化、結果の通知、デプロイ頻度などの指標との付き合い方を解説します。例はGitHub Actionsを使いますが、原則はどのCIサービスにも当てはまります。CI/CDパイプラインが提供するもの
パイプラインを整備すると、すべての変更に伴う定型的なステップが人手を介さずに実行されます。自動テストのゲート
プルリクエストごとにテストが実行され、すべて通るまでマージがブロックされます。壊れた変更はリリース後ではなく数分で検出されます。
自動デプロイ
環境ごとに定義したトリガーでデプロイが実行されます。たとえばリリースブランチへのプルリクエスト作成でステージングへ、マージで本番へデプロイします。
CIを高速に保つ
CIの所要時間はあらゆる変更が支払うコストになります。目安として、プルリクエストのゲートとなるパイプラインは5〜10分以内に保ちます。依存関係をキャッシュする
依存関係を毎回ゼロからインストールするのは純粋なオーバーヘッドです。公式のセットアップ用アクションはロックファイルをキーにキャッシュを管理するため、依存関係が実際に変わったときだけキャッシュが再生成されます。結果に影響しない実行をスキップする
実行時の挙動に影響しない変更に、テストスイート全体を実行する必要はありません。paths-ignoreを指定すると、列挙したファイルのみの変更ではワークフローがスキップされます。
スキップされたワークフローが必須ステータスチェックに指定されている場合、チェックが保留のままになりマージがブロックされます。その場合はワークフロー自体は実行したままコストの高いジョブだけを内部でスキップするか、必須チェックの設定を見直します。
テストを並列化する
テストスイートが大きくなると、総実行時間もそれに比例して伸びます。並列化では、スイートを分割して複数のジョブで同時に実行します。matrixストラテジーは同じジョブを列挙した値ごとに1回ずつ実行する仕組みで、各ジョブはその値を使って自分の担当分だけを実行します。
ランナーをスケールアップする
キャッシュと並列化を行ったら、CPUコア数とメモリの多い大きなランナーがビルドを短縮し、ジョブ内の並列性を高めます。 ランナーが大きいほど実行時間あたりの単価は上がりますが、実行が短くなるぶん消費する分数は減るため、待ち時間を削減しながら課金額が横ばいか、むしろ下がることもあります。リリースを自動化する
手作業でのリリース、つまり変更履歴を書き、正しい順序でデプロイの手順を実行する作業は、ミスと遅延が入り込む場所です。この一連の流れは自動化できます。1
変更を追跡できる履歴を保つ
リリースノートは、マージされたプルリクエストとそのコミットの履歴から組み立てられます。後から読んで分かるコミットメッセージの書き方はプルリクエストのコミットメッセージを参照してください。
2
リリースノートを自動生成する
前回のリリース以降にマージされたプルリクエストの一覧を、リリースノートとして組み立てます。変更履歴を手で編集する人はいなくなります。
3
デプロイをリリース用プルリクエストのマージに集約する
ワークフローがリリース用のプルリクエストを生成します。あわせて、マージによる
mainへのプッシュをトリガーにデプロイを実行するワークフローを別に用意します。これにより、本番リリースが手順書の実行ではなく、レビューとマージになります。developに集まり、mainが本番を反映するブランチモデル(いわゆるgit flowを簡略化したもの)でこの流れを実装したものです。
手動で起動すると、developから日付入りのリリースブランチを切り、前回のリリース以降にマージされたプルリクエストの一覧を本文として組み立て、mainへのリリース用プルリクエストを作成します。
プルリクエスト一覧はマージコミットから抽出しているため、このワークフローは
developへの取り込みを「Create a merge commit」で行うリポジトリを前提とします。Squash mergeを使う場合は、gh pr list --state mergedなどによる取得へ置き換えてください。mainへのプッシュ、つまりリリース用プルリクエストのマージをトリガーに、別のワークフローが本番デプロイを実行します。
concurrencyは、プッシュが連続したときにデプロイが重複して走ることを防ぎます。environmentを指定するとGitHubの保護ルールと環境スコープのシークレットを本番デプロイに適用できます。
同じ発想で、ワークフローの他の場所にある繰り返しの摩擦も取り除けます。プルリクエストの作成者を自動でアサインする、ブランチ名や変更されたパスに基づいてラベルを付与する、リリース時にプルリクエストのテンプレートからチェック済み項目を抽出してQAへ引き継ぐ、などです。
パイプラインの結果を通知する
パイプラインがフィードバックループを短縮するのは、その結果が対応すべき人へ届いてこそです。多くのケースは次の2つの経路でカバーできます。- プルリクエスト上のステータスチェック。 CIサービスは各ジョブの結果をプルリクエスト上に表示します。これは組み込みの仕組みで、変更がレビュー中である間のフィードバックをカバーします。
- チャットへの通知。 本番デプロイの完了や定期実行ワークフローの失敗など、プルリクエストの外で起きるイベントには、Slackなどのプッシュ型の経路が必要です。
デプロイ頻度は目標ではなく結果
デプロイ頻度は広く使われる生産性指標ですが、高い値は自動化されたパイプライン、小さく適切に区切られた変更、変更のリスクを低く保つテストスイートといったプラクティスの「結果」です。 数値そのものを最適化すると因果が逆転し、指標が測ろうとしている成果自体が悪化します。- 内容を変えずにデプロイ回数だけを増やすと、運用の手間が増え、価値を届けないまま変更障害率が上がります。
- 障害率を下げる目的でデプロイ頻度を落とすと、1回のデプロイに含まれる変更が増え、障害が起きやすくなり原因の切り分けも難しくなります。