[レポート] WebサイトのバックエンドとしてのAPIを構築する #SVS402 #reinvent

本記事はre:Invent 2019のセッション「SVS402 - Building APIs from front to back」のレポートです。WebサイトのバックエンドとしてのAPI Gatewayを構築する上で非常に参考になるセッションです。
2019.12.31

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Building APIs from front to back

本記事はre:Invent 2019のセッション「SVS402 - Building APIs from front to back」のレポートです。

スピーカー

  • Eric Johnson [Senior Developer Advocate – Serverless, Amazon Web Services]

概要

APIは、アプリケーションが相互に通信できるようにするための標準仕様になりました。 AWSでは、クライアントはAmazon API Gatewayを使用してHTTP、REST、またはWebSocket APIを構築し、サービスとクライアント間の通信エンドポイントを提供します。さらに、Amazon API Gatewayはさらに多くのことができます。このセッションでは、Amazon API Gatewayを使用してREST APIのセットアップをデモします。シンプルなLambdaベースのバックエンドを構成し、データモデリングと検証、カスタムレスポンス、バックエンドサービスとの機能なしの統合に関する機能を追加します。バックエンドサービスに到達する前にAPIレベルで実行できる高度な機能の一部について説明します。

スライド

動画

3:40 くらいまでジョークです。長め!w

レポート

APIs on AWS - Amazon API Gateway

本セッションの主役はAmazon API Gatewayです。まずはAPI Gatewayのおさらいから。

  • APIのフルマネージドサービス
  • キャッシング、セキュリティ、スロットリングなどAPIに関する多くの機能を提供
  • HTTP APIが発表されたばかり

HTTP APIはre:Invent 2019期間中に発表された新しい機能です。特に「Lambdaをプロキシさせる」部分については従来の方法より簡単かつ低コスト(最大約70%減)で構築できるようになります。

APIアーキテクチャ

API Gatewayを使ったアーキテクチャで考えて欲しいこと、それは「API Gatewayとして受けて、他のAPIを対話する」ということ。APIの両側(図で言うとAPI Gatewayの左側と右側)にフロントエンドがあり、API Gatewayから見た場合のフロントエンドとバックエンドがあるということです。

まずはフロントエンド側から見てみましょう。Edge-Optimizedのパターンでは、CDN(API GatewayによってフルマネージドされたCloudFront)が各地域に最適化された通信を実現します。Regionalパターンでは、各地域で配置したアプリケーションまたはサービスから呼び出すことで、同じ地域から通信することができます。またPrivateパターンではVPCからのみプライベートに通信することもできます。

バックエンド側では、まず分かりやすいのは他のAWSサービスへのプロキシです。Lambdaが使われることが多いですが、他にも例えばDynamoDBなどに直接プロキシすることもできます。オンプレミスへの通信も、VPC内のエンドポイント、またはAWS DirectConnect(DX)にプロキシすることで可能です。あまり多く知られていないのが、Web上の他のHTTPエンドポイントへのプロキシも可能なことです。API Gatewayでプロキシすることでキャッシングやスロットリングといった機構を簡単に追加することができます。

API Gatewayのマネジメント

6つの方法があります。

  • AWS Management Console
  • AWS CLI
  • AWS SAM (Serverless Application Model)
  • AWS CloudFormation
  • Swagger / OpenAPI
  • AWS CDK (Cloud Development Kit)

構成管理をしたい場合は必然とCloudFormation、またはCloudFormationをラップするツール(SAMやCDK)を使うことになります。

SAMを使う場合は約20行程のYAMLから始められます。例えばDynamoDBをコントロールするAPIを作りたい場合は、Lambda Function、IAM Role、API Gateway、DynamoDB Tableなどを定義します。

実装例

本セッションでは例として、「新婚の行動のトラッキング」をするWebアプリのバックエンドを想定して実装していきます。具体的でユニークな例ですね。

以下のようなコンテキストを前提としています。

  • 最近結婚した
  • お互いをトラッキングしたい
  • 新規の開発者
  • 自分たちのために作りたい
  • セキュアにしたい
  • 自分たちのために使いたい

下図のようなWebサイトを構築します。なお、Webサイト自体の実装方法は本セッションでは触れません。

フェーズ1 : ベーシックな家族のためのWebサイト

バックエンドAPIが存在するWebサイトは、以下のようなアーキテクチャを採用することが基本です。

  • フロントエンドはAWS Amplify Consoleを利用
    • プロダクションコードはAmazon S3で管理
    • Amazon CloudFrontで配信
    • AWS Amplify ConsoleがCI/CDを担当
  • バックエンドはAmazon API Gatewayで構築
    • AWS Lambdaでプロキシ
    • Lambda Function内でAmazon DynamoDBに読み書き

フェーズ2 : セキュリティと最適化

フェーズ1でフロントエンドとバックエンドを構築することができましたが、認証などは行われていないため不正アクセスが可能な状態になっています。

セッション内では時間などの関係で実装しないものも一部含まれていますが、以下がセキュリティおよび最適化する上で必要となる機能です。

  • Amazon Cognito User Pools
  • スロットリング
  • リソースポリシー
  • AWS WAF
  • データモデル
  • キャッシュ (※)
  • CloudFront (※)

※ 時間の関係でデモは省略

認証と認可

Amazon Cognito User Poolsでユーザーがログインできるようにし、API GatewayのCognito Authorizerで認可を行います。

Cognito User Poolsを使う場合、Webサイトからポップアップでログイン画面が表示されます。ログイン画面でのログインが完了したあと、Webサイトにリダイレクトします。

スロットリング

スロットリングは4つのレベルで処理されます。

1つ目は図の④、アカウントレベルです。10,000rpsまで対応可能です。さらに5,000rpsまでバースト可能です。しかしアカウントに対しての値なので、複数のAPIがある場合は共有されます。例えば17個のAPIを用意したい場合はその17個全て合わせて10,000rpsまで対応可能です。また、これはソフトリミットですので上限緩和が可能となっています。

2つ目は図の③、メソッド単位です。各メソッドに対してスロットリングのルールを設定できます。

3つ目は図の②、使用量プランを利用した方法です。使用量プランは例えばクライアント毎、またはマイクロサービスを組んでいたり他のシステムと連携する場合などに便利な機能で、それぞれ(APIキー単位)にスロットリングルールを設定できます。数多くのシステムと連携するバックエンドを構築する場合、一部のクライアントによってアカウントレベルのスロットリングを受けてしまうと他のシステムとの連携も道連れになってしまいます。各クライアント・システムの許容リクエスト範囲を設定してあげることが重要です。

4つ目は図の①、使用量プランのメソッド毎にスロットリングルールを設定する方法です。使用量プランをさらに細分化し、より細やかなスロットリング設定が行えます。

API Gatewayではこれらのスロットリング設定を①→④の順に評価していきます。

リソースポリシー

リソースポリシーは制限に最適です。図の例ではIAMおよびIPアドレスを使ってAPI GatewayのAPIへのアクセス制限をかけています。

IAMの場合はIAM UserまたはIAM Roleに対して、IAM Policyを設定します。IPアドレスの場合はAWS WAFを利用します。ホワイトリストに加えることで、特定のIPアドレスからのアクセスのみを許可させることが可能です。

データモデリングバリデーション

API Gatewayではデータモデルを設定することで、各APIのリクエスト/レスポンスで扱うモデルについて、各値の型情報を指定できます。型情報に合わないデータが流れてきた場合にはエラーとすることができます。

この層が重要な理由は、Lambda Functionを無駄に実行しないという点です。Lambda Function内でバリデーションすることもできますが、API Gatewayの層でバリデーションをかけることによってLambda Functionを実行せずに不正なデータを弾くことができます。AWS Lambdaの無料利用枠も大きいですが、より最適化することで無料利用枠をより有効活用できますし、超過した場合も最適化されたコストの支払いで済みます。

デモ

上記の実装をAWS SAMを利用して構築するデモです。こちらは実際に見ていただいた方が良いかと思いますので、以下の動画をご覧ください(デモ開始時間を頭出ししている埋め込みリンクです)。

フェーズ2の結果

フェーズ2を経て、下図のようなアーキテクチャになりました。リソースポリシーとWAFを追加し、またスロットルも追加しました。

フェーズ1から比べるとかなりセキュアで安全なサイトに仕上げることができました。

フェーズ3 : 要件の変更

フェーズ2まで作ってきたWebサイトの利用者に、以下のようなアップデートが起こりました。

  • 家族が増えた
  • トラッキングが目的なのには変わりはない
  • 子どもが使うようなシンプルなデバイスが必要

呼び出し回数が多い

シンプルなデバイスの位置情報サービスは更新イベントが多く、APIに対して多くのリクエストが行われることが分かりました。しかしほぼ同じデータ送信のためにAPI GatewayのAPIは叩かせたくありません。

解決策 : APIキー

使用量プランを利用します。APIキーを発行し、子どものデバイスからのリクエストにはAPIキーを利用します。

ペイロードの変更ができない

シンプルなデバイスから取得できる位置情報データが、これまで想定していたデバイスから取得できる位置情報のデータと異なる場合があります。

解決策 : データ変換 (Mapping Template)

Mapping Templateを使ってデータの変換が行えます。変換する際はApache VTLの記法を使って記述します。

さらに、GetについてはLambda Functionを不要で実現できます。API GatewayからDynamoDBに直接リクエストし、Mapping Templateを使ってデータモデル(スキーマ)に合わせます。

デモ

上記の実装をAWS SAMを利用して構築するデモです。こちらは実際に見ていただいた方が良いかと思いますので、以下の動画をご覧ください(デモ開始時間を頭出ししている埋め込みリンクです)。

フェーズ3のサマリ

GetのLambda Functionが不要になり、DynamoDBに直接アクセスする形になりました。

最終的な考え

本セッションでは、以下の機能をAWS SAMを利用して構築できました。様々なインフラストラクチャをシンプルなYAMLコードで構成することができます。

  • Webサイトのベース
  • 認証/認可
  • スロットリング
  • リソースポリシー
  • AWS WAF
  • APIキー/使用量プラン
  • Mapping Template
  • サービス連携

さらに、最初はマネジメントコンソールから作り、OpenAPI 3形式にエクスポートすることができます。こうすることでPostmanやSwaggerなどの周辺ツールとの連携も容易になります。

まとめ

Amazon API Gatewayはリリース以来、数多くのアップデートが行われていますが、その総まとめのようなセッションでした。特にスロットリング周りを詳細に解説されていて非常に勉強になりました。