Experts.jp日本に、もっとエキスパートを。(5)巨大化・複雑化したモノリシックなアプリケーション開発から、サービスを小さい単位に分割し、開発のスピードを上げようとするマイクロサービスが注目されています。アプリ開発のアーキテクチャとして関心はあるのものの、実際にはどのようなメリット・デメリットがあるのかは気になるところ。そこで、マイクロサービスアーキテクチャを採用して新サービスをリリースしたという株式会社ビズリーチ・CTO室チーフアーキテクトの竹添直樹さんに、お話を伺ってきました。Experts.jp編集長・白石俊平さんです。そもそもマイクロサービスって何ですか?岩瀬:そもそもマイクロサービスとは、どのようなアーキテクチャなのでしょうか。竹添:システムをがちっとした一枚岩で作るのでなく、コンポーネントに分割して、小さなサービスの集合体として構築するという考え方がマイクロサービスです。各サービス間は疎結合で、HTTPやMessagingなどでつなぎ、通信し合えるように設計します。#jissenscala岩瀬:一般的に、これがマイクロサービスだという代表例はありますか?竹添:外側から見ているとわかりにくいですが、Amazonさんのシステムがそうですね。チームがそれぞれ独立してサービスを作っていて、裏側はそれぞれマイクロサービスになっているというのは有名です。竹添直樹さん岩瀬:モノリシックのほうはどうでしょう。竹添:小さくてまだ立ち上げたばかりのサービスは、モノリシックが多いと思います。したサービスが大きくなるにつれて機動力が落ち、メンテナンスコストも増大するにつれ、マイクロサービス化するという流れが多いのかなと。岩瀬:ちなみに例えばWordPressは、がっつり一枚岩で作られている印象があるのですが、モノリシックですか?竹添:マイクロサービスの粒度をどう考えるかによりますね。ブログという一つのサービスと見ることもできるし、アーキテクチャとしてはがっちり一枚岩だと思います(笑)。岩瀬:確かに、人によって解釈が違うから定義は難しいですね。義昌さん竹添:たとえば、決済サービスは呼ぶ側からすれば、マイクロサービスと言えるかと。岩瀬:たしかに!では、Web制作で最近よく見るシングルページ・アプリケーションはいかがでしょうか?竹添:フロントエンドがどうあれ、裏側にあるサーバーサイドのシステムが一枚岩かどうかが、マイクロサービスとモノリシックの違いになります。管理者向けの機能とコンシューマ向け機能がそれぞれが独立されたサービスになっていて、APIで必要なデータだけをやりとりする...といった作りがマイクロサービス。フロントエンドとバックエンドのやりとりが、APIでシンプルになっているからといって、マイクロサービスというわけでもないんです。マイクロサービスのメリットとデメリット岩瀬:マイクロサービスのメリットって、どんなところだとお考えですか?竹添:そもそも弊社がマイクロサービスを採用したのは、それぞれのサービスが独立して開発やメンテナンスができるからなんです。全サービスのシステムへの影響を考えなくていいのが、最大のメリットと言えるでしょう。どんなシステムも、運用をずっと続けていくとアーキテクチャが古びてしまうことは避けられません。そのときに全部新しいものに載せ換えるのは、全サービスの停止を伴うことが多いので、リスクも高いしコストもかかる。部分的にメンテナンスしたり、稼働できるというのはいいですね。運用面やバッチ処理、アップデートも、そのサービスごとに単位で入れ替えたりできる。古いアーキテクチャを差し替えるときや、新しいサービスを立ち上げるときも既存システムを止めずに開発できるので、トライアルもしやすいです。SEOが重要なサービスと、業務寄りのサービスとでは目指す方向も、適している技術も違います。サービスごとに技術の選択ができるのは大きいですね。開発者の問題もあって、全部同じ技術でできるかというとそうでもない。エンジニアによって得手不得手があるので、チームとしてもプロジェクトとしてもスケールしやすい面があるなと。岩瀬:スケーラビリティはキーワードですね。バックエンドの言語を好きに選べるというのは、生産性も上がるし、組織的なメリットがあると思います。岩瀬:では、マイクロサービスのデメリットはいかがでしょうか?竹添:デメリットの部分ももちろんあります。サービスごとに冗長性を考えなくてはいけないので、コストも手間もその分かかります。モノリシックならロードバランサ一つでサービス全体を冗長化できますが、マイクロサービスだとそうもいかないので、オーバーヘッドとかインフラとか運用のコストがかかるのが悩みどころです。実際に開発をやってみるとデバッグが難しいですね。いろんなサービスがつながっているので、どこで落ちたのかがわかりにくい。プログラミング的にも、今まで単純にトランザクションスクリプトで書いていたのがいろんなサービスに分割されていくので、単純にシリアルで呼び出していくとその分遅くなってしまう。サーバサイドでも、並列に投げられるところはパラレルに取りに行ったりする並列プログラミングや、非同期的な処理を扱うクライアントサイドの知識が求められるようになってきました。慣れていないとキツイかもしれませんね。アプリケーションプログラマに求められるスキルは上がっていると思います。岩瀬:これまで同期的なコードを書いてきたプログラマが、マイクロサービスでNode.jsなどの非同期な並列プログラミングをしようとしても、前の癖で同期的に書いてしまって、全然パフォーマンスが出ないというのもあり得るわけですね。ちなみに先ほどデバッグの話が出ましたが、モノリシックに比べて、時間がかかって大変だったということはありますか?竹添:作っているときはそうでもないんですが、動かしてトラブルがあったときが大変ですよね。結合させてテストしているときや、プロダクションで何か起きたときのトラッキングとか。セッションIDを全部のログに含めるようにするなど、エラーをトラッキングできる仕組みを最初から作っておけばなんとかなるんですけど、それでも難しい時は難しいですね。岩瀬:設計の段階でわかっていればいいけど、結構大変だってことですね。コツがわかってくるといいのかもしれませんが。サービスの監視についてはいかがでしょうか?以前、マイクロサービスとして各パーツを構築してみたのですが、クラウド上でサービスを作成・破棄することが多く、監視するサービスが増えて大変でした。自動化して監視すればいいと思うんですが、その辺は具体的にどうされてましたか?竹添:自動で監視できるようにテンプレート化してますね。Relic、Mackerel、SasS、ほかにも使えるものは何でも使ってます(笑)。サーバーの種類に応じて、Webサーバー用やデータベース用などを作ってます。岩瀬:ビズリーチでのマイクロサービス実践例について、聞かせてください。竹添:『スタンバイ』というインターネット上から求人情報をクローリングしてくる求人検索エンジンがそうです。スタンバイ垂直方向のアーキテクチャなのですが、コンシューマを向いている部分や、出稿企業に向いている部分、管理的なものや内部のAPIと接続したりといった、マイクロサービスになっています。バックエンドのいわゆるAPIサーバーなどは、機能別にいろいろあるんですが、フロントも二層に分かれていて、直接HTMLからWebサーバーにアクセスするのではなく、間にAPIをはさんでいます。バックエンドは基本プリミティブに作っていて、フロントのAPIがHTMLを生成して直接返しているところもあります。岩瀬:共通する処理とかも結構あるんですか?竹添:認証とかがそうですね。スマホのものであれば、同じようなAPIを共用できます...と言いたいところですが、やはりいろいろ予期せぬことが起こりましたね(笑)。一応、もともとの思惑としてはチームをスケールさせたいという意図がありました。バックエンドとフロントエンドをきっちり分けて、フロントエンドだけでアプリをメンテできるようにしたかったという意図があったんです。#jissenscala岩瀬:バックエンドはScalaで書かれているんですよね?フロントエンドの人も頑張ってScalaを書かなくちゃならないようなことにはならなかったんでしょうか?竹添:全員ががっちり(Scalaを)書かなくちゃならない、ということにならないよう、役割分担を決めて、なるべくフロントエンドで修正が完結するようにしています。いえどうしても、サーバーサイドのプログラミングが必要になることもあるので、各チーム一人サーバサイドのサポートをできる人を配置したりはしています。その人が作ったパーツを使って、フロントエンドの人がテンプレートを作ったり。フロントエンドのほうで全部できるのが理想的なんですけどね。開発のライフサイクルが全然違うので。フロントだけデプロイとか、もっと短いスパンでやっていけるといいなと。画面を変えるだけなのに、サーバーサイドも落とさなくてはいけないのは避けたいので、将来的はフロントエンドだけで回せるようにしたいと思っています。試行錯誤しているところです。岩瀬:フロントとサーバーサイドのライフサイクルの長さの違いって、重要ですね。最近のフロントエンドでも、AngularやReactとかいろいろ流行があるから、必要に応じて対応していかなくてはいけない部分もありますよね。竹添:とはいっても、現実的には厳しいところもあります。フロントで完結できずに、サーバーサイドのエンジニアがサポートしなくてはいけない部分はどうしても出てきます。サーバサイドをサポートするエンジニアが少なくてボトルネックになってしまったり、PCとスマホで同じようなAPIを共有できると思っていたら、画面に引きずられて、結局画面のAPIが増えて再利用できなかったりすることもありました。パフォーマンスとか工数面で考えると、レイヤーが増えていくのでいい結果が出なかったり、JSONの処理にCPUを使うなどの性能面の課題もありましたね。結局、時間的にチューニングしている余裕もないし、直接データベースをいじってしまうこともありました(苦笑)。工数的にも、想定よりかかってしまいましたね。岩瀬:その辺の開発はホント大変ですよね...。アーキテクチャは毎回試行錯誤されているのだと思いますが、次に設計するときはもっとうまくいきそうですか?竹添:実は、ここに来るまで何度かチャレンジはしているんです。最初はモノリシックに作って、それをバラしたり。この『スタンバイ』というサービス自体も、最初から大規模で開発することがわかっていました。工数の都合で密結合となっている部分もありますが、最終的には落ち着くんじゃないかと思っています。次からは、あらかじめ手を打って切り離せるところもあるんじゃないかと。岩瀬:サービスのどの部分が、マイクロサービスに変えていきやすかったといったことはありますか?竹添:今作っているサービスは、(モノリシックなサービスを)部分的に切り離してマイクロサービス化していったのではなく、一回捨ててマイクロサービスで構築し直したんですよね。既存の安定したサービスの隣や上とかに、新たなマイクロサービスを乗っけていくのであればまだやりやすいのですが、仕様もゆるい状態で全てのサービス開発が並列で進んでいくのが難しいところでした。最初はこの機能とこの機能だけとか縛りを設けて、少しづつ拡張していくとか、一個ずつサービスを作っていくことができればやりやすいと思います。マイクロサービス導入の選定ポイントは?竹添:こないだ海外のScalaのカンファレンスに行ってきたときに聞いたのですが、「HootSuite」というSNSのクライアントツールを作っている会社が、PHPのモノリシックなアプリケーションからScalaのマイクロサービスにリプレイスしているという話がありました。それによると、まず最初に手をつけたのが、認証系だったそうです。いうと一番難しいところだから。難しいところから一番最初にやれば、ほかのところもできるでしょうという考え方なんですよね。我々は経験値があまりないので、簡単なところからやろうとするけど、強いチームで「頑張ってやるんだ」という覚悟があれば、あとはできるだろうと。岩瀬:難しいところからやるって度胸ありますね(笑)。竹添:コードを静的解析して、どこが一番複雑かを分析してるんですね。リファクタリングと同じでダメなところからつぶしていく考え方じゃないかと。岩瀬:マイクロサービスはパーツが多いので、モノリシックなサービスに比べて壊れやすいと思うのですが、何か設計のポイントはありますか?竹添:ユーザー向けのサービスで、一個壊れると止まってしまうようなところは、必ず冗長化させるようにしています。全部冗長化させるとコストがかかるので、社内向けのシステムやバックエンドで動いているバッチ処理など、1日くらい止めてもいいサービスならゆるめにするとか、強弱をつけて冗長化しています。岩瀬:クリティカルなところはかなり力を入れているんですね。竹添:検索サービスは弊社のサービスとしてかなりメインになっていくので、スケールすることが前提です。コストをかけても実装しておくようにしていました。岩瀬:マイクロサービスは冗長化しておくように、頑張って作らないといけない部分もありますよね。竹添:弊社はAWSを使っているのと、冗長化についてはかなり学んでいるので、その辺はなんとかなっています。エンタープライズのミッションクリティカルなものと比べると、やりきれていない部分もありますけどね。本当なら相乗りさせられるものはしたほうが運用も楽になるし、コストも下げられると思いますが、まだそこまでやりきれてない。岩瀬:今回はマイクロサービスで作ったけど、次回はモノリシックで作ったほうがいいとか判断に悩むことがあると思うのですが、何か選定ポイントはありますか?竹添:開発のときのオーバヘッドや、インフラコストで見ると、短期的にはデメリットのほうが大きいと思っています。どこかで損益分岐点はあると思うんですが。スモール仕立てでやってみようとか、成功するかまったくわから状況なら、モノリシックで作って、後から大きくなったタイミングでマイクロサービスにしてもいいのではないでしょうか。その事業が大きくなるタイミングでシステムを止めてコアアーキテクチャを変えるという判断は簡単にはできません。経営判断が必要になるかと。リソースがあるなら、現状のモノはエンハンスを続けながら、マイクロサービスで作りなおすなど、体制を組むという選択肢もあるが難しいですね。企業向けのサービスをやっていると、機能的に変わってしまうとか、デグレードしてしまうのは、許されない面もあるので。最初からこれだけ大きなサービスにするというように決められたら、マイクロサービスで作るという判断もできると思います。大きくなったタイミングで経営判断もあるが、あきらかにスケールすることが見えていればマイクロサービスにするほうがいい。ある程度大きくなってくると、プロダクトとしても、チームとしてもマイクロサービスでないとスケールできない。モノリシックだとレガシー化してしまうので、新しい技術も入れられないし、新陳代謝していかないと新しい人も入ってこない。両面的に健全ではないです。岩瀬:例えば新しくチームメンバーが増えたとき、モノリシックなら全体像が見えるけど、マイクロサービスは全体像が見えにくくて困る...といったことはないのでしょうか?竹添:それはもちろんありますね。ひとつひとつのサービスのインターフェイスが明確であれば、それほど問題はないはずです。サービス開発を立ち上げたばかりだと難しいところもありますが、なんとか頑張って、インターフェイスだけはちゃんとしようと思っています。そこまで到達していない過渡期ではありますが...。岩瀬:インターフェイスは、ほぼJSONでやりとりしているようですね。Schemeを使ったりしているのでしょうか?竹添:インターフェースは、内部ではScalaのオブジェクトで定義しています。JSONを直接さわることはほぼありませんね。それだと、(Scalaを知らない)フロントエンジニアにとってはハードルが高いので、そこもカバーできるようなものがあるといいのですが。型定義の強いTypeScriptにトライしているチームもありますね。白石:インターフェース定義がJSONを前提としていないということは、他のシリアライズ方法に切り替えることも難しくなさそうですね。Bufferに置き換えたりすることで、処理負荷を軽減することもできるんでしょうか。竹添:その辺も研究はしています。扱っているデータがテキストが多いので、バイナリ形式にしてもあまり速くならないんですよね。MessagePackとかも試しましたが、あまり変わらなかったので、今は使いやすさでJSONを選んでいます。コンパクトな情報でやりとりしているところは、プロトコルを変えたりとか、今後検討の余地はありますが。白石:マイクロサービスのAPI設計はどのように行うのですか?サービス同士が複雑な依存関係を持たないように設計するには?竹添:マイクロサービスの設計を適切に行うのはなかなか難しいですが、あるまとまった処理を他の部分から独立させうるかどうか、複数のサービスから利用されうるかどうか、といったところがひとつの判断基準になると思います。その上で、データベース処理を必要とするかどうかによって、ライブラリとして実装するか、サービスとして実装するか判断する感じでしょうか。データベースアクセスを伴わないなら、ライブラリとして配布すればいい。データベースアクセスを伴うのであれば、マイクロサービスにすることを考えますね。白石:データベースって、どうしているんでしょうか。マイクロサービスごとに別々のデータベースを持っているのか、それともひとつのデータベースを複数のマイクロサービスが共有しているイメージでしょうか?竹添:いえ、マイクロサービスごとにデータベースは分けています。白石:じゃあ、トランザクションはどうするんですか?竹添:マイクロサービスごとに切ってますね。白石:マイクロサービスをまたがるような処理を、ひとつのトランザクション内で行いたいという場合はありませんか?竹添:それぞれのマイクロサービスを、適切な粒度で切っているので、基本的には大丈夫かと。大きくなって出てこないとは言い切れないかもしれませんが、そうなったら外側の仕組みでなんとかするかんじですね。白石:SOAPという選択肢はないですか?WS-Transactionとかあると思いますが。フロントがスマートフォンのアプリだったり、JavaScriptだったりするので、フロントエンドから使いやすいフォーマットということで。トランザクションが必要なところが切り出せているのと、参照系がほとんどなので現段階では問題として浮上していませんね。出てきたときには、大きな課題になるかもしれませんが。白石:マイクロサービス・アーキテクチャと、アジャイル開発との相性はいかがでしょう?特に、複数の小さなチームが連携しなくてはならないとなると、それぞれのチームがアジャイルに独立して進めるのは難しいのでは...と思ったのですが。竹添:私たちもスクラムで開発していますし、マイクロサービスとアジャイル開発の相性は良いと思っています。マイクロサービス間で、密に連携しなくてはならないという状況はそれほど多くないですし、そうした必要が生じた場合は、マイルストーンを置いて調整します。白石:例えばサービスに一個の機能を追加するとき、チームを横断した変更が必要になったりしませんか?竹添:チームとしてはマイクロサービスごとに(フロントエンドからデータベースまで)縦に分けているので、チーム内で完結しますね。それぞれのチームの中でフロント・サーバーなどの役割分担があります。ひとつひとつのサービスの粒度も、小さいものから大きいものまであります。単機能のサービスもあれば、大きなWebアプリケーションだけど一つのマイクロサービスというものまであって、その中でスクラムを回すかんじですね。白石・岩瀬:いろいろ試行錯誤やチャレンジをされているお話を伺い、マイクロサービス・アーキテクチャ実践のポイントが見えてきた気がします。