序章

ソフトウェアの開発とリリースは、特にアプリケーション、チーム、および展開インフラストラクチャが複雑になるにつれて、複雑なプロセスになる可能性があります。 多くの場合、プロジェクトが成長するにつれて、課題はより顕著になります。 ソフトウェアを迅速かつ一貫した方法で開発、テスト、およびリリースするために、開発者と組織は、これらのプロセスを管理および自動化するための3つの関連するが異なる戦略を作成しました。

継続的インテグレーションは、個々の開発者の作業を1日に複数回メインリポジトリに統合して、統合のバグを早期に発見し、共同開発を加速することに重点を置いています。 継続的デリバリーは、デプロイまたはリリースプロセスの摩擦を減らし、ビルドのデプロイに必要な手順を自動化して、コードをいつでも安全にリリースできるようにすることを目的としています。 継続的デプロイは、コードが変更されるたびに自動的にデプロイすることで、これをさらに一歩進めます。

このガイドでは、これらの各戦略、それらが互いにどのように関連しているか、およびそれらをアプリケーションのライフサイクルに組み込むことでソフトウェア開発とリリースのプラクティスをどのように変えることができるかについて説明します。 さまざまなオープンソースCI/CDプロジェクトの違いをよりよく理解するには、 CI/CDツールの比較を確認してください。

継続的インテグレーションとは何ですか?なぜそれが役立つのですか?

継続的インテグレーションは、開発者がコードを共有リポジトリのメインブランチに早期に頻繁に統合することを奨励する手法です。 機能を分離して構築し、開発サイクルの最後にそれらを統合する代わりに、コードは各開発者によって1日を通して複数回共有リポジトリに統合されます。

アイデアは、統合を早期に検討することにより、統合のコストを最小限に抑えることです。 開発者は、新しいコードと既存のコードの境界で競合を早期に発見できますが、競合は比較的簡単に調整できます。 競合が解決されると、新しいコードが既存のコードベースの要件を満たしていることを確信して作業を続行できます。

多くの場合、コードを統合しても、それ自体では、新しいコードや機能の品質を保証するものではありません。 多くの組織では、コードが標準を満たし、バグが発生せず、既存の機能が損なわれないようにするために手動プロセスが使用されるため、統合にはコストがかかります。 自動化へのアプローチが実施されている品質保証措置と一致しない場合、頻繁な統合は摩擦を生み出す可能性があります。

統合プロセス内のこの摩擦に対処するために、実際には、継続的インテグレーションは、堅牢なテストスイートと自動化されたシステムに依存してこれらのテストを実行します。 開発者がコードをメインリポジトリにマージすると、自動化されたプロセスが新しいコードのビルドを開始します。 その後、テストスイートが新しいビルドに対して実行され、統合の問題が発生したかどうかが確認されます。 ビルドフェーズまたはテストフェーズのいずれかが失敗した場合、チームはビルドの修正に取り組むことができるようにアラートを受け取ります。

継続的インテグレーションの最終目標は、統合コストを削減し、欠陥に早期に対応するために、統合を日常の開発ワークフローの一部である単純で反復可能なプロセスにすることです。 CIの成功の基本は、システムが堅牢で、自動化され、高速であることを確認しながら、問題を構築するための頻繁な反復と応答性を促進するチーム文化を育むことです。

継続的デリバリーとは何ですか?なぜそれが役立つのですか?

継続的デリバリーは継続的インテグレーションの拡張です。 これは、ソフトウェア配信プロセスの自動化に重点を置いているため、チームはいつでも簡単かつ自信を持ってコードを本番環境にデプロイできます。 コードベースが常にデプロイ可能な状態にあることを保証することにより、ソフトウェアのリリースは、複雑な儀式なしに、目立たないイベントになります。 チームは、複雑な調整や後期テストを行わなくても、必要なときにいつでもリリースできると確信できます。 継続的インテグレーションと同様に、継続的デリバリーは、効果を上げるために技術的改善と組織的改善を組み合わせて行う必要があるプラクティスです。

テクノロジーの面では、継続的デリバリーは、テストとデプロイメントのプロセスを自動化するためのデプロイメントパイプラインに大きく依存しています。 デプロイメントパイプラインは、一連の連続したステージとしてビルドに対してますます厳格なテストスイートを実行する自動化されたシステムです。 これは継続的インテグレーションが中断するところを取り上げるので、信頼できる継続的インテグレーションのセットアップは継続的デリバリーを実装するための前提条件です。

各段階で、ビルドはテストに失敗してチームに警告するか、テストに合格して次の段階に自動的に昇格します。 ビルドがパイプラインを移動するときに、後の段階で、本番環境を可能な限り忠実に反映する環境にビルドをデプロイします。 このようにして、ビルド、展開プロセス、および環境を連携してテストできます。 パイプラインは、単一のステップでいつでも本番環境にデプロイできるビルドで終了します。

継続的デリバリーの組織的側面は、主要な関心事として「展開可能性」の優先順位付けを促進します。 これは、機能が構築され、コードベースの残りの部分にフックされる方法に影響を与えます。 不完全な場合でも、いつでも機能を本番環境に安全にデプロイできるように、コードの設計に配慮する必要があります。 この分野を支援するために、多くの技術が登場しました。

継続的デリバリーは、コードをリポジトリにチェックインしてから、十分にテストされた機能ビルドを本番インフラストラクチャにリリースするかどうかを決定するまでの手順を自動化するため、魅力的です。 コードの品質と正確性を主張するのに役立つ手順は自動化されていますが、リリースするものに関する最終決定は、最大限の柔軟性を実現するために組織に委ねられています。

継続的展開とは何ですか?なぜそれが役立つのですか?

継続的デプロイは、完全なテストサイクルに合格した各ビルドを自動的にデプロイする継続的デリバリーの拡張機能です。 人間のゲートキーパーが何をいつ本番環境にデプロイするかを決定するのを待つ代わりに、継続的デプロイメントシステムは、デプロイメントパイプラインを正常に通過したすべてのものをデプロイします。 新しいコードが自動的にデプロイされた場合でも、後でまたは一部のユーザーに対して条件付きで新しい機能をアクティブ化できることに注意してください。 デプロイすると、機能と修正がお客様に迅速にプッシュされ、限られた範囲で小さな変更が促進され、現在本番環境にデプロイされているものに関する混乱を避けることができます。

この完全に自動化された展開サイクルは、リリースされるものの自動化システムへの制御を放棄することを心配している組織にとって不安の原因となる可能性があります。 自動展開によって提供されるトレードオフは、それらが提供する見返りには危険すぎると見なされる場合があります。

他のグループは、ベストプラクティスが常に守られるようにする手段として、このアプローチを活用しています。 コードをデプロイする前に最終的な手動検証を行わない場合、開発者は、コードが適切に設計され、テストスイートが最新であることを確認する責任を負う必要があります。 これにより、メインリポジトリにコミットする内容と時期、および本番環境にリリースする時期と時期に関する意思決定が、開発チームの単一の意思決定ポイントに統合されます。

継続的展開により、組織は一貫した早期フィードバックの恩恵を受けることもできます。 チームが非生産的な方向に多大な労力を費やす前に、機能をユーザーがすぐに利用できるようにし、欠陥や役に立たない実装を早期に発見することができます。 機能が役に立たないという迅速なフィードバックを取得することで、チームは、影響を最小限に抑えながら、より多くのエネルギーを領域に沈めるのではなく、焦点を移すことができます。

継続的なプロセスの重要な概念と実践

継続的インテグレーション、デリバリー、およびデプロイメントは、それらの関与の範囲によって異なりますが、それぞれの成功の基礎となるいくつかの概念と実践があります。

小さな反復的な変更

継続的インテグレーションを採用する際の最も重要なプラクティスの1つは、小さな変更を奨励することです。 開発者は、大きな作業を小さな断片に分割し、それらを早期にコミットする練習をする必要があります。 抽象化による分岐や機能フラグ(以下を参照)などの特別な手法は、進行中のコード変更からメインブランチの機能を保護するのに役立ちます。

小さな変更により、統合の問題の可能性と影響が最小限に抑えられます。 可能な限り早い段階で共有ブランチにコミットし、その後開発全体を通じて継続的にコミットすることにより、統合のコストが削減され、無関係な作業が定期的に同期されます。

トランクベースの開発

トランクベースの開発では、作業はリポジトリのメインブランチ(「トランク」)で行われるか、頻繁に共有リポジトリにマージされます。 短期間の機能ブランチは、小さな変更を表し、できるだけ早くマージされる限り許容されます。

トランクベースの開発の背後にある考え方は、上記で説明した小さな反復的な変更の概念に違反する大きなコミットを回避することです。 ピアはコードを早期に利用できるため、スコープが小さい場合に競合を解決できます。

リリースは、メインブランチから、またはその目的のためにトランクから作成されたリリースブランチから実行されます。 信頼できる唯一の情報源としてメインブランチに焦点を合わせ続けるために、リリースブランチで開発は行われません。

構築フェーズとテストフェーズを高速に保つ

各プロセスは、自動化された構築とテストに依存して、正確性を検証します。 ビルドとテストのステップは頻繁に実行する必要があるため、これらのステップに費やす時間を最小限に抑えるために、これらのプロセスを合理化することが不可欠です。

ビルド時間の増加は、各コミットがビルドを開始するという事実によって影響が悪化するため、主要な問題として扱う必要があります。 継続的なプロセスにより、開発者はこれらの活動に毎日従事する必要があるため、これらの領域での摩擦を減らすことは非常に価値があります。

可能であれば、テストスイートのさまざまなセクションを並行して実行すると、ビルドをパイプラインでより速く移動するのに役立ちます。 また、テストの各タイプの比率が適切であることを確認するように注意する必要があります。 単体テストは通常、非常に高速で、メンテナンスのオーバーヘッドが最小限に抑えられます。 対照的に、検収試験は複雑で破損しやすいことがよくあります。 これを説明するために、単体テストに大きく依存し、かなりの数の統合テストを実行し、より複雑なテストの数を最小限に抑えることをお勧めします。

展開パイプライン全体の一貫性

継続的デリバリーまたはデプロイメントの実装はリリースの価値をテストすることになっているため、プロセスの各ステップ(ビルド自体、デプロイメント環境、およびデプロイメントプロセス自体)で一貫性を維持することが不可欠です。

  • コードはパイプラインの開始時に1回ビルドする必要があります:結果のソフトウェアは保存され、再構築せずに後のプロセスにアクセスできる必要があります。 各フェーズでまったく同じアーティファクトを使用することにより、異なるビルドツールの結果として不整合が発生していないことを確認できます。
  • 展開環境は一貫している必要があります:構成管理システムはさまざまな環境を制御でき、環境の変更は展開パイプライン自体を介して行われ、正確さと一貫性を確保できます。 従来の条件によってテストの整合性が損なわれるのを防ぐために、テストサイクルごとにクリーンな展開環境をプロビジョニングする必要があります。 ステージング環境は、ビルドがプロモートされるときに存在する未知の要因を減らすために、本番環境と可能な限り一致する必要があります。
  • 一貫したプロセスを使用して、各環境にビルドをデプロイする必要があります:各デプロイメントを自動化し、各デプロイメントで同じ集中型のツールと手順を使用する必要があります。 パイプラインツールのみを使用して展開するために、アドホック展開を排除する必要があります。

展開とリリースを切り離す

コードのデプロイをリリースからユーザーに分離することは、継続的デリバリーとデプロイの非常に強力な部分です。 コードは、最初にアクティブ化したり、ユーザーがアクセスできるようにしたりすることなく、本番環境にデプロイできます。 次に、組織は、展開から独立した新しい機能をいつリリースするかを決定します。

これにより、ビジネス上の決定を技術的なプロセスから分離することにより、組織に大きな柔軟性がもたらされます。 コードがすでにサーバー上にある場合、デプロイメントはリリースプロセスのデリケートな部分ではなくなり、リリース時に参加者の数と関連する作業の量が最小限に抑えられます。

チームが機能をリリースせずに機能を担当するコードをデプロイするのに役立つテクニックはたくさんあります。 機能フラグは、環境変数の値に基づいてコードを実行するかどうかをチェックする条件付きロジックを設定します。 抽象化による分岐により、開発者はプロセスの入力と出力の間に抽象化レイヤーを作成することにより、プロセスを段階的に書き換えることができます。 これらの手法を慎重に計画することで、これら2つのプロセスを切り離すことができます。

テストの種類

継続的インテグレーション、デリバリー、およびデプロイメントはすべて、各コード変更の有効性と正確性を判断するための自動テストに大きく依存しています。 特定のソリューションへの信頼を主張するには、これらのプロセス全体でさまざまなタイプのテストが必要です。

以下のカテゴリは完全なリストを表すものではなく、各タイプの正確な定義については意見の相違がありますが、これらの幅広いカテゴリのテストは、さまざまなコンテキストでコードを評価するさまざまな方法を表しています。

スモークテスト

スモークテストは、コア機能といくつかの基本的な実装および環境の前提条件を確認するために設計された特別な種類の初期チェックです。 スモークテストは通常、より完全なテストスイートを実行する前に、健全性チェックとして各テストサイクルの最初に実行されます。

このタイプのテストの背後にある考え方は、実装で大きな危険信号をキャッチし、それ以上のテストが不可能または価値がないことを示す可能性のある問題に注意を向けるのに役立つことです。 スモークテストはそれほど広範囲ではありませんが、非常に迅速に行う必要があります。 変更がスモークテストに失敗した場合は、コアアサーションが壊れていることを示す初期のシグナルであり、問題が解決するまでテストにこれ以上時間を費やすべきではありません。

新しいフェーズテストの開始時にコンテキスト固有のスモークテストを使用して、基本的な前提条件と要件が満たされていることを確認できます。 たとえば、スモークテストは、統合テストの前またはステージングサーバーへの展開の両方で使用できますが、テストされる条件はそれぞれの場合で異なります。

ユニットテスト

単体テストは、コードの個々の要素を分離された高度にターゲットを絞った方法でテストする責任があります。 個々の関数とクラスの機能は、独自にテストされます。 外部の依存関係は、問題のコードに完全にテストを集中させるために、スタブまたはモックの実装に置き換えられます。

単体テストは、個々のコードコンポーネントの正確性をテストしてから、より複雑なコンテキストに配置する前に、内部の整合性と正確性をテストするために不可欠です。 テストの範囲が限定され、依存関係が削除されるため、欠陥の原因を簡単に突き止めることができます。 また、後で再現するのが難しい可能性のあるさまざまな入力やコードブランチをテストするのに最適な時期です。 多くの場合、スモークテストの後、単体テストは、変更が加えられたときに実行される最初のテストです。

単体テストは通常、変更を送信する前に、個々の開発者が自分のワークステーションで実行します。 ただし、継続的インテグレーションサーバーは、ほとんどの場合、統合テストを開始する前に、安全策としてこれらのテストを再度実行します。

統合テスト

単体テストの後、コンポーネントをグループ化してアセンブリとしてテストすることにより、統合テストが実行されます。 単体テストではコードの機能を個別に検証しますが、統合テストでは、コンポーネントが相互にインターフェイスするときに連携することを確認します。 このタイプのテストには、コンポーネント間の相互作用によって明らかになるまったく異なるクラスのバグを検出する機会があります。

通常、統合テストは、コードが共有リポジトリにチェックインされるときに自動的に実行されます。 継続的インテグレーションサーバーはコードをチェックアウトし、必要なビルド手順を実行し(通常、ビルドが成功したことを確認するためにクイックスモークテストを実行します)、ユニットテストと統合テストを実行します。 モジュールはさまざまな組み合わせで接続され、テストされます。

統合テストは、プロジェクトの健全性を保護するため、共有作業にとって重要です。 変更は、既存の機能を壊さないこと、および期待どおりに他のコードと相互作用することを証明する必要があります。 統合テストの第2の目的は、変更をクリーンな環境に展開できることを確認することです。 これは多くの場合、開発者自身のマシンで実行されない最初のテストサイクルであるため、このプロセス中に未知のソフトウェアや環境の依存関係も発見される可能性があります。 通常、新しいコードが実際の外部ライブラリ、サービス、およびデータに対してテストされるのはこれが初めてです。

システムテスト

統合テストが実行されると、システムテストと呼ばれる別のレベルのテストを開始できます。 多くの点で、システムテストは統合テストの拡張として機能します。 システムテストの焦点は、コンポーネントのグループがまとまりのある全体として正しく機能することを確認することです。

システムテストでは、コンポーネント間のインターフェイスに焦点を当てるのではなく、通常、ソフトウェア全体の外部機能を評価します。 この一連のテストでは、構成されたソフトウェアを統合されたエンティティとして評価するために、構成要素を無視します。 この違いのため、システムテストは通常、ユーザーまたは外部からアクセス可能なインターフェイスに焦点を合わせます。

受け入れ試験

受け入れテストは、配信前にソフトウェアで実行される最後のタイプのテストの1つです。 受け入れテストは、ソフトウェアの一部がビジネスまたはユーザーの観点からすべての要件を満たしているかどうかを判断するために使用されます。 これらのテストは、元の仕様に基づいて作成される場合があり、多くの場合、期待される機能と使いやすさについてインターフェイスをテストします。

多くの場合、受け入れテストは、ソフトウェアのリリースを超えて拡張される可能性のある、より複雑なフェーズです。 自動受け入れテストを使用して、設計の技術要件が満たされていることを確認できますが、通常は手動検証も役割を果たします。

多くの場合、受け入れテストは、本番システムをミラーリングするステージング環境にビルドをデプロイすることから始まります。 ここから、自動テストスイートを実行し、内部ユーザーがシステムにアクセスして、システムが必要な方法で機能するかどうかを確認できます。 ソフトウェアをリリースするか、ユーザーにベータアクセスを提供した後、ソフトウェアが実際の使用でどのように機能するかを評価し、追加のフィードバックを収集することによって、さらに受け入れテストが実行されます。

追加の用語

上記の幅広いアイデアのいくつかについて説明しましたが、継続的インテグレーション、デリバリー、およびデプロイメントについて学習するときに遭遇する可能性のある多くの関連する概念があります。 表示される可能性のある他のいくつかの用語を定義しましょう。

  • ブルーグリーンデプロイメント:ブルーグリーンデプロイメントは、本番環境のような環境でコードをテストし、最小限のダウンタイムでコードをデプロイするための戦略です。 2セットの本番環境に対応した環境が維持され、テストを実行できる非アクティブなセットにコードがデプロイされます。 リリースの準備ができると、本番トラフィックは新しいコードを使用してサーバーにルーティングされ、変更を即座に利用できるようになります。
  • 抽象化による分岐:抽象化による分岐は、アクティブなプロジェクトで主要なリファクタリング操作を実行する方法であり、ソースコードリポジトリに長期間の開発ブランチがないため、継続的インテグレーションの実践では推奨されません。 抽象化レイヤーは、既存の実装内に構築およびデプロイされるため、新しい実装を抽象化の背後に並行して構築できます。
  • ビルド(名詞):ビルドは、ソースコードから作成されたソフトウェアの特定のバージョンです。 言語に応じて、これはコンパイルされたコードまたは解釈されたコードの一貫したバンドルである可能性があります。
  • カナリアリリース:カナリアリリースは、限られたユーザーのサブセットに変更をリリースするための戦略です。 問題がある場合の影響を最小限に抑えながら、すべてが本番ワークロードで正しく機能することを確認するという考え方です。
  • ダークローンチ:ダークローンチは、本番トラフィックを受信するがユーザーエクスペリエンスに影響を与えないコードを本番環境にデプロイする方法です。 新しい変更は既存の実装と一緒に展開され、同じトラフィックがテストのために両方の場所にルーティングされることがよくあります。 古い実装は引き続きユーザーのインターフェースに接続されていますが、舞台裏では、実稼働環境で実際のユーザー要求を使用して、新しいコードの正確性を評価できます。
  • 展開パイプライン:展開パイプラインは、リリースの準備ができているかどうかを評価するために、ますます厳しくなるテストと展開のシナリオを通じてソフトウェアを移動させるコンポーネントのセットです。 パイプラインは通常、本番環境に自動的にデプロイするか、手動でデプロイするオプションを提供することで終了します。
  • 機能フラグまたは機能トグル:機能フラグは、環境変数の値に基づいて実行するかどうかを決定する条件付きロジックの背後に新しい機能を展開する手法です。 このフラグを適切に設定することにより、アクティブ化せずに新しいコードを本番環境にデプロイできます。 機能フラグには、多くの場合、ユーザーのサブセットが新しい機能にアクセスできるようにするロジックが含まれており、新しいコードを徐々に展開するメカニズムを作成します。
  • プロモート:継続的なプロセスのコンテキストでは、プロモートとは、ソフトウェアビルドをテストの次の段階に移行することを意味します。
  • ソークテスト:ソークテストには、大量の本番環境または本番環境のような負荷の下でソフトウェアを長期間テストすることが含まれます。

結論

このガイドでは、継続的インテグレーション、継続的デリバリー、継続的デプロイを紹介し、それらを使用して十分にテストされたソフトウェアを安全かつ迅速に構築およびリリースする方法について説明しました。 これらのプロセスは、広範な自動化を活用し、継続的なコード共有を促進して欠陥を早期に修正します。 これらのソリューションを実装するために必要な技術、プロセス、およびツールは多大な投資を意味しますが、適切に設計され、適切に使用されたシステムの利点は莫大なものになる可能性があります。

プロジェクトに適したCI/CDソリューションを見つけるには、 CI/CDツール比較ガイドで詳細を確認してください。