BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル ブルー・グリーンデプロイメントの現場から

ブルー・グリーンデプロイメントの現場から

キーポイント

  • ブルーグリーンデプロイメントでのバージョン変更の取り扱いには、計画と優れたアーキテクチャ選択が必要です。
  • バージョン変更を扱う方法の一つは依存関係の順序をリリースに割り当てることですが、これによってリリースが複雑化する可能性があります。
  • 理想的にはマイクロサービスアーキテクチャでのブルーグリーンデプロイメントでは、APIバージョン管理を使用して正しいバージョンへのリクエストのルーティングを保証します。
  • ブルーグリーンリリースをサポートしていないアーキテクチャに追加することは、もっと複雑で脆弱なデプロイメントにつながる可能性があります。
  • マイクロサービスアーキテクチャはメリットがありますが、デプロイメントをより複雑にする可能性があります。必要でない場合は、モノリシックアプローチがより簡単にブルーグリーンリリースを実現できるかもしれません。

先週、私はチームのGitLabプロジェクトに対するマージリクエストの承認を保留しました。

なぜって?彼らが提案している"ブルーグリーン・リリース"をサポートするためのアプリケーション変更に、私は気が進まなかったからです。

私はこの案のせいでデプロイメントとコードが結びついて、特定の環境に依存することが気になりました。また、変更することで新たなバグやエラーが発生するのも懸念点でした。

この状況は、より頻繁に変更をプロダクション環境に導入することを目的とした改善リリースの意図から起こったものです。

私たちのチームのアプリケーションは、Dockerコンテナでホスティングされ、自動的にクラウドにデプロイされます。変更時にはユニットテストやコンポーネントテストが実行され、全てのテストが合格したら、自動的にデプロイメントが続行されます。複数のサービスの成果物をタグづけして、クラウド環境にデプロイすることを「リリース」と呼んでいます。

しかし成果物をプロダクション(または準プロダクション)環境に移動するためには、すべてのサービスを再起動するためのダウンタイムが必要で、通常の営業時間外にスケジュールすることになります。リリースは別のチームによって実行され、特定の種類の更新をするためには手動での作業も必要です。これらのリリースイベントは稀ですし、チームにとっては苦痛を伴います。また、見通しのない労働時間も要求されます。このプロセスは改善の余地があり、ブルーグリーンリリースを採用することで、通常の営業時間外の作業やダウンタイムの要件を削除できます。

ブルーグリーンデプロイメントという概念は、要約すると常に(少なくとも)2つのアプリケーションインスタンスが実行されることを意味しています。新しいバージョンがリリースされたとき一方のインスタンスにのみリリースされ、残りのインスタンスは旧バージョンで動作します。最初は新しいバージョンへのアクセスは完全に制限され、徐々に一部のユーザーに公開されて新しいリリースの信頼性が確保されます。この時点で、旧バージョンを実行するインスタンスへのアクセスは徐々に制限され、いずれこれらもアップグレードされます。こうしてユーザにとってダウンタイムのないリリースを実現します。

もちろん、注意すべき点もあります。データソースやAPIに対する変更がある場合、古いリクエストは新しいバージョンで処理されず、ブルーグリーンリリースは不可能となります。 「誰かが素晴らしい解決策を思いついたとき、ブルーグリーン環境における破壊的な変更にどのようにアプローチすればよい?」これは私の好きなインタビューの質問の一つです。そして、おそらく「古い」リクエストを「新しい」システムに適合させるための独自のルーティングレイヤーでもって解決するのでしょう。その時点で、古き良きダウンタイムがあった方が良いのではないか、と考える必要があります。ほとんどのソフトウェアチームは壊れた変更を避けるために最善を尽くしますが、しばしば避けられません。そこで最善の事態を想定して、壊れた変更がないと仮定しましょう。私のプロジェクトと同じようにDockerコンテナをクラウドサービスに直接デプロイすることを仮定します- 自動スケーリングとルーティングをサポートし,PaaSレイヤーであるKubernetesなどではなく,Azure App Serviceに直接デプロイするのです。

ではどのように進めましょうか。

私たちのアーキテクチャは、REST APIを介して通信する複数のマイクロサービスから構成されていますが、現在はすべての成果物が1つのGitリポジトリに格納され、単一のリリースとして一緒にデプロイされています。つまりAマイクロサービスとBマイクロサービスがバージョン1.0で実行されており、Aに新しいインターフェイスが含まれたリリース(バージョン2.0)があり、これがBの新しいメソッドによって呼び出されます。さらにAの2つのインスタンスとBの2つのインスタンスがロードバランスされたプロダクション環境にデプロイされていると仮定します。ブルーグリーンの方法では各インスタンス1つずつが新しいリリースに移行することになります。

バージョン2.0のBのインスタンスは、バージョン2.0のAのインスタンスしか呼び出せないのです。もし、バージョン1.0のエンドポイントに誘導されると、必要な新機能を見つけることができません。この特定のルーティング要件のため、サービスBは、サービスAを呼び出すためにサービスディスカバリーから拾ったロードバランスされたエンドポイントを使用できず、代わりに特定の「グリーン」インスタンスのアドレスが必要となります。

私たちはまさにこのようなシナリオに直面しました。

考えられる解決策を見てみましょう。

1. 依存関係の順番にリリースする

APIを呼び出す機能よりも前にAPIをリリースします。上記の場合、マイクロサービスBのブルーグリーンリリースして問題がないことを確認し、マイクロサービスBの両方のインスタンスをバージョン2.0に移行した後、マイクロサービスAのブルーグリーンリリースができます。

この方法は、APIを少しずつ変更して破壊的な影響を避けることができますが、依存関係があるために幾回ものリリースが必要になります。このためシステムのバージョン追跡は難しくなります。マイクロサービスアーキテクチャはシステムの一部だけがリソースを必要とする場合には、システム全体をスケールすることなくその一部だけをスケールできますが、各部分のライフサイクルを個別に管理する必要があります。

このモデルは、インクリメンタルで破壊的でないAPIの変更に対してはシンプルで良い方法ですが、次のサービスをリリースする前にそれぞれの依存関係を整えるためにリリース回数は多くなります。またタグ付けされたリリースが複数のマイクロサービスのバージョンにまたがっている場合など各マイクロサービスがどのバージョンで動いているのか把握が難しいという欠点があります。でもこれはマイクロサービスとのトレードオフですし、デプロイの複雑さと計算効率との兼ね合いです。マイクロサービス・アーキテクチャでは、システムの一部がより多くのリソースを必要とする場合、システム全体をスケールするのではなくその一部だけを水平方向にスケールさせることができますが、そのためにもすべての部分のライフサイクルを個別に管理する必要があるのです。

2. APIコールをバージョン管理する

API呼び出しにバージョンを導入するにはいくつかの方法があります。ひとつはURLにバージョンを直接追加する方法です。もう一つはHTTPヘッダーなどのメタデータを使ってバージョンを表現する方法ですが、これはすべてのサービスを制御している内部サービス間の通信にのみ有効です。そうでない場合は、サービスリクエストにバージョン情報を含めることを要求できません。

APIエンドポイントがバージョン管理されていると、それはリリースにどのように役立つでしょうか?

サービスBのバージョン2.0が、バージョン1.0のサービスBのインスタンスにバージョン2.0のリクエストを送った場合にHTTP 404「URLが見つかりません」の応答を管理できるようになります。またサービスAはバージョン1.0とバージョン2.0のエンドポイントの両方をホストできるため、前のバージョンがまだ生存している間もサービスを続けることができます。このためすべてのサービスが移行された後、サービスBのバージョン1.0のコードを管理しクリーンアップする作業が発生します。

3. インフラストラクチャに頼る

これはクラウドベースのソリューションです。チームはアプリケーションをAzureにデプロイします。Azureにブルーグリーンリリースについて尋ねたら、彼らはAzure Traffic Manager製品を示すでしょう。これはDNSベースのロードバランサーで、重み付けされたラウンドロビン方式のルーティングを提供します。重み付けを使って新しく移行するサーバに徐々に負荷を与えることができます。またブルーサーバーが他のブルーサーバーにのみルーティングするようにするルールを追加可能でブルーとグリーンの環境を分離しておくことができます。この方法にはコストがかかりますが、けっして高いものではありません。

具体的な問題に戻りましょう。

私たちはバージョン付きのAPIを構築していないと言いました。さらに私たちは現在すべてのマイクロサービスを1回のリリースでデプロイしています。私たちのサービスをマイクロサービスと呼ぶ理由は、別々にデプロイおよびスケーリングできるからですが、私たちのリリースプロセスは効果的にBBOM(マイクロサービスの大きな泥だんご)にマージしています。

第三のオプションでは、Azure Traffic Manager(これは高すぎると判断されました)がない場合、チームはブルーフロントエンドがバックエンドのマイクロサービスにリクエストを送るときに、ブルーバックエンドが呼ばれることを確認または強制する方法がありませんでした。このためバックエンドから変更を最初に伝播する(常に可能ではなく特にブルーとグリーンが同じデータベースを共有する場合)以外は処理できないリクエストをルーティングするリスクがありました。私がひどく悩んで打った回避策は、ブルーまたはグリーンに設定できる設定変数を含め、フロントエンドからのリクエストにHTTPヘッダを設定して、アプリケーションコードベースでAzure Traffic Managerの機能を再現することでした。あーあ。

このコードは、ルーティングURLを生成する際に、グリーンサーバーまたはブルーサーバーを経由するかどうかを決定するためのフラグとしてHTTPヘッダー/設定変数を使用できます。たとえば、「ログアウト」リンクはフロントエンドの設定にグリーンとブルーの2つの設定変数が指定されています。これにより、サーバーの「色」に応じて異なるログアウトリンクが生成されます。え?これまだ説明したほうが良い…ですか?

私たちのチームはこれがブルーグリーンリリースの作成方法としては最悪だと知っていましたが、予算のプレッシャーと時間のプレッシャーという、例の2つの悪魔に無理強いされました。要求はAzureのクラウドネイティブサービスを使用せずに、1 ヶ月以内にブルーグリーンデプロイプロセスを作成することで、私たちの出発点からすると選択肢は非常に限られていました。しかし、もっと早く分かっているべきだった...例えば、APIを構築すると分かった時点で、APIのバージョン管理を検討する必要があったのです。

私たちは「DevOpsのギャップ」に陥りました。開発チームとWebOpsチームの2つのチームが異なる優先順位を持っていたからです。開発チームの優先事項は変更をリリースパイプラインにできるだけ早く入れることであり、WebOpsチームの優先事項はクラウドプラットフォームの再現性とセキュリティを保証することです。マイクロサービスを構築するよう要請があったとき、開発チームはWebOpsチームもブルーグリーンデプロイメントを最優先で管理するだろうと思い込んで、それをサポートするために自分たちのソリューションをどのように構築すべきかを検討し続けました。この見落としが、最終的に私たちを苦しめることになりました。

では、現在はどうなっているのでしょうか?予想通り私たちが構築したプロセスを使おうとするとかなり厄介なルーティングのバグが発見されます。最終的には Azure Traffic Managerを使うことになるでしょう。そこから「マイクロサービスの大きな玉」を複数のデプロイメント・パイプラインに分解し、新しい変更のボトムアップ・リリースを計画できるようにします。最初のリリースでは、サービスAをバージョン2.0にアップグレードしてAPIとデータベースで新しいエンドポイントフィールドを利用できるようにし、次に2番目のリリースでサービスBを更新してサービスAの新しいエンドポイントを呼び出すようにします。

開発者とWebOpsチームの距離を縮め、リリースチームとより緊密に連携し、私たちがどのように彼らを支援できるかを理解することは、私たちにとって非常に貴重な学習プロセスでした。スキルセットが異なる場合他の誰かに属すると仮定したタスクを委任するのは自然なことです(たとえば、アプリケーション・インスタンスのロードバランシングは、Azureクラウドのコンセプトとインフラをコードとして記述するさまざまなテンプレート言語を理解している人に委任されるでしょう)。

学んだこと

私たちがブルーグリーンセットアップの初期段階の試みにおいて学んだことをまとめます。

変化のためのアーキテクト

私はアプリケーションの「未来に対応する」アプリケーションに大反対です。もし性能に問題がなければ、キャッシュを作らなくていい。もしコンテンツを削除する必要がないのであれば、削除機能を実装する必要はないでしょう。予想は往々にして当たらないものです。しかしそのような将来的な変更を最初から可能かつ簡単にできるようにすべきです。つまり、アプリケーション全体の設計をする際に、データベースレベルでどのように変更を加えるか、APIにどのようなバージョンを追加するかなどを検討する必要があるということです。

マイクロサービスのためのマイクロサービスを作らない

アーキテクチャにピンチポイントがない場合、他のポイントよりも高負荷になりそうなポイントがない場合、また、コンポーネント同士が通信し、同じ程度の場所(同じクラウド、または同じデータセンターなど)にデプロイされる場合、マイクロサービスアーキテクチャから多くのメリットを得ることはありません。

それよりも可動部品の数を減らし、コンポーネント呼び出し間のネットワーク遅延を減らすことで、デプロイメントを簡素化する方が得策かもしれません。ただ流行に流されるのではなく何を実現しようとしているのか、よく考えてみてください。

チームの境界線に注意する

それぞれのチームが一緒に働いている場合、UXデザイナーや開発者、ビジネスアナリストやQA、開発者やオペレーションチームなど、チーム間のインタフェースがプロジェクトのリスクが高い場所であることを認識しましょう。

例えば、開発者はUXデザイナーが有効なHTMLプロトタイプを提供していると仮定し、ビジネスアナリストはQAチームが文書化された要件に基づいて自動テストを行ったと仮定し、運用チームはアプリケーションの依存性を通知されたと仮定します。例えば、ドメイン駆動設計のツールを使って、イベント・ストームのイベント・ワークショップを実施できます。

プロジェクトの早い段階で、これらの前提をリスク領域として提起すればするほど、物事はより良く、より安全になるのです。

 

著者について

サラ・サンダースは、20年以上のアプリケーション開発経験と10年以上の開発プロジェクトのコンサルティングを経験。ジャック・オブ・オール・トレードであり、Javaのバックグラウンドがあり、効率的なクラウドアーキテクチャに特化しています。彼女はソフトウェアアーキテクチャを包括的に取り上げ、年々進化するパターンやトレンドを見つけるのが大好きです。彼女はCapgeminiのレギュラーブロガーであり、世界各地のコンファレンスでスピーカーとして活躍しています。

作者について

この記事に星をつける

おすすめ度
スタイル

BT