この記事はBob Reselmanによるゲスト投稿です。
マイクロサービスは分散システムのための新しいスタイルのアーキテクチャであり、Webスケールで運営する企業では広く受け入れられるようになっています。古いタイプのモノリシックなシステムでは現代の要求に合わせてスケールできないため、NetflixやAmazonなどの企業は、ソフトウェアをますます早いスピードでリリースするために、マイクロサービスを導入しています。
しかし、マイクロサービスアーキテクチャも、それをサポートするテストプラクティスがしっかりしていなければ、メリットを最大限に発揮できません。ほぼ即時リリースされるマイクロサービスのテストを設計する際は、マイクロレベルでもマクロレベルでも、新しい考え方が必要です。
マイクロサービス指向アプリケーションの3つのタイプ、テストの観点から検討すべき課題、そしてマイクロサービスのメリットを最大限にするためのテスト作成方法を見てみましょう。
マイクロサービスの基本
マイクロサービスのテスト設計の細かな面に入っていく前に、マイクロサービスとは何かについて触れておきましょう。マイクロサービスは粒度の高いソフトウェアコンポーネントで、意味的に明確な境界があり、他のデータ構造やデータソースから独立した固有のデータを維持します。マイクロサービスには固有のデプロイメントサイクルがあり、マイクロサービスの修正は、サービスが動作するアプリケーションドメイン内の他のマイクロサービスの動作を妨げることなくリリースできます。
モノリシックアプリケーションと比較しての利点
モノリシックアプリケーションと比較したときの違いについても見ておきましょう。下の図 1 は、典型的なモノリシックアプリケーションの例を示しています。
Customers、Products、Orders、Commentsコンポーネントは、JavaやC#などのオブジェクト指向言語ではクラスのセットとして実装できます。たとえばcustomersはcustomerオブジェクトの配列、productsはproductオブジェクトの配列になります。orderオブジェクトはcustomerオブジェクトとproductオブジェクトの両方を使用する可能性があります。あるいは、モノリシックアプリケーションのすべてのコンポーネントは同じデータベースを使用するため、orderオブジェクトは直接データベースにアクセスして、タスクの実行に必要なproductおよびorder情報を取得するかもしれません。データベースへの直接アクセスは可能であるだけではなく—オブジェクト関係マッピングベースのオブジェクト指向プログラミングの精神にも反しますが—実際によく行われます。とにかく機能をリリースしなければならないという要求が大きい企業では、アーキテクチャは緊密に結合される傾向が顕著です。結果として、時に脆弱なシステムが生まれます。
モノリシックアプリケーションのリリース
アプリケーションの新しいバージョンをリリースするのに、関係するすべての開発チームにわたる高度な調整が必要になります。緊密な結合の影響として、リリースサイクルは依存関係にあるコンポーネントの中で最も修正作業が遅いコンポーネントより短くできません。つまり、productsの新機能とcustomersの新機能を含む修正をリリースする予定の場合、productsとcustomersの両方の用意ができるまでリリースできません。productsは1日でアップグレードできても、customersでは3週間かかる場合、リリースに3週間かかります。さらに、問題をより複雑にするのは、リリースプロセスでproductsとcustomersの新しいコードの同期をとる必要があるだけでなく、データベースのスキーマにも変更がある可能性があり、それもリリース管理プロセスに含める必要があるという点です。
データベースの変更のリリースは、非常に注意を要する作業です。データベースの構造を変更すると、意図しない副作用のリスクがあります。繰り返しになりますが、任意のコンポーネントがデータベースにアクセスできるなら、そのコンポーネントはデータベースにアクセスするだけでなく、コンポーネントが関わる領域外のデータに不用意にアクセスしていることがあります。そのような対象領域外での「不用意なアクセス」が、他のコンポーネントの破損を引き起こし、大問題となるまで気づかれない可能性があります。潜在的な問題は、コードが運用環境にリリースされるまで気づかれない可能性もあります。残念ながら、このようなことは頻繁に起きています。
モノリシックなアプリケーションに伴う長いリリースサイクルを許容できる企業もあります。しかし、何十万というユーザーをサポートし、何千ものコンポーネントがある場合、一番遅いコンポーネントにリリースを合わせるというのは許容できません。
マイクロサービス指向アプリケーション
マイクロサービス指向アプリケーション(microservices-oriented application)では、コンポーネントは(合理的な範囲で)できる限り小さな機能に分解されます。分解された各機能領域がマイクロサービスとして表現されます。企業にはそれぞれ対処すべき固有の技術的レガシーと文化があるため、分解に際して従うべき決定的なシナリオというものはありません。
既存のモノリシックなアプリケーションをマイクロサービス指向アプリケーションに分解する際の秘訣は、完璧を目指しすぎないことです。各マイクロサービスが意味的な境界に従って構成され、固有のデータと固有のリリースサイクルを保持するようにします。実装をどの程度徹底するかは、利用できる時間、専門知識、リソースに依存します。単一の関数からなるマイクロサービスもあれば、多くの関数を持つマイクロサービスもあるでしょう。
マイクロサービス指向アプリケーションには3つのタイプがあります。同期型、非同期型、そしてハイブリッド型です。
同期型マイクロサービス指向アプリケーション
次の図 2 は同期型マイクロサービス指向アプリケーションを表しています。サービス間の通信は、Web上のHTTP通信で一般的なリクエスト-レスポンスパターンを使用して行われます。
同期型マイクロサービス指向アプリケーションには次の特徴があります。
- 個々のマイクロサービスは、マイクロサービスのロジックへのアクセスを提供するHTTPサーバーの背後でセグメント化される
- 各マイクロサービスは、それ自体の対象領域だけに関わる
- 他のマイクロサービスのインターフェイスは認識するが、他のサービスの内部ロジックはわからない
- マイクロサービスがアクセスできるデータは⾃⾝のデータだけである
- 他のマイクロサービスのデータストアにはアクセスできない
- データべースへの直接アクセスによって、他のマイクロサービスのデータを「ハイジャック」することは不可能
- マイクロサービス間でデータをやりとりする唯⼀の⽅法は、マイクロサービスのパブリックなインターフェイスを経由して相互通信することである
独立性
同期的なマイクロサービス指向アプリケーションには独立性という利点があります。たとえば、上の図のCustomersマイクロサービスはいつでも独立してアップグレードできます。考慮すべき外部的な依存関係はありません。マイクロサービスのパブリックなインターフェイスに変更がなく、リクエストとして消費するデータやレスポンスとして返すデータの構造が変わらない限り、マイクロサービス指向アプリケーション全体が壊れるリスクは最小限です。
構造的整合性
マイクロサービス指向アプリケーションアーキテクチャそのものが、構造的整合性を自己強化する性質を持っています。マイクロサービスは固有のデータを保持するため、他のサービスのデータストアは影響を受けません。また、マイクロサービスはHTTP URLとして提示され、リクエスト-レスポンスデータ構造に関連付けられるため、インターフェイス境界が明確です。
同期型マイクロサービス指向アプリケーションは非常に一般的になってきています。多くの同期型マイクロサービス指向アプリケーションのインターフェイスは、2000年からあるREST形式に基づいています。このように一般的な同期型マイクロサービス指向アプリケーションですが、デメリットもあります。スピードです。同期型マイクロサービスのコンシューマーはそのマイクロサービスのリクエストおよびレスポンス時間よりも速く動作できません。これは、特に実行に時間がかかるプロセスがあるマイクロサービスでは障害になります。たとえば、テラバイト級のデータを消費して処理する複雑な解析サービスなどです。解析が終わるのを何分もじっと待っていられるコンシューマーはほとんどいません。マイクロサービスにやるべきことを伝え、結果が準備できたときに通知を受け取るほうがよいでしょう。
このような場合、要望を簡単に満たす方法として、非同期型アプローチのマイクロサービス設計が適しています。
非同期型マイクロサービス指向アプリケーション
次の図 3 は、マイクロサービス指向アプリケーションの非同期型実装を示しています。
非同期型マイクロサービス指向アプリケーションには次の特徴があります。
- サービス間の通信は、関係者間でのメッセージ交換として実現される
- 「開始して放置(fire and forget)」と表現されることがある
- メッセージは随時、あるいは特定のイベントへのレスポンスとして⽣成される
Ordersマイクロサービスで注文が作成されると、サービスは関連するメッセージキューにorders_createdメッセージをパブリッシュし、そのメッセージキューでは別のマイクロサービスであるbilling(図中にはなし)が受信メッセージを待機しています。billingマイクロサービスは注文に関する情報が含まれたメッセージをピックアップし、billingの対象領域に応じた方法でメッセージを処理します。
マイクロサービス指向アプリケーションのメリットとデメリット
マイクロサービス指向アプリケーションの設計で非同期型アプローチを採用する利点は、ボトルネックを避けられること、したがって効率性が高いことです。デメリットは、作成と管理が非常に複雑であることです。
Uberなどの大規模な非同期型システムでは、1秒間に何十万というメッセージを処理することも稀ではありません。プロセスのワークフローへの直接的なパスがないため、このようなシステムをデバッグするのは難しくなります。これを実行してからあれを実行する、というようなものではありません。ロジックは受信したメッセージに従って実行されます。つまり、いつ何が起こってもおかしくありません。
テスト戦略を立てるときは、このことを忘れないようにするべきです。たとえば、非同期型システムでは1対1のリクエスト-レスポンスアクティビティはないため、リクエスト-レスポンス時間でパフォーマンスを測るのは現実的ではありません。
ハイブリッド型マイクロサービス指向アプリケーション
マイクロサービス指向アプリケーションを実装するバランスの取れた方法は、ハイブリッドアプローチを採用することです。ハイブリッド型マイクロサービス指向アプリケーションは、以下のような面で同期的であり、非同期的であります。
- 同期的:サービス間の直接的なリクエスト-レスポンス通信をサポートする
- 非同期的:同期化されたデータ交換の中で、あるいはその結果としてメッセージが⽣成される
図 4 はマイクロサービス指向アプリケーションの設計でハイブリッドアプローチを採用したときに行われる通信パターンを表しています。CustomersマイクロサービスはHTTPサーバーにバインドされたURLとしても、メッセージブローカーのキューとしても表されます。
標準的なHTTPリクエスト-レスポンス通信を使用してマイクロサービスに顧客を追加できます。リクエストが受信されて処理され、レスポンスが生成されます。ただし、レスポンスが生成される前に、新しい顧客情報を含むメッセージが関連するメッセージキューにパブリッシュされ、他の関係サービスがメッセージを消費します。メッセージキューに送信される情報は、HTTPレスポンスで生成されるものと同じ場合もあれば、HTTPレスポンスに含まれる情報とは異なる情報がメッセージキューに送信される場合もあります。すべてはマイクロサービスの設計とマイクロサービスがサポートするべきサービスレベルしだいです。
GraphQLは、コンシューマーとサービス間の同期通信と非同期通信の両方をサポートするAPI技術です。GraphQLの詳細は、こちらでGraphQLサブスクリプションについて参照してください。
同期型と非同期型の良いところを取ることができるというのは、ハイブリッドアプローチの重要なメリットです。しかし、デメリットもあります。特に、パフォーマンスのボトルネックの可能性や、マイクロサービスとの送受信でまったく異なる2つのタイプのインターフェイスをサポートすることから複雑さが増すという点などです。
マイクロサービスのテスト設計の課題
このようなアプリケーションをテストするうえで、まず念頭に置くべきは、1つのマイクロサービスがさまざまなマイクロサービス指向アプリケーションで共有されることはまれだということです。通常、マイクロサービスは特定のアプリケーションドメインに含まれる多数のうちの1つです。アプリケーションアーキテクチャ設計におけるマイクロサービス指向アプローチの利点は、要求の厳しいコンシューマーにより速いスピードでコードを提供できることです(Netflixは1日に4,000以上のデプロイメントを行っています)。このようなリリース速度は、従来型のモノリシックアプリケーションでは単純に不可能です。
もう1つ忘れてはならないこととして、マイクロサービス指向アプリケーションのテストはマイクロレベルとマクロレベルの両方で行う必要があります。
マイクロレベルのテスト
マイクロレベルでは、各サービスはそれぞれの対象領域の中で徹底的にテストされる必要があります。界隈によっては、マイクロサービスの境界は関数であるとみなされます。
典型的な単体テストを超えて
マイクロサービスは単一の関数として表現されるべきたと唱えるサーバーレスムーブメントの台頭を考慮すると、マイクロサービス内の関数に注目することは合理的です。しかし、多くのテスト実践者の間では、そのようなテストは単体テストに限られる傾向があります。単体テストは、非常に限定されたテスト条件下で実行される単一の関数に対しては有効ですが、マイクロサービスはWebスケールで実行されるものです。そのため、極限的なテスト条件も必要になります。
たとえば、十分なマイクロレベルのテストには、何十万というマイクロサービスのインスタンスを同時に実行し、このようなスケールでの動作を観察することも含まれるかもしれません。1度に1つの関数の1つの単体テストを実行するだけでは不十分です。指定されたホスト環境に従って同時実行される関数の千個のインスタンスで単体テストを実行する必要があります。
ここで覚えておくべきことは、マイクロサービス指向アプリケーションでは1つの関数が千個のインスタンスで実行されることも普通だということです。それに応じて計画しましょう。
デプロイメントユニットのテスト
マイクロサービスの機能のテストに加えて、マイクロサービスがリリースされるデプロイメントユニットのテストについても検討する必要があります。マイクロサービスはKubernetesやDocker Swarmなどのオーケストレーション技術の一部をなすコンテナーとしてデプロイされるのが普通です。コンテナーオーケストレーションの重要な側面として、マイクロサービスの永続性の確保があります。
マイクロサービスはさまざまな理由で失敗することが予想されます。たとえば、ホストがダウンした、マイクロサービスが誤動作したなどです。何千ものコンテナーが実行されている最中にエラーが発生することも珍しくありません。テストとの関連でいえば、マイクロサービスの「終了」動作が通常の動作と同様に重要です。マイクロレベルのテストでは、大規模な実行でも、マイクロサービスがグレースフルに開始され、グレースフルに終了することを確認する必要があります。
詳細なログ記録が有効であることを確認する
テストでは、マイクロサービス内で起こる重要なイベントがすべて意味のある方法でログ記録されること—さらに重要な点としては、ログエントリが意味をなすことを検証する必要もあります。マイクロサービスの世界では、特に実行がシーケンシャルではない非同期型マイクロサービス指向アプリケーションでは、ログエントリは重要です。アプリケーション内で何が起きているかを知る唯一の材料がログデータであることもまれではありません。
マクロレベルのテスト
同期型でも非同期型でもハイブリッドでも、徹底したマイクロサービスのテスト体制を運用する必要があります。マイクロサービスをマクロレベルでテストする場合、サービス間の通信、そしてデプロイメントプロセスという2つの面が期待どおり動作していることを確認しなければなりません。
確実なサービス間通信の確保
その定義から言って、マイクロサービスは互いに独立しているものです。マイクロサービスは、取得した情報によって互いが行う処理を知ります。したがって、サービス間通信の正確さはマイクロサービス指向アプリケーションの動作にとって重要です。
サービス間通信の検証は、正しい情報が期待どおりやりとりされるかどうかの確認を意味します。テストでは、メッセージの交換と処理を観察する必要があります。これは、HTTPリクエスト-レスポンス通信にも、メッセージブローカーを使用して分散化された非同期メッセージにも当てはまります。いわゆる「ハッピーパス」のメッセージフォーマットがサポートされ、誤ったフォーマットのメッセージが適切な方法で(単に「不正なメッセージ」エラーを出すのではなく)却下されることを確認します。
CI/CDプロセスのテスト
どのようなソフトウェア開発パラダイムにとっても、健全な継続的インテグレーションおよび継続的デリバリー(CI/CD)プロセスは重要ですが、とくにマイクロサービス指向アプリケーションでは、効率的で正確かつ迅速なCI/CDプロセスが不可欠です。マイクロサービス指向アプリケーションの修正サイクルは1日に千回のアップデートというレベルになることもあるため、ビルドが遅いマイクロサービスが1つあるだけで、リリースプロセス全体を妨げるボトルネックになる可能性があります。
これを予防する最善の方策は、他のハイレベルでのテストと同じ優先度でCI/CDパイプラインのテストを行うことです。マイクロサービスコード成果物のビルドが遅い、マイクロサービスをホストするランタイム環境のプロビジョニングが遅い、デプロイされたマイクロサービスの立ち上がりが遅いなどの問題を検出し、対処することは、CI/CDパイプラインの健全性を確保するうえで重要です。高度なエンジニアリングを駆使したマイクロサービスも、すばやくデプロイメントユニットを取得し、運用できなければ役に立ちません。
リリース対象のアイテムのテストと同等の労力をリリースプロセスのテストに費やすべきです。簡単に言えば、CI/CDパイプラインのテストにCI/CDがデプロイするアプリケーションのテストと同じ優先順位を割り当てるべきです。
まとめ
マイクロサービスはゲームの流れを変える重要な要素です。マイクロサービス指向アプリケーションは、新しい機能をほぼ即時的にオンラインにリリースするために必要な柔軟性とスピードを提供します。何百万ものユーザーをサポートする大企業は、すでにこのことに気付いていますし、アプリケーションがウェブスケールに移行するにつれ、日々さらに多くの企業がマイクロサービスアーキテクチャを採用しています。
しかし、企業は真剣に開発にマイクロサービス指向アプリケーションの精神を取り入れようとしているにもかかわらず、多くの場合、従来モノリシックアプリケーションをテストする際に行っていたプラクティスを使用してマイクロサービス指向アプリケーションをテストしようとしています。これは近眼的なアプローチです。
そうではなく、マイクロサービスの独立性と、マイクロサービスが使用されるアプリケーションの変遷パターンに合わせた最新のテスト技術をと入りれる必要があります。マイクロサービス指向アプリケーション設計の背後にある原則を開発チームとテストチームがともに了解すれば、企業は市場でより良い位置を占め、マイクロサービスがもたらす利点を享受できるでしょう。
筆者のBob Reselmanについて:全国的に著名なソフトウェア開発者、システムアーキテクト、業界アナリスト、テクニカルライター/ジャーナリスト。コンピュータープログラミングに関する書籍のほか、ソフトウェア開発テクノロジーおよびテクニック、ソフトウェア開発カルチャーに関する記事を多数執筆。元Cap GeminiのPrincipal ConsultantおよびコンピューターメーカーGatewayのPlatform Architect。ロサンゼルス在住。現在は、ソフトウェア開発およびテスト活動のかたわら、自動化が雇用に与える影響に関する書籍を執筆中。連絡はLinkedInwww.linkedin.com/in/bobreselmanまで。
(この記事は、開発元Gurock社の Blog 「Testing Microservices-Oriented Applications」2020年1月28日の翻訳記事です。)
関連する製品
テスト管理ツール TestRail
テスト管理をシンプルにするWebベーステスト管理ツールのトップブランド
Webベースのテスト管理ツールのトップブランドであるTestRail。テストケースの管理やテスト結果の記録、チームでの情報共有、REST APIを用いた外部ツールとの連携など、テスト活動の本質的ではない作業に時間を取られていませんか?TestRailはシンプルで使いやすいUIを提供し、テストにかかるさまざまな管理コストの削減に貢献します。
API テスト自動化ツール・サービス仮想化ツール SOAtest/Virtualize
APIのテスト自動化とサービス仮想化を1ツールで
API の開発者/利用者に向けてテストの自動化とテスト環境の仮想化の2つの側面から開発を効率化します。SOAtest/Virtualize は、API のテストドライバーを提供し、開発中の API のテストを自動化する機能と、API を利用するアプリケーションが必要とする API を高性能なスタブとして仮想化する機能を同梱して提供します。