Amazon Web Services ブログ

AWS LambdaとAmazon S3を利用した連携スキーマの管理

この記事は、Senior Software Development Engineer, IMDbであるKrzysztof Lisによって執筆されました。

GraphQLの連携構成の中でも最も大きな課題の一つに、スキーマ管理があります。IMDbは19のサブグラフ(グラフレット)を保持しているのですが、各グラフレットはスキーマ全体のうちの自分の担当部分を、独立したCI/CDパイプライン上で所有し公開を行なっています。

連携スキーマを効果的に管理するために、IMDbはSchema Managerというコンポーネントを採用しました。このコンポーネントの責務は、スキーマの最新の変更を取得しGatewayに公開する前に検証を行うというものです。

第一回の記事では、モノリシックなREST APIをAWS Lambda上で実行されるFederated GraphQL(GQL) エンドポイントに移行するというものでした。今回の記事では、Federated GQLシステムでのスキーマ管理についてフォーカスしていきます。チームがこのコンポーネントを設計する際に直面した課題やどのように解決していったかを示します。また、実際の経験に基づいたスキーマ管理に関するベストプラクティスやプロセスも共有していきます。

モノリシックなGQLスキーマとFederated GQLスキーマの比較

モノリシックなGQLの一般的な実装は、スキーマ全体を管理するのに一つのファイルを使うというものです。この方針は新しい変更と以前のスキーマのコンフリクトが起こらないことを簡単に保証できます。ビルド時にスキーマ全体を検証することができるため、外部的な変更が実行時にエンドポイントを壊してしまうようなリスクがありません。

しかし、Federated GQLエンドポイントでは異なる動きをします。Gatewayがランタイムで各グラフレットのサービス定義を収集し、スキーマ全体を合成していきます。いずれかのグラフレットが整合性の合わない変更をしていた場合、Gatewayは合成に失敗し、リクエストを処理することができなくなります。

グラフレットの連携の数を増やしていけばいくほど、破壊的な変更が入ってしまうリスクは増えていきます。エンタープライズ規模のシステムでは、本番環境を潜在的なダウンタイムから保護するようなコンポーネントが必要となります。このコンポーネントは、できれば変更をリリースする前の開発の時期に、グラフレットの所有者に破壊的な変更が適用されようとしていることを通知するような機能を持っていなくてなりません。

連携スキーマの課題

連携スキーマの管理については他にも考慮する側面があります。AWS Lambdaを使っている場合、デフォルトのスキーマ合成の仕組みではGatewayの起動時間が増加するため、エンドポイントのパフォーマンスに影響してしまいます。スキーマ合成時に宣言されているグラフレットのうち一つでも利用できないものがあると、Gatewayのダウンタイムが発生したり、全体として不完全なスキーマとなってしまったりする可能性があります。スキーマが事前に検証されてAmazon S3のような高可用なデータストアに蓄積されていれば、このような問題を軽減することができます。

スキーマの一貫性という課題もあります。理想的には、スキーマの変更が公開されたら適宜Gatewayに変更を伝播したいはずです。他に考慮しなければならないこととして、非推奨なフィールドの取り扱いやグラフレット間でのフィールドの移行(所有権の変更)があります。潜在的なエラーを早期に見つけるには、開発者が開発中に現在のスキーマに対して変更の検証ができることを許可するドライラン的な機能を、システム上にサポートする必要があります。

Schema Manager

Schema Manager

これらの課題を解決するために、Gateway/PlatformチームはSchema Managerというコンポーネントをワークロードに導入しました。グラフレットパイプラインのデプロイが走った際にスキーマ検証のプロセスがトリガーされるというものです。

Schema Manegerは、全てのグラフレットから直近の外部スキーマを取得し、スキーマ全体を合成しようと試みます。エラーやコンフリクトが出なければ、変更は承認され安全に本番環境へデプロイすることが可能となります。

検証が失敗した場合、デプロイされようとした破壊的な変更は、グラフレットのデプロイパイプラインからブロックされ、所有チームは変更をデプロイする前にエラーの原因を解決する必要があります。バックエンドのロジックを含んだグラフレットのコード変更のデプロイについても、この承認ステップと依存関係を持つことになるため、スキーマ変更が承認ステップによってブロックされた場合でもバックエンドのロジックとスキーマの同期が崩れるというリスクがなくなります。

Gatewayとの統合

合成されたスキーマのバージョン管理のために、マニフェストファイルには最後に承認されたグラフレットスキーマの保存場所を記載しておく必要があります。マニフェストファイルは、スキーマファイルへのS3キーとグラフレットサービスのエンドポイントがマッピングされたJSONファイルでです。

各グラフレットスキーマのファイルはスキーマのハッシュで名前をつけられています。Schema Mangerは現在のマニフェストを取得し、検証対象のスキーマのハッシュを使って対象のスキーマが変更されたかを確認します:

{
   "graphlets": {
     "graphletOne": {
        "schemaPath": "graphletOne/1a3121746e75aafb3ca9cccb94f23d89",
        "endpoint": "arn:aws:lambda:us-east-1:123456789:function:GraphletOne"
     },
     "graphletTwo": { 
        "schemaPath": "graphletTwo/213362c3684c89160a9b2f40cd8f191a",
        "endpoint": "arn:aws:lambda:us-east-1:123456789:function:GraphletTwo"
     },
     ...
  }
}

これらの情報から、Gatewayはグラフレットスキーマをサービススタートアップの一環としてS3から取得し、インメモリキャッシュに保存します。立ち上がった後は、5分ごとに更新をポーリングします。

S3をスキーマストアとして利用することによって、レイテンシーや可用性の問題を解消できます。また、検証時にグラフレットのランタイムからスキーマを直に取得しなくても良くなるため、ランタイムアクセスに対する懸念がなくなります。

スキーマの結果整合性

複数のグラフレットが同時に更新される可能性もあるため、一つのスキーマ検証ワークフローが他のワークフローを上書きしないという保証はありません。

例えばこのようなケースです:

  1. SchemaUpdater 1がグラフレットAの更新のために起動
  2. SchemaUpdater 2がグラフレットBの更新のために起動
  3. SchemaUpdater 1 がマニフェストv1を取得
  4. SchemaUpdater 2 がマニフェストv1を取得
  5. SchemaUpdater 1 がグラフレットAの変更が入ったマニフェストv2をアップロード
  6. SchemaUpdater 2 がグラフレットBの変更のみが入ったマニフェストv3をアップロードしv2の変更を上書き

ただしこれは、どちらのバージョンのマニフェストが更新を勝ち取っても致命的な問題にはなりません。どちらのマニフェストも、有効でエラーのないスキーマを表現できており、Gatewayにとって問題とならないためです。SchemaUpdaterがグラフレットAに対して再び実行されると、現在のマニフェストが以前アップロードした変更を含んでいないということがわかるので、アップロードし直せば良いのです。

スキーマの不一致が発生するリスクを減らすために、Schema Managerはスキーマの変更を15分ごとにポーリングするように、Gatewayは5分ごとにポーリングするようになっています。

ローカルでのスキーマ開発

スキーマの検証は、デプロイパイプラインに組み込まれた形でグラフレットの変更全てに対して自動的に実行されます。しかし、効率的な開発サイクルを回すためにはこのフィードバックループは少々遅すぎます。この不整合を解消するために、チームでは変更を公開することなくこの検証プロセスを実行できるようなツールを利用しています。このツールは、スキーマの公開する代わりにアウトプットとして開発者に検証の結果を提供します。

Schema validation

Schema Validatorスクリプトは、グラフレットのどのような依存関係に対しても追加することができます。このスクリプトは、Schema Definition Language(SDL)で書かれたグラフレットのスキーマ定義を取得し、Schema Managerにペイロードとして渡します。Schema Managerはスキーマ全体に対して検証をで行い、ユーザーに検証エラー(もしくは成功)を返却します。

連携スキーマ開発のベストプラクティス

Schema Managerは、連携スキーマ開発を行うための最も致命的な課題を解決してくれます。しかし、自分の組織でこのプロセスを運用しようとしてみるとほかの問題も出てくるでしょう。

連携スキーマの長期的なメンテナンス性にとって重要なのは、入ってくるスキーマの更新の水準を高く保つということです。外部スキーマに複数の所有者がいるので、グラフレットチーム間のコミニケーションチャンネルを整えておくことをお勧めします。チャンネルを整えておくことにより、予定しているスキーマ変更に対してフィードバックを素早く得ることができます。

また、グラフの共通部分を共有ライブラリとして抽出し、型定義やリゾルバを生成しておくこともおすすめです。このようにしておくことで、グラフレット開発者は強い型付けの恩恵を受けることができます。我々のチームではオープンソースライブラリを使ってこれを実現しています。

結論

スキーマ管理はfederated GQLにとっては自明に解決できない課題と言えます。最も高いリスクとしては、グラフレットの一つが破壊的な変更を導入することによってシステムの可用性に影響を与える可能性があるという点です。破壊的な変更が入ってしまうと、システムはリクエストを処理することができなくなります。スキーマ変更を行なっているエンジニアへのフィードバックループの遅延についての問題や、実行時スキーマ合成におけるサービスレイテンシーへの影響についても考える必要があります。

IMDbは、Labmda上で実行されるSchema ManagerというコンポーネントやスキーマのストアとしてS3を利用することにより、この問題に対処しています。また、デプロイパイプラインにガードレールを敷くことによって、本番環境へ破壊的な変更がデプロイされないことを保証しています。各グラフレットチームは、自動生成された型定義を含んだ共通スキーマライブラリを使用し、予定されているスキーマ変更をスキーマワーキンググループのミーティングでレビューして、デプロイプロセスを効率化させています。

こういった要素の積み上げと自動化された変更管理によって、連携グラフ(federated graph)の安全性や高いメンテナンス性を保っています。次の段階では、使用中のフィールドが削除されないようなメカニズムの提供や、複数のグラフレット間でスキーマ変更を調整することのできる仕組みが必要になります。IMDbでは解決すべき興味深い課題が山積しています。

サーバーレスの話をもっと学びたければ、Serverless Landをご覧になってみてください。

この記事の翻訳は Solutions Architect 野村侑志(@ugnomura)が担当しました。