継続的な統合、配信、および展開の概要

前書き

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

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

このガイドでは、これらの各戦略、それらの相互関係、およびそれらをアプリケーションライフサイクルに組み込むことでソフトウェア開発とリリースのプラクティスをどのように変えることができるかについて説明します。 さまざまなオープンソースCI / CDプロジェクトの違いをよりよく理解するには、https://www.digitalocean.com/community/tutorials/ci-cd-tools-comparison-jenkins-gitlab-ci-buildbotをご覧ください-ドローンとコンコース[CI / CDツールの比較]。

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

*継続的な統合*は、開発者がコードを共有リポジトリのメインブランチに早期かつ頻繁に統合することを奨励するプラクティスです。 機能を分離して構築し、開発サイクルの最後にそれらを統合する代わりに、コードは各開発者によって1日中何回も共有リポジトリに統合されます。

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

多くの場合、コードを統合するだけでは、新しいコードまたは機能の品質についての保証はありません。 多くの組織では、コードが標準を満たし、バグを導入せず、既存の機能を破壊しないようにするために手動プロセスが使用されるため、統合にコストがかかります。 頻繁に統合すると、自動化のレベルが適切な品質保証対策の量と一致しない場合に摩擦が生じる可能性があります。

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

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

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

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

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

各段階で、ビルドはチームに警告するテストに失敗するか、テストに合格して次の段階に自動的に昇格します。 ビルドがパイプラインを移動すると、後の段階で、ビルドを可能な限り実稼働環境をミラーリングする環境にデプロイします。 これにより、ビルド、展開プロセス、および環境を並行してテストできます。 パイプラインは、1つのステップでいつでも実稼働にデプロイできるビルドで終了します。

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

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

継続的デプロイとは何ですか?なぜ役立つのですか?

*連続展開*は、完全なテストサイクルに合格した各ビルドを自動的に展開する連続配信の拡張機能です。 人間のゲートキーパーが本番環境に何をいつデプロイするかを決定するのを待つ代わりに、連続デプロイメントシステムは、デプロイメントパイプラインを正常に通過したすべてをデプロイします。 新しいコードは自動的に「デプロイ」されますが、新しい機能を後でまたはユーザーのサブセットに対してアクティブ化する手法が存在することに注意してください。 自動的に展開すると、機能と修正が顧客に迅速にプッシュされ、限られた範囲で小規模な変更が促進され、現在運用環境に展開されているものに対する混乱が回避されます。

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

他のグループは、ベストプラクティスが常に守られるようにし、テストプロセスを限られた運用環境に拡張する方法として、自動リリースの約束を活用しています。 コードをデプロイする前に最終的な手動検証を行わない場合、開発者はコードが適切に設計され、テストスイートが最新であることを保証する責任を負う必要があります。 これにより、メインリポジトリに何をいつコミットするか、いつ何を本番環境にリリースするかの決定が、開発チームの手にしっかりと存在する単一のポイントに崩壊します。

また、継続的な展開により、組織は一貫性のある早期のフィードバックを活用できます。 ユーザーがすぐに機能を利用できるようにし、チームが非生産的な方向に多大な努力を傾ける前に、欠陥や役に立たない実装を早期に発見できます。 機能が役に立たないという迅速なフィードバックを得ると、チームは最小限の影響でより多くのエネルギーをエリアに吸収するのではなく、フォーカスをシフトできます。

連続プロセスの主要な概念と実践

継続的な統合、配信、および展開は、関与する範囲によって異なりますが、それぞれの成功の基本となる概念と実践がいくつかあります。

小さな反復的な変更

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

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

トランクベースの開発

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

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

リリースは、メインブランチから、またはその目的のために特にトランクから作成されたリリースブランチから実行されます。 単一の真実のソースとしてメインブランチに焦点を当て続けるために、リリースブランチでは開発は行われません。

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

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

各コミットがビルドを開始するという事実によって影響が複合されるため、ビルド時間の増加は大きな問題として扱われるべきです。 継続的なプロセスにより、開発者はこれらのアクティビティに毎日従事する必要があるため、これらの分野での摩擦を減らすことは価値のある追求です。

可能であれば、テストスイートのさまざまなセクションを並行して実行すると、ビルドをパイプラインで高速に移動するのに役立ちます。 また、テストの各_type_の割合が意味をなすように注意する必要があります。 通常、単体テストは非常に高速で、メンテナンスのオーバーヘッドが最小限です。 対照的に、自動化されたシステムまたは受け入れテストは、多くの場合、複雑で破損しやすい傾向があります。 これを説明するために、多くの場合、単体テストに大きく依存し、かなりの数の統合テストを実施し、その後、より複雑なテストの数に戻ることをお勧めします。

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

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

  • コードはパイプラインの最初に一度構築する必要があります:結果のソフトウェアは保存され、再構築せずに後のプロセスにアクセスできる必要があります。 各フェーズでまったく同じアーティファクトを使用することで、さまざまなビルドツールの結果として矛盾が生じていないことを確認できます。

  • 展開環境は一貫している必要があります:構成管理システムはさまざまな環境を制御でき、展開パイプライン自体に環境の変更を加えて、正確さと一貫性を確保できます。 レガシー条件がテストの整合性を損なうことを防ぐために、テストサイクルごとにクリーンな展開環境をプロビジョニングする必要があります。 ステージング環境は、ビルドがプロモートされるときに存在する未知の要因を減らすために、実稼働環境に可能な限り一致する必要があります。

  • 一貫性のあるプロセスを使用して、各環境でビルドを展開する必要があります:各展開を自動化し、各展開で同じ一元化されたツールと手順を使用する必要があります。 パイプラインツールのみを使用して展開する場合は、アドホック展開を削除する必要があります。

展開とリリースの分離

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

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

機能を担当するコードをリリースせずにチームが展開するのに役立つ多くのテクニックがあります。 機能フラグは、環境変数の値に基づいてコードを実行するかどうかをチェックする条件付きロジックを設定します。 抽象化による分岐により、開発者はリソースコンシューマーとプロバイダーの間に抽象化レイヤーを配置することで実装を置き換えることができます。 これらの手法を慎重に組み込むことにより、これらの2つのプロセスを切り離すことができます。

テストの種類

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

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

煙テスト

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

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

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

単体テスト

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

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

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

統合テスト

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

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

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

システムテスト

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

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

受け入れ試験

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

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

多くの場合、受け入れテストは、実稼働システムをミラーリングするステージング環境にビルドを展開することから始まります。 ここから、自動化されたテストスイートを実行し、内部ユーザーがシステムにアクセスして、必要な機能を果たしているかどうかを確認できます。 リリースまたは顧客へのベータアクセスの提供後、ソフトウェアが実際にどのように機能するかを評価し、ユーザーからフィードバックを収集することにより、さらなる受け入れテストが実行されます。

追加の用語

上記のより広範なアイデアのいくつかについて説明しましたが、継続的な統合、配信、展開について学習する際に出くわす可能性のある多くの関連概念があります。 表示される可能性がある他の用語をいくつか定義してみましょう。

  • * Blue-Greenデプロイメント*:Blue-Greenデプロイメントは、本番環境でコードをテストし、最小限のダウンタイムでコードをデプロイするための戦略です。 本番環境の2つのセットが維持され、テストが行​​われる非アクティブなセットにコードがデプロイされます。 リリースの準備が整うと、本番トラフィックは新しいコードでサーバーにルーティングされ、即座に変更が利用可能になります。

  • 抽象化による分岐:抽象化による分岐は、ソースコードリポジトリに長期にわたる開発分岐がない、アクティブなプロジェクトで主要なリファクタリング操作を実行する方法です。 抽象化レイヤーは、コンシューマーと既存の実装の間に構築およびデプロイされるため、新しい実装を抽象化の背後で並行して構築できます。

  • ビルド(名詞):ビルドは、ソースコードから作成されたソフトウェアの特定のバージョンです。 言語に応じて、これはコンパイルされたコードまたは解釈されたコードの一貫したセットである場合があります。

  • カナリアリリース:カナリアリリースは、限られたユーザーのサブセットに変更をリリースするための戦略です。 アイデアは、問題がある場合の影響を最小限に抑えながら、すべてが本番ワークロードで正しく機能することを確認することです。

  • ダークローンチ:ダークローンチは、プロダクショントラフィックを受信するが、ユーザーエクスペリエンスに影響を与えないプロダクションにコードをデプロイする方法です。 新しい変更は既存の実装と一緒に展開され、同じトラフィックがテストのために両方の場所にルーティングされることがよくあります。 古い実装はまだユーザーのインターフェースに接続されていますが、舞台裏では、本番環境で実際のユーザーリクエストを使用して、新しいコードの正確性を評価できます。

  • 展開パイプライン:展開パイプラインは、リリースの準備ができているかどうかを評価するために、ますます厳格なテストおよび展開シナリオを通じてソフトウェアを移動するコンポーネントのセットです。 通常、パイプラインは、実稼働環境に自動的にデプロイするか、手動でデプロイするオプションを提供することで終了します。

  • 機能フラグ*または*機能トグル:機能フラグは、環境変数の値に基づいて実行するかどうかを決定する条件付きロジックの背後に新しい機能を展開する手法です。 フラグを適切に設定することにより、アクティブ化せずに新しいコードを実稼働環境にデプロイできます。 ソフトウェアをリリースするには、環境変数の値が変更され、新しいコードパスがアクティブになります。 機能フラグには多くの場合、ユーザーのサブセットが新しい機能にアクセスできるようにするロジックが含まれており、新しいコードを徐々に展開するメカニズムが作成されます。

  • 推進:継続的なプロセスのコンテキストでは、推進とはソフトウェアビルドをテストの次の段階に移行することを意味します。

  • ソークテスト:ソークテストでは、長時間にわたって本番環境または本番環境に相当する負荷がかかっている状態でソフトウェアをテストします。

結論

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

プロジェクトに適したCI / CDソリューションを見つけるには、https://www.digitalocean.com/community/tutorials/ci-cd-tools-comparison-jenkins-gitlab-ci-buildbot-をご覧ください。詳細については、ドローンとコンコース[CI / CDツール比較ガイド]をご覧ください。

前の投稿:Ubuntu 16.04でuWSGIとNginxを使用してDjangoアプリケーションを提供する方法
次の投稿:Ubuntu 16.04でBINDをプライベートネットワークDNSサーバーとして構成する方法