infrastructure
microservices

microservicesに分割する際に注意するべき5つのこと

More than 1 year has passed since last update.

はじめに

マーティンファウラーがmicroservicesの記事で、小さな役割をもったサービス群にアプリケーションを分割することを提案しています。

cookpadが、サービスをマイクロサービス群に分割していることの記事が注目を浴びており、最近急速にバズワード化しているように感じます。

http://techlife.cookpad.com/entry/2014/09/08/093000

バズワード化して、ポイントが損なわれる前にいくつかの注意点をまとめておきます。

1.インフラコストは基本的に増大する

microservicesは、今まで単一のアプリケーションコードで行われていたことを複数のサービスサーバーに分割して管理・運営していくことです。ですので、プロセスを跨いだ通信が大量に発生します。その結果、サーバー台数は増大します。

つまり、インフラコストの増大と開発速度の高速化のコスト感覚をバランスして判断していく必要があります。疎結合性が高まり、アーキテクチャとしては美しく感じますが、実施には高度な経営レベルの判断が必要になります。

開発コストに関しては、以下に述べるような注意点がしっかりと設計されたときに効率化がはかられます。

2.グレイスフルデグラデーションをサービスレベルで意識する

http://ejje.weblio.jp/content/graceful+degradation
グレースフルデグラデーションは、一部の障害やエラーに際しても、サービス全体の停止ではなく、有効な部分だけ機能するように設計することです。microservicesへの分割によって、dev/opsチームの体制は中央集権ではいられなくなります。

microservicesに分割することで一サービスの障害という事象は頻繁におこることになるでしょう。その際に、アプリケーションのすべての機能が停止するのか、一部の機能のみが安全に切り離されるのかという設計が重要になります。

このとき、あるmicroservicesへのAPIリクエストをタイムアウトまで待ってしまうと、サービス全体のパフォーマンスが低下しかねません。特定のサービス障害時にリクエストをエラーとして即時に返すプロキシーを挟んでおき、ハートビートが停止したら設定を切り替えるというような仕組みが必要になるかもしれません。

あるいはDNSの拡張やconsulのようなサービスディスカバリに関わる装置を導入するのも手です。

いずれにしても、サービスの部分停止か完全停止かをプロダクトとしてどのように設計するべきかのコンセンサスが重要になるでしょう。

3.デプロイ順序や残留タスクにまつわる障害に気をつける

マーティンファウラーは、microservicesの構成要件にデプロイの自動化とメッセージキューやHTTPのAPIによる結合を挙げています。実際にサービスを分割して、運用する際に次のような注意が必要です。

単一のサービスをデプロイする際

あるサービスを変化させる際、そのサービスが担当しているメッセージキューの残留タスクに注意しましょう。デプロイ中の中途半端な状況、つまり古いサービスサーバと新しいサービスサーバが同居していても問題ないような設計になっているかを確認するようにしましょう。サービスサーバがBlue-Green Deploymentになっていて一瞬で切り替えられる場合でもメッセージキューの処理は混在する可能性があります。また、デプロイ時にキューの処理速度が低下しますので、サービスへの反映が遅延しても問題ないか確認する必要があります。

また、サービスを提供されたサーバが独自でキャッシュを持っている場合などにも不整合の可能性がありますので、注意深く設計しましょう。

複数のサービスをデプロイする際

サービスのアップデートに際して、複数のサービスが混在している場合、そのアップデートが同時に起こることを期待してはいけません。デプロイツールの実装次第ですが、そこにはタイムラグが発生します。

その結果、新しいメッセージを処理できないうちに送信してしまい、キューが詰まるというような事態も発生しかねません。エラーがどこに起因するか、HTTPエラーコードで言えば、40xか、50xなのかを判断したエラーハンドリングするか、サービスを受ける側を先にデプロイするということを意識する必要があります。

デプロイツール自体がmicroservices同士の依存関係木の変化を読み取って、適正にデプロイするようにするなど工夫を行うか、4で述べるようなサービスディスカバリの仕組みとAPIのバージョニングを行っていく必要があります。

4.サービスディスカバリ、API拡張規則、被呼び出し元管理を導入する

サービスディスカバリ

チームを分割して、各microservicesにドメインを分割すると、チーム間のコミュニケーションもモノリシックではいられなくなります。間のコミュニケーションコストを下げることもmicroservicesの目的の1つであるので、それらのサポートとして、APIのドキュメント管理やサービスの生死モニタリング、ディスカバリのための装置が必要になります。

APIのバージョニング、拡張規則

microservicesの提供するAPIは1つのコードベースが読むことのできないところで利用されるため、外部公開と同一レベルでも提供が必要になります。example.com/v1/apiのようにurlでバージョニングし、ドキュメントとしてフォーマットをxmlやjson schemaなどで管理し、サービスディスカバリに登録し、変更の際は一定期間deprecatedメッセージを出し、、、といった工夫が必要になるでしょう。

また、利用する側もオプショナルなパラーメタの追加に対して、エラーを起こさないようにする実装を義務づけたりといった注意が必要になります。(そういったテストスイートの自動生成も必要となるでしょう。)

さらに暗黙で束縛されるデータについても意識する必要があります。
microservicesに分割することで、ユーザのIPやUAなどの環境変数やHTTP Requestに付与されている情報を全く必要としない設計であれば問題ないのですが、往々にして必要になることがあります。

十分な情報を自動的に束縛しなくては、むやみに必要なAPIパラメータが増大してしまう可能性があります。

被呼び出し元管理とキャパシティプランニング

PaaSのようにquotaの管理も重要になります。どのサービスからどの程度呼び出されているかを把握する必要があります。アプリケーションサーバーなどのCPU資源や読み込みのためのDBスレーブの追加などは自動的に行うこともできるでしょうが、書き込みサーバの分割などはサービスコードの変更が必要になる場合があります。

突然、非常に多くのリクエストが別チームからやってきた場合、十分に処理できない可能性があり、スペックとしてのquotaをもうけておくことでチーム間のコミュニケーションを円滑に行うことができます。

5.ビジネスドメインとチーム毎にサービスを編成できるかを意識する

最初に紹介したcookpadの記事では、導入に「conwayの法則」が紹介されているなど、十分に意識されていたと思いますが、microservicesへの分解は、実のところ組織パターンの問題です。

ある程度大きなサービスになり、多くのメンバーが関わるようになると、領域別にチームが編成されることが多くなります。チーム間の調整コストが増大すると開発は遅滞しやすくなり、大きな変更をすることや継続的な改善を行うことが難しくなります。

組織的な解決策としては、ビジネスケーパビリティに通貫したチーム編成があげられます。このチーム編成に対するアーキテクチャ的な解決策が、microservicesという方法です。

そのため、microserviceへの分割とチーム編成が一致していなかったり、KPI設定が一致していない場合、これらのアーキテクチャ的方策は足かせになりかねません。経営と技術のつなぎ込みがしっかりできていなければ、なかなか難しいことだと言っていいでしょう。

おわり

このように見ていくと、大抵の組織ではすぐに適応できない高度なアプローチだと理解できるでしょう。
小さな組織の段階から導入するには、コストの方が高くついてしまう場合がありますので、

あきらめるにはまだ早い!ソースコードの品質向上に効果的なアプローチ

こちらの記事に、「射撃しつつ前進」的な漸進的アプローチをまとめてありますので、ご参考までに。