LINEヤフー Tech Blog

LINEヤフー株式会社のサービスを支える、技術・開発文化を発信しています。

プライベートクラウドにおけるサービス間通信の今、そして「サービス指向クラウド」が作る未来

こんにちは。LINEヤフーで、プライベートクラウドのネットワーク関連サービスを開発している清水です。

現代の大規模なWebサービスにおいては、一つのモノリシックなアプリケーションだけで構成されることは少なくなり、複数の小さなサービスが協働するマイクロサービスアーキテクチャが主流となっています。数多くのサービスを提供するLINEヤフーにおいても、もちろん例外ではありません。社内では多くのマイクロサービスが稼働しており、その多くが自社開発のプライベートクラウド上で動いています。

そのプライベートクラウドが「Flava」です。Flavaは、LINEとYahoo! JAPANがそれぞれ運用してきたプライベートクラウドを統合して生まれました。OpenStackをベースとするIaaSに加え、Kubernetes上のマネージドサービスなども提供しており、仮想マシン、ベアメタルサーバー、Pod、PaaS上のアプリケーションといった多様なワークロードが稼働しています。

マイクロサービスアーキテクチャそのものについては、さまざまな文脈で語られているため、今回は割愛させていただきますが、本記事では「サービス間接続」に焦点を当て、Flavaが抱えていた特有の課題と、その課題にどのように取り組んでいるのかをご紹介します。

Flavaにおけるセキュリティ

Flavaはテナントごとに論理的に隔離されたネットワークを持ち、ネットワーク間の境界ではL4のアクセス制御リスト(ACL)を使って通信を制御しています。このACLは、限定的な一部の社内システムを除き、外部への通信と外部からの通信をデフォルトで拒否する運用になっています。さらに、多くのサービスでは、L4レイヤによるアクセス制御だけでなく、オープンソースの認証・認可システムAthenzを利用したアプリケーションレベルのアクセス制御も実施しています。このように、Flavaはデフォルトでセキュアになるよう構成されています。

一方、強固なセキュリティの導入は、しばしばユーザー体験の低下を招きます。しかし、プライベートクラウドを作る私たちの使命は、サービス開発者にとってより良い道具を提供することです。そのため、強固なセキュリティと優れたユーザー体験を両立しなければなりません。

Athenzについて

Athenzは、サービスアイデンティティに基づくオープンソースの認証・認可システムです。各ワークロードをX.509証明書などで認証し、「どのロールが、どのリソースに、どの操作を実行できるか」をポリシーとして定義します。サービス間通信では、接続元のサービスアカウントを接続先へのアクセス権限を持つロールに関連付けることで、アプリケーションレベルのアクセスを制御します。

サービス間接続の課題

マイクロサービスにおいて、他のサービスとの接続は日常的に発生します。しかし、Flavaではサービス間を接続するために多くの作業が必要であり、それが開発体験を低下させていました。利用者は二つのサービスをつなぎたいだけなのに、つなぐための手続きが煩雑な状態となっていました。

具体例を交えて説明します。

ここからは、フロントエンドを提供する「サービスA」から、バックエンドAPIを提供する「サービスB」のTCPポート8080へ接続する場面を考えます。

従来、この接続を実現するには、少なくとも次の作業が必要でした。

  1. サービスBの接続先IPアドレスを調べる
  2. サービスA側のVPCで、サービスBへのEgress通信を許可する
  3. サービスB側のVPCで、サービスAからのIngress通信を許可する
  4. サービスBを利用できるように、アプリケーションレベルの認可ポリシーを設定する
  5. 必要に応じて、ロードバランシング、タイムアウト、リトライを設定する

それぞれの設定には、送信元と送信先のチーム、ネットワーク管理者、認証・認可基盤など、異なる関係者が関わります。利用者が実現したいことは「AからBへ接続する」だけですが、実際には複数のシステムにまたがる申請と設定が必要でした。

さらに、接続関係が複雑になるにつれ、複数のシステムに分散した設定のうち、実際にどのルールが適用され、それぞれが何の用途で必要なのかを追跡することも難しくなります。その結果、サービス構成の変更後も不要なルールが残りやすくなり、権限を定期的に棚卸しして最小権限を維持するための負担も大きくなります。不要なルールが残り続けることは、それ自体がセキュリティ上のリスクにつながります。セキュリティとユーザー体験はしばしば天秤にかけられますが、ユーザー体験の悪さによって、セキュリティ対策が十分に機能しない場合もあります。

私たちが実現したいのは、安全性を損なうことなく、容易なサービス間接続体験と、安全を担保し続けるための仕組みを提供することです。

課題1:サービス間接続の管理が困難である

あるサービスから別のサービスへ通信するには、送信側と受信側の両方でACLを変更します。それぞれを別のチームが管理していれば、双方で申請、確認、承認が必要です。

サービス数が増えると、必要な作業はサービス数に比例するだけでは済みません。AからB、AからC、BからCというように接続関係が増え、その関係ごとに設定を管理する必要があります。サービスの実装より先に、接続の調整に時間を使う状況が起こります。

ここには、開発者のアジリティとプラットフォームのガバナンスという二つの正しい要求があります。開発者は接続を素早く追加したい一方、プラットフォームはデフォルト拒否と最小権限を守りたい。必要なのは、どちらかを諦めることではなく、安全な接続を少ない作業で実現する仕組みです。

課題2:IPアドレスとサービスの実態がずれる

ACLに書かれているのはIPアドレスです。しかし、開発者が接続したい相手はIPアドレスではなくサービスです。この管理単位の違いは、サービスを構成するIPアドレスが変化したときに問題になります。

例えば、別のリージョンで仮想マシンを再作成すると、IPアドレスが変わります。また、仮想マシンを増減するだけでも、サービスを構成するIPアドレスの集合は変化します。

こうした変更のたびにACLを更新しなければ、接続できないインスタンスや、すでに存在しないインスタンス向けのルールが生まれます。更新漏れを避けるために広いCIDRを許可する方法もありますが、それでは最小権限の原則から遠ざかります。

また、ACLのルールはIPアドレスとポートの組み合わせで表現されます。ただし、これらの組み合わせを見ただけで「どのサービスが、何の目的で通信しているか」は分かりません。障害調査や権限の棚卸しでは、IPアドレスをサービスや担当組織へ読み替える作業が必要になります。

クラウド上では、スケーリングや再作成によって、サービスを構成するインスタンスとそのIPアドレスが継続的に変化します。こうした変化へ人手で追従し、IPアドレスを基準にサービスを管理すること自体に限界がありました。

課題3:通信制御の課題

アクセス制御とは別に、データセンターやクラウドの内部でサービス同士がやり取りする通信(East-West通信)の経路にも課題がありました。

従来は、サービス間の通信を、データセンター内の一部ラックに配置された共有ロードバランサーへ集約していました。多数のサービスが同じロードバランサー層を経由するため、ネットワークファブリック内の特定箇所へトラフィックが集中します。

さらに、同じVIPのトラフィックを処理するロードバランサーノード間で状態を共有しない構成では、連続した失敗を検知して接続を一時的に遮断するサーキットブレーカーなど、状態を持つ制御を実現しにくいという状況があります。

マイクロサービスでは、単に通信できるだけでは十分ではありません。障害の連鎖を防ぐため、接続先の選択、タイムアウト、リトライ、レート制限、サーキットブレーカーなどを、サービス間の関係に合わせて制御する必要があります。

パラダイムシフト:IPアドレスではなく「サービス」を制御の基点とする

サービス単位の接続モデルへの移行図

これらの課題に対し、私たちは制御の基点をIPアドレスから「サービス」へ変えることにしました。

各サービスは名前をつけて登録し、そのサービスに次の情報をひも付けます。

  • そのサービスを提供するコンピューティングノードのIPアドレスとポート(エンドポイント)
  • そのサービスで使用されるAthenzのサービスアカウント
  • そのサービスに対して通信を行う際のロードバランシング、タイムアウト、リトライ戦略などのトラフィックポリシー

エンドポイントの登録と解除を、利用者が手作業で行う必要はありません。コンピューティングノードの作成時に所属するサービスを指定すると、そのIPアドレスがエンドポイントとして自動的に登録されます。また、コンピューティングノードが削除されたときも、そのライフサイクルに合わせて登録が自動的に解除されます。

これにより、ユーザーは安定したサービス名を参照するだけでよくなり、変化するIPアドレスへの追従をプラットフォームに任せることができます。

同じサービス情報をロードバランシングだけでなくVPCのACL生成にも利用することで、サービスのIPアドレスが変わったときにはネットワーク側のルールも自動的に更新できます。存在しなくなったエンドポイントに対応する不要なACLルールが残り続けることを防ぎ、意図しない通信を許可するリスクを低減できます。

サービス間の接続を一つの操作にする

制御単位をサービスへ変えると、接続方法も変えられます。

接続元サービスの開発者は、接続先の一覧から利用したいサービスを探し、「接続する」と宣言します。実際には、以下のようにUI上で接続が完結します。

Service ConnectのUIで二つのサービスを接続している画面

接続先サービスの管理者が必要に応じて承認すると、プラットフォームはサービス間の接続関係を記録します。その関係をもとに、次の処理を行います。

  1. 接続元サービスへ接続先の情報を配布する
  2. サービス間の通信に必要なVPC ACLを設定する
  3. Athenzのロールとサービスアカウントを関連付ける
  4. サイドカーに対してタイムアウトやリトライなどのポリシーを適用する

従来は別々だったネットワーク、認証・認可、トラフィック制御の設定を、「サービス間を接続する」という一つの意図から導出します。

接続関係そのものがデータとして残ることも重要です。どのサービスがどのサービスを利用しているかを確認できるため、影響範囲の調査や、不要になった権限の棚卸しもしやすくなります。

「サービス指向クラウド」の未来

このサービス中心の接続を実現する社内基盤として、私たちはService Connectというシステムを開発しています。

Service Connectは、上述のサービスの定義や接続関係を一元的に管理します。アプリケーションがサービス名を指定すると、サービスディスカバリによって現在利用可能なエンドポイントが返されます。利用者は、コンピューティングノードの増減によって変化するIPアドレスを意識する必要がありません。

このエンドポイント情報は、接続先の発見やロードバランシングだけでなく、L3/L4のVPC ACLの生成にも利用します。サービス間の接続関係が登録されると、通信に必要なACLが自動的に設定されます。エンドポイントが追加または削除された場合もACLが更新されるため、ネットワークの設定を実際のサービス構成へ追従させられます。

Service Connectが扱うのは、サービスディスカバリとACLだけではありません。タイムアウトやリトライなどのトラフィックポリシーと、Athenzによる認証・認可も同じ接続関係に基づいて適用します。コンピューティングリソース内のサイドカープロキシが接続先を選び、通信をクライアント側で分散するため、East-West通信のたびに中央の共有ロードバランサーを経由する必要もありません。

また、従来は、サービスごとに導入したトラフィック制御用のサイドカープロキシと、Athenzの認可用サイドカープロキシで多段プロキシになることもありました。Service Connectではこれらの役割を一つのサイドカーへ統合します。アプリケーションから見れば、一度プロキシを経由するだけで、ロードバランシング、トラフィック制御、認証・認可が適用されます。

ここまで読んでいただいた読者の方の中には、Service Connectをサービスメッシュそのものだと感じた方もいるかもしれません。技術的には、コントロールプレーンが設定を配布し、サイドカーやライブラリが通信を制御するなど、一般的なサービスメッシュと共通する構造を持っています。

一方で、Service Connectが対象とするのは、単一のKubernetesクラスタや一つのアプリケーション群だけではありません。ベアメタルサーバー、仮想マシン、Kubernetes Pod、PaaSを同じサービスモデルで扱い、L7のトラフィック制御に加えて、サービスの接続関係からIP ACLも更新します。

私たちは、最初からサービスメッシュを作ろうとしたわけではありません。異なる実行環境を横断し、現在のセキュリティを守りながらサービス間接続を簡単にする方法を追求した結果、プライベートクラウド全体をサービス単位で制御する仕組みにたどり着きました。私たちはこれを「サービス指向クラウド」と捉えています。

おわりに

サービス間接続の未来は、厳格なネットワーク分離が不要になることではありません。開発者がIPアドレスや個々のACLを直接扱う代わりに、利用したいサービスへの接続を宣言し、プラットフォームがその意図を具体的なネットワーク設定、認証・認可、トラフィック制御へ変換し、セキュリティとユーザー体験を両立することです。

この仕組みは発展途上ですが、サービス開発者が接続設定ではなく本来の開発に集中できる基盤を目指します。