実践マイクロサービス ─ コンポーネント分割やトラブル回避の考え方をLINEの導入事例に学ぶ

マイクロサービスとは、小さいサービス同士をつないで連携し、ひとつのサービスを構成する手法のことです。そのメリット・デメリットなどマイクロサービスの本質と、LINEでの導入事例から実運用とトラブル回避について、LINE Shopチームの佐藤春旗さんが解説します。

実践マイクロサービス ─ コンポーネント分割やトラブル回避の考え方をLINEの導入事例に学ぶ

はじめまして、佐藤春旗です。

前職でソーシャルサービスやOSの開発を経て、2013年にLINEに入社。スタンプショップやLINE Payの開発に参加したのち、現在はスタンプショップなどを担当するLINE Shopのチームに所属し、マネージャーを務めています。

本稿では、マイクロサービスを軸に、2つのテーマを取り上げていきます。

1つ目は、マイクロサービスの概要解説です。あわせて、実際に運用して見えたメリット・デメリットを紹介しながら、マイクロサービスの本質を探っていきます。

もう1つのテーマは、「いかにマイクロサービス実運用を考えるか」です。LINEでの導入事例のほか、マイクロサービスで起こりやすいトラブルと解決方法を紹介していきますので、実際にマイクロサービスの導入を考える方の参考になれば幸いです。

マイクロサービスの基本的な考え方

そもそもマイクロサービスとはどういうものなのでしょうか? ここでは、個々のサービスとしてのマイクロサービスではなく、アーキテクチャとしてのマイクロサービスについてお話したいと思います。

マイクロサービスとは、Web APIなどのRPC(Remote Procedure Call、遠隔手続き呼び出し)を通じて、小さいサービス同士をつないで連携し、ひとつのサービスを構成する手法のことです。

マイクロサービスを知るには、対義語のモノリシック(monolithic)アプリケーションと比較すると、分かりやすいかもしれません。

モノリシックなサービスでは、1つのアプリケーションに対してすべての機能が開発されます。複数のアプリケーションを開発する場合でも、認証サービスやデータベース、アクセス許可などのコンポーネントもそれぞれのアプリケーションの中で開発が必要になります。

対してマイクロサービスでは、これらを共通コンポーネント化し、APIで呼び出すというアプローチをとることが可能です。

1

すべての機能が特定のアプリケーション向けに開発されるモノリシックと、機能を適宜APIで呼び出し連携させるマイクロサービス

マイクロサービスアーキテクチャの基本的な思想は、

  • サービスを構成する各要素を疎結合に構成して連携する
  • 各要素に適した技術を用いる
  • サービス間の会話はAPIなど決められた様式に従う

という考えのもと、大きな規模のサービスを分割して、小さなコンポーネントにする設計をしようというものです。

これらの考え方は、SOA(Service Oriented Architecture、サービス指向アーキテクチャ)などの形で、以前から存在していたと思います。規模の大きなサービスを作ろうとするときも、似たような考えで構築することが多いのではないでしょうか。

今から振り返ると、私がLINEに入社した2013年は、マイクロサービスアーキテクチャを構成するための技術が徐々に出そろってきた時期になります。

Apache Thriftや、Protocol BuffersとgRPCなど、IDL・シリアライズフォーマット・RPCフレームワークが存在し、またその後にはAWS Lambdaに代表されるサーバーレス・コンピューティングのコンセプトが生まれています。

そして、それ以前からも、ビジネスやロジックの複雑化と、トラフィックやユーザー数などの大規模化に伴うスケーラビリティの必要性に対応するため、これまでひとつのサーバーの中で完結していたものが、規模が大きくなってきて、分割せざるを得なくなりつつありました。

  • 機能を分割して別のサーバーで構築した上で、ThriftやgRPCといったRPCによって機能を呼び出す
  • 機能間のメッセージによる会話はJSONで機械的に誤解のないように規定しよう

といった具合です。

マイクロサービスアーキテクチャのメリットとデメリット

マイクロサービスの理想的な形を追求すると「機能ABの間で余計な依存性がない」「機能Aのトラブルで機能Cが影響を受けない」ということになりますが、実現するのはそう簡単ではありません。

障害発生時の影響を少なくし、分業して開発にあたることができる

マイクロサービスのメリットは2つ。サービスの障害発生時の影響を限定的にでき、かつ復旧も速やかになることと、開発担当者の責任範囲を分割できることです。

マイクロサービスアーキテクチャの本質は分業です。コンポーネントや1個のマイクロサービスごとに集中できれば、より深く開発、問題分析に取り組めます。

こうしたマイクロサービスのメリットを享受するには、「要素がきちんと分割できている」ことが重要です。それぞれの要素に適した技術を用いた上で、要素を小さなコンポーネントに分割し、疎結合で連携させるということが実現できて、はじめてメリットが見えてくると考えています。

適切な分割の粒度については、以下で説明する導入事例のセクションに譲ります。

見るべき範囲が狭まる反面、属人性が高まるデメリットも

細分化することで生じるデメリットもあります。

その1つが、属人性が高くなることです。障害時の影響が分断でき、責任の所在がはっきりする反面、自分の管轄外のコンポーネントやプロダクトに対して無関心になりがちです。

例えば、他のチームが管轄するコンポーネントの影響を受けて、自分の担当サービスに障害が発生したとします。その場合、実際に手を動かして修正できるのは自分ではなく、そのコンポーネントの担当者です。コミュニケーションがうまくいかないと、かえって復旧まで時間がかかってしまう場合もあるでしょう。

原因調査もあわせて担当者に依頼したときには、エラーの要因が分からないまま、サービスそのものは復旧している、という状態になることもあります。開発者の仕事はコードに触れることなのに、実際のコードを読んだり書いたりできないのはもどかしいところです。

問題対応を受ける側も気を使います。直すべき箇所の認識にズレがないか、そもそもどれくらい重要な問題なのかを共有できていないと、疑心暗鬼になりがちです。

このように、いち個人がサービス全体を俯瞰することが難しくなると、結果的に構造が複雑化してしまうことにも留意せねばなりません。いずれも先ほど挙げたメリットとは裏表の関係にあるため、必要悪と言えるかもしれません。

分断しないためのコミュニケーション

これらのデメリットは、マイクロサービスの本質とは表裏一体です。避けられない問題ではありますが、コミュニケーションを工夫することで、解決できるケースもあると思います。

  • ThriftなどのIDLを用いてAPIを提示する
  • テストコードをきちんと書く

などの方法で、誤解のないコミュニケーションができると考えています。

弊社でも、さまざまな国のエンジニアがサービス開発に携わっています。日本人同士が日本語を使っても誤解は起こるものなので、良いコミュニケーションが必要になってきます。良いプロダクトを作るには、適切な「エンジニアらしい」手法を使って正しくコミュニケーションすることが欠かせないと、日々痛感しています。

人間がひとりでできることには限界がありますし、他の開発者の担当範囲を全部知るのは不可能です。しかし、少しだけでも自分の担当コンポーネントの外に関心を寄せられれば、一緒にできることが増えそうだなと思うケースはあります。

LINEでなぜマイクロサービスが導入されたのか

ここからは弊社での導入実例と、運用で起こりがちなトラブルおよびその解決方法をつづっていきたいと思います。

私が入社した2013年時点、すでにLINEはメッセンジャーだけでなく、多数のサービスを展開していました。モノリシックに開発されたメッセージングサービスだけのときと状況が異なり、運用面での問題が噴出していたのです。

これは転換期での話ではありますが、2016年にはアプリ画面のテーマをカスタマイズできるLINEの着せかえショップで発生した障害の影響によって、メッセージングの送受信機能が影響を受けてしまいました。

着せかえショップの影響でサービスダウンした話(LINE DEVELOPER DAY 2016における発表資料

社内でも、サービス開発に関わるメンバー・チームが拡大するにつれ、サービスそれぞれでリスクを取りつつ全体を安定的に運用する必要性が議論されるようになり、「マイクロサービス的な考え方でサービスをスケールしていった方がいい」という意見が挙がりました。

現在も道半ばではありますが、サービスの増加に伴って、重複している機能を適切に分割していこうというモチベーションになっていると考えています。

事例1. Shopのアーキテクチャ ~ ユーザー視点にそって入口を分割する

もう少し具体的な導入事例として、私が担当しているShopのサービスや、我々が利用しているChannel機能のアーキテクチャを挙げて説明していきたいと思います。

Shopのアーキテクチャでは、ざっくり分けると入口をShop-ProxyとLINE STOREの2つとし、裏側の共通コンポーネント部分としてShop-Serverを経由して、その裏にSearch FEとElasticsearchをおいています。

2

共通のロジックをShop-Serverにおきつつ、ユーザーの行動フローが異なる部分はきちんと分けておくことが、この構成でのポイントになります。

例えば、ユーザーにとっては「スタンプを買う」という同じ動作でも、LINEアプリからのスタンプ購入と、Webブラウザからスタンプを買うという2つの入口に分けることで、Shop-ProxyとLINE STOREどちらかの入口が障害になったとしても、もう一方は動き続けることができます。

マイクロサービスのメリット「拡張性の高さ」を生かす

例として挙げた「スタンプを買う」以外にも、送信などいくつかの機能をこのコンポーネントから呼び出すこともありますし、逆にShopの機能を別のコンポーネントから呼び出して使うこともあります。

一方で、Shop-Serverから後ろのコンポーネントは入口が異なっても共通になっています。こうすることで、新しい販売サービスを立ち上げようと思ったときも、Shop-Serverと会話ができるようなコンポーネントをProxyやShop-Serverと横並びにさせ、拡大することが可能です。

コンポーネントを挟み、使いたい機能を分かりやすく使う

障害時の代替手段を用意しサービスの安定性を高めるだけでなく、必要な機能を必要なサービスで表現し、その責任を担保することもまた重要です。

Shopアーキテクチャの例では、Search FE→Elasticsearchの部分がこれに該当します。

3

多機能なソフトウェアの中から、使いたい機能を絞ってアクセスしやすくする

Elasticsearchは多機能で強力なオープンソース・ソフトウェアですが、ひとつのサービスですべての機能が必ずしも必要になるとは限りません。ごく一部の機能だけ使いたいというケースも多いため、フロントエンド(FE)コンポーネントとしてSearch FEを作り、実際に使いたい機能にアクセスしやすくしています。

自分たちが本当に必要最小限な部分をラップして使いやすい形で提供することは、MySQLなどのRDBMSでも同様のことが言えるでしょう。多機能な反面、使い方によってはチェーンソーとなり得るものをそのまま使うのではなく、特定の機能を呼び出すコンポーネントを作ってやるという考え方です。

事例2. Channelサービス

エンジニアHubに会員登録すると
続きをお読みいただけます(無料)。
登録のメリット
  • すべての過去記事を読める
  • 過去のウェビナー動画を
    視聴できる
  • 企業やエージェントから
    スカウトが届く