Amazon Web Services ブログ

AWS Lambdaを使ったFederated GraphQLの構築

この記事は、IMDbのSenior Software Development EngineerであるKrzysztof Lisが執筆しています。

IMDbは、映画、テレビ、セレブリティのコンテンツを提供する世界でも人気のあるサービスです。映画、番組、セレブリティ、イベント、様々な業界の関係者、分散型のオーナーシップモデルなど、複雑なビジネスドメインを扱っています。そして様々なチームが所有するシステムやデータには明確な境界があります。

IMDbは歴史的にクライアントにサービスを提供するモノリシックなRESTゲートウェイシステムを使用しています。長年にわたり運用する中で効果的に管理をすることが難しくなっています。例えば何千ものファイル、明確な所有権を持たないビジネスロジック、データに結びついた信頼性の低い統合テストなどがあります。これを解決するために、チームはGraphQLを使用しました。これは必要なデータだけを要求できるAPI用のクエリ言語で、既存のデータに対してそのクエリを実行することができます。

このプロトコルを実装するにはスキーマを定義し、クライアントから要求されたすべてのフィールドを解決するモノリシックなサービスを作成するのが一般的です。これはドメインが比較的小さく明確でSingle-Thread Ownership(※1)なアプリケーションに適しています。IMDbはGraphQLリクエストを既存のすべてのデータチームに連携させることができる、Federated アプローチを選択しました。この記事ではAWS Lambda上で Federated GraphQL を構築する方法を紹介します。

※1: Single-Thread Ownershipについての解説は以下の記事や動画を参照ください。
Two-Pizza Teams Are Just the Start, Part 2: Accountability and Empowerment Are Key to High-Performing Agile Organizations
What is a Single Threaded Owner?

概要

この記事では、モノリシックなREST APIとモノリシックなフロントエンドから、モダンなフロントエンドと連携したバックエンドシステムへの移行を取り上げています。以前のシステムにおける課題を整理し、なぜFederated GraphQL がこれらの問題を解決するのかを説明します。

まずはアーキテクチャの概要を説明し、新しいシステムを設計する際に決定したことを解説します。また、トラフィックが多く、注目度の高いページを新しいエンドポイントで開発・運用した経験を紹介します。加えて、IMDbのオーナーシップモデルや開発ライフサイクルの改善、スケーリングの改善なども紹介します。

GraphQL とFederated GraphQLの比較

Federated GraphQLは、複数のマイクロサービスのGraphQL APIを1つのAPIにまとめることができます。クライアントはサービスへの個別対応をせずに単一のリクエストを行い、データソース間の結合を含む複数のソースからデータをフェッチすることができます。

これは、スキーマ定義の例です。

type TitleQuote {
  "Quote ID"
  id: ID!
  "Is this quote a spoiler?"
  isSpoiler: Boolean!
  "The lines that make up this quote"
  lines: [TitleQuoteLine!]!
  "Votes from users about this quote..."
  interestScore: InterestScore!
  "The language of this quote"
  language: DisplayableLanguage!
}
"A specific line in the Title Quote. Can be a verbal line with characters speaking or stage directions"
type TitleQuoteLine {
  "The characters who speak this line, e.g.  'Rick'. Not required: a line may be non-verbal"
  characters: [TitleQuoteCharacter!]
  "The body of the quotation line, e.g 'Here's looking at you kid. '.  Not required: you may have stage directions with no dialogue."
  text: String
  "Stage direction, e.g. 'Rick gently places his hand under her chin and raises it so their eyes meet'. Not required."
  stageDirection: String
}

これはモノリシッククエリの例です。
"Get the 2 top quotes from The A-Team (title identifier: tt0084967)":

{ 
  title(id:"tt0084967"){ 
    quotes(first:2){ 
      lines { text } 
    } 
  }
}

以下はそのレスポンスの例です。

{ 
  "data": { 
    "title": { 
      "quotes": { 
        "lines": [
          { 
            "text": "I love it when a plan comes together!"
          },
          {
            "text": "10 years ago a crack commando unit was sent to prison by a military court for a crime they didn't commit..."
          }
        ]
      } 
    }
  }
}

これは、Federated クエリの例です。
“What is Jackie Chan (id nm0000329) known for? Get the text, rating and image for each title”

{
  name(id: "nm0000329") {
    knownFor(first: 4) {
      title {
        titleText {
          text
        }
        ratingsSummary {
          aggregateRating
        }
        primaryImage {
          url
        }
      }
    }
  }
}

モノリシックな例では、1つのサービスからquotesを取得します。連携の例では、knownFortitleTextratingsSummaryprimaryImageは、ゲートウェイによって別々のサービスから透過的に取得されます。IMDbでは、ゲートウェイを呼び出すクライアントからは透過的に、19のグラフレット(subgraph)に渡ってリクエストを連携しています。

アーキテクチャの全体像

IMDbは、Webサイト、iOS、Androidアプリの3つのチャネルをサポートしています。それぞれのチャンネルは単一のFederated GraphQL Gatewayにデータをリクエストすることができ、Gatewayはそのリクエストを複数のグラフレット(subgraph)に連携されます。呼び出された各グラフレットは部分的なレスポンスを返し、Gatewayはそのレスポンスを他のグラフレットが返したレスポンスと統合します。クライアントはクエリで指定した形式で必要なデータのみ受け取ることができます。これは、開発者がネットワークの使用状況を意識しなければならない場合(例えば、モバイルネットワーク上での使用)に特に効果的です。

以下はアーキテクチャ図です。

アーキテクチャには、Lambda上で動作する「Gateway」と「Schema Manager」という2つの主要なコンポーネントがあります。GatewayはNode.jsベースのLambda関数で、オープンソースのApollo Gateway上に構築されています。Gatewayは主に認証、キャッシング、メトリクス、ロギングを処理するコードでカスタマイズされています。

また、Upstream Graphletsや、グラフ上でのA/Bテストを可能にするA/B Testing Serviceも注目すべきコンポーネントです。Gatewayは、AWS WAFで保護されたApplication Load Balancerに接続されており、CDN としてAmazon CloudFrontが前段に配置されています。また、グラフレットからの部分的なレスポンスをキャッシュするためのエンジンとしてAmazon ElastiCacheのRedisを使用しています。システムが生成するすべてのログとメトリクスは、Amazon CloudWatchに収集されます。

コンピュートプラットフォームの選択

ここではオンデマンドでスケールするため、Lambdaを使用しています。IMDbはLambdaのProvisioned Concurrencyを使ってコールドスタートのレイテンシーを減らしています。またインフラは抽象化されているので、自分たちで容量を管理したり、パッチを処理したりする必要はありません。

さらに、Application Load Balancer(ALB)は、HTTPトラフィックをLambdaに誘導する機能をサポートしています。これはAPI Gatewayの代替となります。ワークロードは、Gatewayが単一のエンドポイントを持っているため、API Gatewayが提供する多くの機能を必要とせず、ALBがより良い選択となります。また、ALBはAWS WAF にも対応をしています。

Lambdaを使ってリクエストごとにスキーマを取得しなくとも、スキーマの更新を取得する方法を設計しました。これはSchema Manager コンポーネントで対応しています。このコンポーネントはレイテンシーを改善し、全体的なカスタマーエクスペリエンスを向上させます。

レガシーデータサービスとの連携

Federated GraphQL移行の主な目的は複数のバックエンドサービスからデータを集約してからクライアントに送信するモノリシックなサービスを廃止することです。Federated Graphのデータの一部は、新しいシステムを考慮して開発された全く新しいグラフレットのデータです。

しかし、GraphQLエンドポイントを動かすデータの多くは既存のバックエンドサービスから供給されています。Lambdaで実行することの利点の一つは、基盤となるデータソースやデータサービスに最適なランタイム環境を柔軟に選択できることです。

レガシーサービスに依存するグラフレットについては、IMDbではJavaで書かれた提供済みのクライアントライブラリを使用し、軽量のJava Lambda関数を使用しています。これらの関数はAWS PrivateLinkを介してレガシーバックエンドに接続し、データを取得してGraphQLリクエストで期待される形式に変換します。新しいグラフレットについては、パフォーマンスの向上と開発のしやすさからグラフレットチームがNode.jsを最初の選択肢として検討することをお勧めします。

キャッシュ

Gatewayでは、グラフレットのキャッシングモードとして静的キャッシュと動的キャッシュの2種類をサポートしています。静的キャッシュでは、グラフレットのオーナーがグラフレットから返されるレスポンスのデフォルトTTLを指定することができます。動的キャッシュは部分的なレスポンスにカスタムキャッシュ拡張機能に基づいてTTLを計算します。これにより、グラフレットの管理者は、そのグラフレットが返すコンテンツの最適なTTLを決定することができます。例えば、静的なテキストのみを含むクエリの場合は24時間とすることができます。

権限について

各グラフレットは、それぞれ個別のAWSアカウントで動作しています。グラフレットのアカウントは、GatewayのAWSアカウントに(AWSプリンシパルとして)グラフレットのLambda関数に対する呼び出し権限を設定します。これはエンジニアの個別アカウントにデプロイされた開発環境のスタックによって委任されるクロスアカウントのIAMロールを使用します。

Federated GraphQLの開発

Federated GraphQLへの移行は、期待通りの結果をもたらしました。ビジネスロジックを適切な専門知識を持つチーム(グラフレットチーム)に近づけることができました。同時にエコシステムの中核となる技術的な部分は、プラットフォーム専門のチームが管理・開発しています。これにはGatewayやSchema Manager、グラフレットチームが再利用できる共通ライブラリやCDKコンストラクトなどが含まれます。その結果、明確なオーナーシップ構造が生まれ、それはIMDbのチーム編成にも合致しています。

プラットフォームチームの運用上の優秀性の観点からは、ビジネスロジックに関連するサポートチケットが減少しました。以前は、適切なサービスチームへの伝達が遅れていました。統合テストが安定し、基礎となるデータから独立したものになったことで、継続的デプロイメントプロセスに対する信頼性が高まりました。また、データの変更がテストの失敗やパイプラインのブロックの根本原因になることも無くなりました。

グラフレットのチームは、グラフで利用可能なデータの完全なオーナーシップを持つようになりました。グラフレットチームは、部分的なスキーマとグラフとその部分のデータを提供するリゾルバを管理しています。彼らはその分野で最も専門知識を持っているので、潜在的な問題を早期に発見することができます。これはお客様の体験を向上させ、システム全体の健全性を高めることに繋がります。

Webやアプリの開発者グループも、移行の影響を受けています。GraphQL PlaygroundApollo Clientなどのツールによる学習手助けもあり、各チームは学習ギャップを素早くカバーし、新機能の提供を開始しました。

IMDb.comのメインページの1つにタイトルページがあります(例:『Shutter Island』)。これは新しいGraphQLエンドポイントを使用するように移行しました。これにより、新しいサーバーレスのFederated Systemが10,000TPS以上の本番トラフィックに対応できることが証明されました。

まとめ

単一で、見つけやすく、文書化されたバックエンドエンドポイントにより、クライアントはグラフで利用可能なデータを試すことができました。私たちはバックエンドAPIレイヤーを整理し、明確なオーナーシップの境界を導入し、クライアントの開発サイクルを短縮する強力なツールを提供することができました。

インフラストラクチャにはLambdaを使用したことで、EC2フリートの管理やパッチ適用、スケーリングの負担を取り除きました。チームはこの時間を利用して、システムの機能や運用性の向上に取り組んでいます。

パート2では、IMDbがどのように連携スキーマを管理しているか。また、連携したGraphQLエンドポイントの高可用性を確保するために使用しているガードレールについて説明をします。

その他のサーバーレスの学習リソースについては、こちらで見つけることができます。

この記事の翻訳は Solutions Architect 岡田が担当しました。原文はこちらからご覧いただけます。