マイクロサービス アーキテクチャを主な利点の 1 つは、リリース サイクルの高速化です。 しかし、優れた CI/CD プロセスなしでは、マイクロサービスが約束するアジリティは達成できません。 この記事では、これらの課題について説明し、問題に対処するために推奨するいくつかの方法を示します。
CI/CD とは
CI/CD の話は、実際には、継続的インテグレーション、継続的デリバリー、および継続的デプロイという関連する複数のプロセスについての話です。
継続的インテグレーション。 コード変更は、メイン ブランチに頻繁にマージされます。 自動化されたビルドとテストのプロセスによって、確実にメイン ブランチのコードが、常に運用環境の品質であるようにします。
継続的デリバリー。 CI プロセスに合格したコード変更は、運用環境に似た環境に自動的に公開されます。 運用環境へのデプロイには手動による承認が必要な場合がありますが、それ以外の場合は自動化されます。 目標は、コードが常に運用環境にデプロイされる "準備ができている" ことです。
継続的なデプロイ: 前の 2 つの手順に合格したコード変更は、自動的に運用環境にデプロイされます。
マイクロサービス アーキテクチャのための堅牢な CI/CD プロセスの目標のいくつかを次に示します。
各チームは、他のチームに影響を与えたり妨害したりすることなく、独立に所有するサービスを構築およびデプロイできます。
サービスの新しいバージョンは、運用環境にデプロイされる前に、検証のために開発/テスト/QA 環境にデプロイされます。 品質ゲートは、各段階で適用されます。
サービスの新しいバージョンは、以前のバージョンと並行してデプロイできます。
十分なアクセス制御ポリシーが設定されています。
コンテナー化されたワークロードの場合、運用環境にデプロイされているコンテナー イメージを信頼できます。
堅牢な CI/CD パイプラインが必要な理由
従来のモノリシック アプリケーションには、その出力がアプリケーション実行可能ファイルである単一のビルド パイプラインがあります。 すべての開発作業は、このパイプラインに注ぎ込まれます。 優先度の高いバグが見つかった場合、修正を統合し、テストして発行する必要がありますが、それによって新機能のリリースが遅れる可能性があります。 これらの問題は、十分にファクタリングされたモジュールを用意し、機能ブランチを使用してコード変更の影響を最小限に抑えることで軽減できます。 ただし、アプリケーションが複雑になり、機能が追加されるにつれて、モノリシックのリリース プロセスは不安定になり、中断される可能性があります。
マイクロサービスの理念に従うと、すべてのチームは、リリースするために一列に並んで待つ必要はありません。 サービス "A" をビルドしているチームは、サービス "B" の変更がマージされ、テストされ、デプロイされるまで待たずに、更新プログラムをいつでもリリースできます。
高速なリリースを実現するには、リリース パイプラインを自動化して、信頼性を高くし、リスクを最小限に抑える必要があります。 毎日 1 回以上、運用環境にリリースを行えば、エラーの再発やサービスの中断はめったに発生しないはずです。 同時に、不適切な更新プログラムがデプロイされた場合は、信頼性の高い方法で、サービスの前のバージョンに迅速にロールバックするかロールフォワードする必要があります。
課題
多数の小さな独立したコード ベース。 各チームは、独自のビルド パイプラインを使用して独自のサービスをビルドします。 組織によっては、チームは別々のコード リポジトリを使用する場合があります。 別々のリポジトリでは、システムをビルドするための知識がチームをまたがって分散し、アプリケーション全体をデプロイする方法を知っている人物が組織内に存在しないという状況に陥る可能性があります。 たとえば、ディザスター リカバリー シナリオで、すぐに新しいクラスターへのデプロイが必要になったら、どうしたらよいのでしょうか。
対応策:この知識が各チーム内に "隠れて" しまわないように、統合パイプラインや自動パイプラインを用いてサービスをビルドし、デプロイします。
複数の言語とフレームワーク。 各チームはテクノロジーを独自に組み合わせて使用するため、組織全体で動作する単一のビルド プロセスを作成するのは困難になる可能性があります。 ビルド プロセスは、すべてのチームが言語やフレームワークを選択できる柔軟性を備えている必要があります。
対応策:サービスごとにビルド プロセスをコンテナー化します。 そうすれば、ビルド システムではコンテナーを実行できる必要があるだけです。
統合とロード テスト。 チームは独自のペースで更新プログラムをリリースするため、堅牢なエンド ツー エンド テストを設計するのは困難である可能性があります。これは特にサービスに他のサービスとの依存関係がある場合にそうなります。 さらに、運用クラスター全体の実行は負荷がかかる可能性があるため、すべてのチームが独自のクラスター全体をテスト目的でのみ運用環境規模で実行することはほとんどありません。
リリース管理。 すべてのチームが、更新プログラムを運用環境にデプロイできる必要があります。 これは、すべてのチーム メンバーがこれを行う権限を持っているという意味ではありません。 ただし、一元的なリリース マネージャー ロールを使用すると、デプロイの速度が遅くなる可能性があります。
対応策:CI/CD プロセスが自動化され、信頼性が高くなるにつれて、一元的な権限は必要なくなっていきます。 ただし、主要な機能の更新プログラムのリリースと小さなバグ修正に対して、異なるポリシーを設定できます。 分散型は、ゼロ ガバナンスという意味ではありません。
サービスの更新。 サービスを新しいバージョンに更新するときは、それに依存するその他のサービスの中断が発生してはなりません。
対応策:非破壊的変更の場合は、ブルー グリーンまたはカナリア リリースなどのデプロイ技術を使用します。 API の破壊的変更の場合は、新しいバージョンを以前のバージョンと並行してデプロイします。 そうすれば、以前の API を使用するサービスを、新しい API に対して更新してテストすることができます。 後述の「サービスの更新」を参照してください。
単一のリポジトリと複数のリポジトリの対比
CI/CD ワークフローを作成する前に、コード ベースがどのように構造化され、管理されるかを理解しておく必要があります。
- チームは、別個のリポジトリで作業するのか、単一のリポジトリで作業するのか。
- 使用するブランチ戦略は何か。
- 運用環境にコードをプッシュできるのは誰か。 リリース マネージャー ロールは存在するのか。
単一リポジトリ アプローチのほうが支持されていますが、どちらにも長所と短所があります。
単一のリポジトリ | 複数のリポジトリ | |
---|---|---|
長所 | コードの共有 コードとツールの標準化が容易 コードのリファクタリングが容易 探しやすさ - コードの単一のビュー |
各チームの所有権が明確 マージ競合の可能性が少ない マイクロサービスを強制的に分離するのに役立つ |
課題 | 共有コードの変更が複数のマイクロサービスに影響する可能性がある マージ競合の可能性が大きい 大規模なコード ベースに合うようにツールを拡張する必要がある アクセス制御 複雑なデプロイ プロセス |
コードの共有が難しい コーディング規約の適用が難しい 依存関係の管理 コード ベースが拡散して探しにくい 共有インフラストラクチャの欠如 |
サービスの更新
既に運用環境にあるサービスを更新する戦略は複数あります。 ここでは、一般的な 3 つのオプション (ローリング アップデート、ブルー グリーン デプロイ、カナリヤ リリース) について説明します。
ローリング アップデート
ローリング アップデートでは、サービスの新しいインスタンスをデプロイし、新しいインスタンスがただちに要求の受信を開始します。 新しいインスタンスが起動すると、前のインスタンスは削除されます。
例: Kubernetes では、ローリング アップデートが、Deployment のポッド仕様を更新するときの既定の動作です。 Deployment コントローラーは、更新されたポッドの新しい ReplicaSet を作成します。 次に、目的のレプリカ数を維持するために、古い ReplicaSet をスケールダウンしながら新しい ReplicaSet をスケールアップします。 古いポッドは、新しいポッドが準備されるまで削除されません。 Kubernetes は更新の履歴を保持するため、必要な場合は、更新をロールバックできます。
例: Azure Service Fabric では、既定でローリング アップデート戦略が使われます。 新機能はあっても、既存の API に対する変更はないサービスのバージョンをデプロイするには、この戦略が最適です。 Service Fabric では、アプリケーションの種類をノードのサブセットまたは更新ドメインに更新することによって、アップグレードのデプロイが開始されます。 その後、すべてのドメインがアップグレードされるまで、次の更新ドメインにロールフォワードされます。 アップグレード ドメインで更新が失敗した場合、すべてのドメインで、アプリケーションの種類が前のバージョンにロールバックされます。 複数のサービスが含まれるアプリケーションの種類では (および、すべてのサービスが 1 つのアップグレード デプロイの一部として更新される場合)、エラーが起きやすいことに注意してください。 1 つのサービスの更新が失敗した場合、アプリケーション全体が前のバージョンにロールバックされて、他のサービスは更新されません。
ローリング アップデートの 1 つの課題は、更新プロセス中は古いバージョンと新しいバージョンが混在して実行され、トラフィックを受信することです。 この期間中は、すべての要求が 2 つのバージョンのどちらにもルーティングされる可能性があります。
API の破壊的変更の場合は、以前のバージョンのクライアントがすべて更新されるまで、両方のバージョンを並行してサポートすることをお勧めします。 API のバージョン管理に関するセクションを参照してください。
ブルーグリーン デプロイ
ブルーグリーン デプロイでは、新しいバージョンを前のバージョンと並行してデプロイします。 新しいバージョンを検証した後、すべてのトラフィックを前のバージョンから新しいバージョンに一度に切り替えます。 切り替え後、アプリケーションを監視して問題があるかどうかを確認します。 問題が生じた場合は、古いバージョンに戻すことができます。 問題がないことを前提として、古いバージョンを削除できます。
従来のモノリシックまたは N 層アプリケーションでは、ブルーグリーン デプロイは、通常は、2 つの同一環境をプロビジョニングすることを意味します。 新しいバージョンをステージング環境にデプロイした後、クライアント トラフィックをステージング環境にリダイレクトします (たとえば、VIP アドレスをスワップします)。 マイクロサービス アーキテクチャでは、更新はマイクロサービス レベルで発生するため、通常、更新プログラムを同じ環境にデプロイし、サービスの検出機構を使用してスワップします。
例。 Kubernetes では、ブルーグリーン デプロイを実行するために別のクラスターをプロビジョニングする必要はありません。 代わりに、セレクターの利点を実行できます。 新しいポッド仕様と異なるラベル セットを使用して、新しい Deployment リソースを作成します。 このデプロイは、前のデプロイの削除や、それを指しているサービスの変更を行わずに作成します。 新しいポッドが実行されたら、新しいデプロイと一致するように、サービスのセレクターを更新できます。
ブルー グリーン デプロイの 1 つの欠点は、更新中は、サービスで 2 倍のポッド (現在のポッドと次のポッド) が実行されることです。 ポッドが大量の CPU またはメモリ リソースを必要とする場合は、リソースの消費量を処理するために、クラスターを一時的にスケールアウトする必要があります。
カナリア リリース
カナリア リリースでは、更新されたバージョンを少数のクライアントにロールアウトします。 その後、すべてのクライアントにロールアウトする前に、新しいサービスの動作を監視します。 これにより、制御された方法でロールアウトを時間をかけて実行し、実際のデータを観察し、すべてのユーザーが影響を受ける前に問題を見分けることができます。
カナリア リリースは、異なるバージョンのサービスに要求を動的にルーティングする必要があるため、ブルーグリーン デプロイやローリング アップデートよりも管理が複雑になります。
例。 Kubernetes では、2 つのレプリカ セット (バージョンごとに 1 つ) にまたがるように Service を構成し、レプリカの数を手動で調整できます。 ただし、この方法は、Kubernetes の負荷がポッド間に分散されるため、粒度が粗くなります。 たとえば、合計 10 個のレプリカがある場合、トラフィックのシフトは 10% の増分でのみ実行できます。 サービス メッシュを使用する場合は、サービス メッシュのルーティング規則を使用して、より高度なカナリア リリース戦略を実装できます。
次のステップ
- ラーニング パス: 継続的インテグレーションの定義と実装
- トレーニング: 継続的デリバリーの概要
- マイクロサービス アーキテクチャ
- マイクロサービスの手法を使用してアプリケーションを構築する理由は何ですか