CI/CD のベストプラクティス

継続的インテグレーション、継続的デリバリー、継続的デプロイメントは、DevOpsムーブメントから生まれたソフトウェア開発手法です。 コードのビルド、テスト、およびリリースのプロセスを従来の手法よりさらに効率化し、製品をより素早くユーザーの手元に届けることができます。 うまく構成すれば、ビルドパイプラインを使って、機能するソフトウェアを予定通りに提供し、最新の変更に関するフィードバックを適時に得ることが可能となります。

CI/CDパイプラインの構築は、構築して放置するものではありません。 開発中のソフトウェアと同様に、CI/CD のプラクティスにおいても、そのプロセスを洗練できるよう、継続的にデータを分析し、フィードバックに耳を傾けるといった、反復的なアプローチをとることにメリットはあるでしょう。 この記事では、パイプラインへの適用を検討すべき継続的インテグレーション/継続的デリバリーのベストプラクティスを説明します。

早期にコミット、頻繁にコミット

継続的インテグレーションの実装はまず、すべてのソースコード、構成ファイル、スクリプト、ライブラリ、および実行可能ファイルをソースコントロールに含めることから始まります。すべての変更をトラッキングできるようにするのです。

ただし、ツールのみでは不十分で、それをどのように使用するかが重要となります。 継続的インテグレーションは、より小規模な変更をより頻繁に共有することで、複数の貢献者が提供する変更を統合するプロセスをより簡単にすることを目的としています。

コミットが行われるたびに、変更に関するフィードバックを迅速に提供できるように、一連の自動テストが開始されます。 定期的にコミットすることで、チームは同じ土台に基づいて作業することができるため、コラボレーションを円滑に進めやすくなり、大規模で複雑な変更を統合する際に、解決が困難なマージの競合が発生する可能性を軽減することができます。

継続的インテグレーションのメリットを得るには、すべての関係者がメイン(マスター)にプッシュして変更をチームの他のメンバーに共有し、ほかの人の変更を受け取れるように作業コピーを更新することが欠かせません。 一般的な経験則として、1日に最低1回はメイン(マスター)にコミットするようにしましょう。

長期的なブランチで作業を続けることに慣れているチームにとって、このように頻繁にメインブランチに変更をプッシュするのは、面倒に感じられるかもしれません。 そう感じてしまうのは、ほかの人から監視されているという恐怖感からか、もしくはタスクを1日で片づけるにはその規模が大きすぎるからかもしれません。

品評し合うチームカルチャよりもコラボレーションを中心としたチームカルチャを築くことは重要なことです。機能してきたやり方が変更される際に共通して言えるように、チームとしての取り組み方を話し合う必要があります。 1つのチームとして協力しながらタスクをより小さな個別のタスクに分解することで、それぞれがこの手法を受け入れやすくなるでしょう。

ライブにリリースする準備ができていない新機能をホストする際に、長期的に続いているブランチを使用する場合には、機能フラグを使用するオプションもあります。 このフラグを使えば、設定に応じて特定の機能を表示するように制御できるため、エンドユーザーが利用できないようにしたまま、コードの変更をマージして、品質管理用のビルドに含めることができます。

ビルドを青信号に維持する

CI/CD パイプラインでは、変更がコミットされるたびにソリューションをビルドして一連の自動テストを実行することで、変更に関するフィードバックを開発者に迅速に提供します。

不良のある土台にビルドし続けないようにすることと、コードを一貫してリリースできる状態に維持することが目的です。 課題が生じたときにそれを解決する方がはるかに効率的であるだけでなく、本番環境で何らかの問題が発生した場合に、迅速に修正をロールアウトすることも可能なのです。

何らかの理由でビルドに障害が出た場合、正しく動作するようにすることはチームのプライオリティでなければなりません。 最後に変更を行った人のせいにして、課題を修正するタスクをその人に押し付けたくなることもありますが、 チームのせいにすることに注力しても、建設的なチームカルチャが生まれることもなければ、問題の根底的な原因を特定することもままならないでしょう。 障害が発生しているビルドへの対処と障害の原因を理解する取り組みをチーム全体の義務とすることによって、CI/CD ワークフロー全体を改善することができます。 当然ながら、プレッシャーと緊迫した空気が高まっている時には、このように述べることは、実際に行うよりも簡単なことですが、継続的な改善を行うには、DevOpsカルチャを進化させることも必要なのです。

確かに、ちょっとした構文エラーや依存関係の欠落が原因であることを特定するだけのために、手元の作業をすべて停止して、障害のあるビルドの修正に取り組まなければならないのは不服かもしれません。 このような状況を避けるには、チームメンバーが変更を共有する前に、基本的なテストをローカルで構築して実行することがお勧めです。 理想としては、労力を二重に果たすことのないように、すべての人がCI/CDシステムと同じスクリプトを使用できる必要があります。 また、CI/CD ツールを組織に実装することも検討するとよいでしょう。

ビルドは一度限り

ステージごとに新しいビルドを作成してしまう、共通したミスがあります。

環境ごとに異なるコードをビルドし直せば、矛盾が生じてしまう可能性が出てくるため、以前に行ったすべてのテストに合格できているのかどうかに自信を持てなくなります。 そのため、ビルドパイプラインの各ステージには、同一のビルドアーティファクトを簡易初期化して、最終的にライブにリリースするようにする必要があります。

これを実践するには、システムに依存しないビルドが必要です。 変数、認証パラメーター、構成ファイル、またはスクリプトはすべて、ビルドに統合されるのではなく、デプロイメントスクリプトによって呼び出されるようにしなければなりません。 こうすることで、同一のビルドをテスト用のすべての設定にデプロイし、各ステージにおいて、特定のビルドアーティファクトに対するチームの自信を高めることができます。

ビルドスクリプト、構成ファイル、およびデプロイメントスクリプトなどのあらゆる要素をアプリケーションコードと同じソースコントロールシステムに格納しておくことは良い実践ではありますが、ビルドアーティファクト自体に関しては該当しません。 ビルドはそういった要素の入力によって生成されるものであるため、ソースコントロールには属しないのです。 代わりに、Nexus などの中央のアーティファクトリポジトリでバージョン管理と保管を行い、そこから各インスタンスにプルしてデプロイすることが推奨されます。

テストを合理化する

CI/CDは、ソフトウェアの品質を保証する上で、自動テストに大きく依存していますが、すべての事態でテストしなければならないということではありません。

結局のところ、継続的インテグレーションの目的は、従来の方法よりも迅速なフィードバックの提供と価値の高い製品を早いテンポでユーザーに提供することにあります。 テストのカバレッジとパフォーマンスで釣り合いを取らなければなりません。 テスト結果を得るまでに長い時間を要するのであれば、そのプロセスを迂回する理由と方法を探すことになるでしょう。

できる限り早くフィードバックを得る上で、最も迅速に完了できるテストを最初に実行し、ビルドへのある程度の自信を得た後で、より長く時間がかかるテストに取り組むと良いでしょう。 このフェーズは、手動品質管理に伴う時間を考慮し、チェックを実施するチームの予定に応じて、すべての自動テストが正しく完了するまで制限するのが最善と言えます。

自動テストの最初のレイヤーは通常、ユニットテストです。これは、幅広いカバレッジを提供し、最新の変更によって導入された明確な課題を警告するために使用できます。 ユニットテストの後は、自動化されたインテグレーションテストまたはコンポーネントテストを続けることができます。ここでは、コード内の様々な個所の間で起こるやり取りがテストされます。

それ以降は、GUIテスト、パフォーマンステスト、負荷テストといったより複雑な自動テストに費やし、その後で、最終的に手動の探索的テストや承認テストに時間を掛けることができます。 このような長い時間のかかるテストをより効率的に行うには、それが自動であるか手動であるかに関係なく、特定の製品とユーザーにとって最大のリスクとなるエリアに焦点を当てましょう。

環境をクリアする

QA スイートから最大限の成果を得るには、デプロイメントごとに本番前環境をクリーンアップする時間を取りましょう。

環境を長時間実行し続けると、デプロイメントに適用された構成の変更と更新をすべてトラッキングするのが難化します。

時間が経つと、設定は元のセットアップ、そして設定間で異なってしまうため、ある設定で合格または失敗したテストが別の設定で同じ結果を返さなくなってしまう可能性があります。 静的な環境を維持するにはメンテナンス費がかかります。それによって QA プロセスに遅延が発生し、リリースプロセスが遅れてしまいかねません。

コンテナーを使用して環境をホストし、テストを実行すると、新しいデプロイメントのたびに環境をスピンアップしたり壊したりするのが簡単に行えます。Infrastructure-as-Codeアプローチを使えば、これらのステップをスクリプト化することができます。 毎回新しいコンテナーをインスタンス化することで、一貫性を確保し、より簡単に環境をスケーリングできるため、必要であれば複数のビルドを並行してテストすることが可能です。

本番にデプロイするための唯一の手段とする

ビルドの品質に確信を持つことができる、信頼性のある高速で安全な CI/CD パイプラインを構築したわけですから、どのような理由においてもプロセスをバイパスできるようにすることで、その取り組みを無駄にしてしまうことがあってはいけません。

通常、リリースプロセスを迂回するリクエストは、変更が小さいものであるか、緊急を要するもの(もしくはその両方)であるために行われますが、そのような要求に譲歩してしまうと、節約になりません。

自動品質管理のステージを省略すると、回避可能な課題が紛れ込んでしまうリスクが生じるだけでなく、ビルドをテストインスタンスにデプロイできる準備が整っていないため、課題の再現とデバッグが非常に困難になります。

ある時点で、「今回だけ」と言ってプロセスをバイパスするように求められることがあるでしょう。 その時には、その要望を必死に消し去る衝動に駆られるかもしれませんが、回顧的または事後解析のアプローチで、その要望の背後にあるモチベーションを理解することをお勧めします。 プロセスの進行が遅すぎるのかもしれません。 パフォーマンスの改善や調整が必要なのかもしれません。 プロセスを使用するタイミングに誤解があるのでしょうか。 CI/CD パイプラインのメリットを伝えることで、関係者が意義を理解しやすくなり、今後同じような要求が浮上するのを抑制することができます。

パイプラインの監視と測定

CI/CDパイプラインのセットアップ中に、本番環境ができるだけ早期に問題の兆候を警告できるように監視を実装したことでしょう。

リリースする製品と同様に、ビルドプロセスもフィードバックを得るメリットがあります。

CI/CD ツールが収集したメトリクスを分析すると、潜在的な課題や改善が必要な分野を特定することができます。

  • 毎週、毎日、または毎時間トリガーされるビルド数を比較すると、パイプラインインフラストラクチャがどのように使用されているのか、スケールアップまたはスケールダウンが必要であるのか、ピークロードがいつ発生する傾向にあるのかといった有益な洞察を得ることができます。
  • デプロイメントの速度を経時的にトラッキングし、時間がかかる傾向にあるのかを監視することで、パフォーマンス最適化を行う時期を判断することができます。
  • 自動テストから得る統計をもとに、並列処理が有益でないエリアを特定することができます。
  • QA の結果を確認し、定期的に無視されている課題を見つけ出すことで、品質管理カバレッジを合理化できる箇所を特定できます。

チームワーク化する

効果的な CI/CD ワークフローを構築することは、使用するプロセスやツールだけでなく、チームと組織のカルチャにも関係しています。

継続的インテグレーション、デリバリー、およびデプロイメントは、DevOpsの手法です。 これらは、従来の開発者、QA エンジニア、およびオペレーションのサイロを取り崩し、これらの分野がともにコラボレーションすることを促進する手法です。

サイロを取り崩すと、エンドツーエンドのワークフローやコラボレーションの機会、そしてさまざまな分野の専門知識を役立てられる機会が見えてきます。 パイプラインの管理は、1人で行える作業ではありません。 CI/CD プラットフォームを実装すると、運用手法の改善にもつながる可能性があります。

ソフトウェアを実現させるための責任を共有する雰囲気を作り出すことで、ビルドの修正に着手するなり、環境をコンテナー化するなり、実施が遅れがちな手作業を自動化するなり、チーム内のすべての人が貢献しやすい環境を提供することができます。

チームメンバーが実験しながらアイデアを共有することのできる、信頼に基づくカルチャを促進することで、それに関与する人だけでなく、組織や制作しているソフトウェアにもメリットがあります。 何かがうまく行かなかった場合に、チーム内の誰かにその責任を押し付けるのではなく、その失敗から学ぶことに狙いを定めるべきでしょう。根本的な原因を理解し、将来においてどのように回避できるのかを理解するのです。

CI/CDの手法を改善し、より堅牢で効果的なものにする機会を逃してはいけません。 チームメンバーが非難・批評を恐れずに実験や革新を行える環境を提供することで、継続的な改善の好循環を作り出すのです。