Amazon Web Services ブログ

Lambda@Edge を使ったゲームプレイヤーへのカスタムコンテンツの配信

モバイルゲームのクライアントアプリを起動した時に、いつも最新のコンテンツを入手できるようにしたいですよね?そうすれば、ゲームへ変更を加えて、新しい機能を追加した際、変更内容をすぐにプレイヤーに提供できるようになります。

しかし、すべてのプレイヤーがゲームを最新バージョンに更新したことを常に保証することはできません。では、古いバージョンのゲームクライアントが以前のゲーム形式 でデータを取得できるようにするためにはどうすればいいでしょうか?またプレイヤーがゲームを実行するたびに、データがチェックおよびダウンロードされますが、どうやって高速な配信を保証すれば良いでしょうか?

何をすべきか

この問題には様々な解決方法があります。ほとんどの解決方法はプレイヤーのリクエストをみて、どのファイルをレスポンスで返すかを決めるコードを実行する web 機能を実装します。

しかしながら、”高速な配信”を実現するためには少し問題があります。プレイヤーが地球の裏側にいる場合、ファイルが海底ケーブルを通りダウンロードされますが、どうすればゲームのダウンロード時間が短くなるでしょうか?

もしあなたが web 技術に詳しければ、ある解決方法を知っているはずです。CDN (コンテンツデリバリネットワーク)を使う方法です。

CDN は世界中のサーバを用いてコンテンツを配布するシステムです。その各サーバのコンテンツはオリジンサーバをチェックすることによって常に最新のファイルを持つことを保証しています。

しかし、CDN が問題を完全に解決するわけではありません。1つの URL がヒットしているので、実際に配信するファイルを決定することが必要となります。サーバとクライアントのやりとりが増えて、動作が遅くなることは避けたいでしょう。また、そのコードをCDN で実行することはできませんよね、CDN はデータサービスですから、それとも…

@Edge を活用した生活

AWS では、Amazon CloudFront と呼ばれる高速かつセキュアで、プログラミング可能な CDN を提供しています。CloudFrontには、今回の問題を解決するのに役立つ Lambda@Edge という非常に興味深い機能があります。

AWS Lambda は、サーバのプロビジョニングおよび管理なしでコードを実行できるサービスです。もし AWS Lambda についてご存知でない場合はこちらの “Getting Started” の記事をご参照ください。Lambda@Edge はこのサーバレス技術を CloudFront で利用します。

Lambd@Edge の機能はプレーヤーにコンテンツを提供している、プレイヤーと距離が近いサーバで実行されます。つまり、CloudFront が取得したデータからどのデータをプレイヤーに送るか制御することができるため、コンテンツを送る前にオリジンサーバと通信しなくてよくなります。

Lambda@Edge にはトリガーの種類に制限があり(全て CloudFront を中心としたものです。)、また実行可能時間は短いですが、Lambda@Edge はいわゆる静的な CDN では見られない有用性を CloudFront に提供します。

Lambda@Edge の利用

この機能を理解していただくために、クイックスタートガイドとサンプルゲームを作成しました。すぐにダウンロードしていただくことができます。

注意:現在 Lambda@Edge に無料利用枠がないため、このサンプルを実行すると利用料金が発生します。

まず、「オリジンサーバ」と「エッジサーバ」とは何か説明します。

オリジンサーバは、私たちのデータを配置する場所です。これはデータの最新バージョンが配置される場所です。CloudFront にデータを読み込むために、データをすべての CloudFront のサーバにプッシュ配信する必要はなく、代わりにオリジンサーバに配置をします。

プレイヤーにデータを提供する、世界中の様々な位置に配置されているサーバをエッジサーバと呼びます。エッジサーバは、設定に応じてオリジンサーバに新しいデータがあるか確認してからプレイヤーにデータを配信します。

エッジサーバはデータをキャッシュします。これは、次にデータへリクエストがあったときに、データがプレーヤーに配信される準備ができたことを表し、オリジンサーバからデータを取得する必要はありません。これにより高速化が実現できます。デフォルトでは、キャッシュは 24 時間保持されますが、必要に応じてこれをもっと長くしたり短くしたりすることができます。

それでは、オリジンサーバを設定しましょう。

オリジンサーバのセットアップ

CloudFront はオリジンサーバとして web サーバを使うことも、Amazon S3 を使うこともできます。本デモでは、設定とアップロードが簡単である S3 を使用しています。

  1. AWS マネジメントコンソールを開き、コントロールパネルから S3 を検索して開いてください。
  2. Amazon S3 のサービス画面で新しいバケットを作成してください。
    • バケットの名前を全ての Amazon S3 でユニークである必要があります。本デモでは「cfdemo.amazongametech.com」としました。
    • 設定はデフォルトのものを利用します。
    • アクセス権は全て非公開です。S3 からではなく、CloudFront からだけ、ファイルへのパブリックアクセスを許可します。
  3. ファイルをアップロードします。
    • まず、S3 バケットに「OldFormat」、「NewFormat」という名前で2つのフォルダを作ります。
    • それぞれのフォルダにアクセスし、「Upload」ボタンをクリックし、こちらのGitHubプロジェクトから取得したファイルをアップロードしてください。
    • アクセス権の設定はそのままにします。繰り返しとなりますが、これらのファイルは CloudFront からだけアクセスができます。

CloudFront のセットアップ

ここで私たちは「ディストリビューション」と呼ばれるものをセットアップします。これは、エッジサーバがどのように動作するかを指定する方法です。次に、作成したディストリビューションをデプロイして、実際にエッジサーバにその機能を実行させます。

  1. AWS マネジメントコンソールを開き、コントロールパネルから CloudFront を検索して開いてください。
  2. 「Create Distribution」をクリックしてください。
  3. 「Web Delivery method」を選択してください。
  4. オリジン設定で、「Origin Domain Name」をクリックすると、アクセス可能なリソース一覧がドロップダウンで確認できます。その中から先ほど作成した S3 バケットを選択してください。
  5. キャッシュ動作の設定はデフォルト設定のままとします。
  6. Distributions の設定を次の通り修正します。
    • このデモでは世界中のユーザにデータを提供することを想定し、「use all edge locations」の設定を選択します。もし提供範囲がより狭い地域である場合は、Edge distribution の範囲をあなたが提供する地域に限定することでコストを抑えることができます。
    • 独自のドメイン名を使用することが可能ですが、本デモではリクエストがゲームクライアントのコードで行われるため、プレイヤーがドメイン名を確認できないことから特に設定しません。
  7. ディストリビューションを作成します。
  8. しばらく待ちます。このプロセスではデプロイするまでに 20-40 分ほどかかりますが、最終的にステータスが「In Progress」から「Deployed」に変わります。
  9. ディストリビューションの作成が完了した後は、CloudFront が S3 に格納されたプライベートファイルにアクセスできるようにオリジンアクセスアイデンティティを作成します。
    • この設定をする理由は、Amazon S3 に格納したファイルをパブリックには公開せずに、Lambda@Edge を利用して正しいファイルを選択できるようにするためです。
      • 「distribution」をクリックします。
      • 「Origins and Origin Groups」のタブをクリックします。
      • 先ほど作成したオリジンを選択し、「edit」をクリックします。
      • バケットアクセスを制限するために「Yes」選択します。
      • まだ設定がされていない場合は、「create a new identity」を選択します。
      • 「update bucket policy」ボタンをクリックすると、S3 バケットのアクセス権限をするパーミッションが設定されます。
  10. ここまでの設定が終わると、CloudFront のこのディストリビューションで作成された URL を利用することができます。この URL は S3 バケットのルートディレクトリにマッピングされます。そのため、アップロードしたファイルのフォルダーと名前を指定することで動作確認することができます。例えば、http://a123abcdef1abc.cloudfront.net/NewFormat/some_data_file.dat にアクセスして、ファイルがダウンロードできることを確認してください。

Lambda@Edge の作成

  1. 現在、Lambda@Edge はバージニア北部リージョンでのみ設定ができるため、別のリージョンを表示している場合はコンソールの右上よりリージョンを変更する必要があります。使用量と料金は、エッジサーバで実際に関数が実行された分だけ発生します。
  2. Lambda のコンソールを開きます。
  3. 前述のとおり選択できるトリガーは 4 つあります。今回はプレイヤーから CloudFront へのリクエストをトリガーとするビューワーリクエストをトリガーとします。 つまりプレイヤーが URL をリクエストすると Lambda が実行されます。
  4. 「Create function」をクリックします。
  5. 「Blueprint」を選択します。
  6. 今回実行するコードは CloudFront で AB テストを実行するコードと似ているため「cloudfront-ab-test」を検索します(なお、AB テストのために Lambda@Edge を利用することは良いユースケースの 1 つです)。
  7. 関数に任意の名前を設定します。
    • 例えば「DeliverVersionAppropriateContent」と設定します。
  8. Lambda を実行できる権限を持った IAM ロールが必要なため、テンプレートから新しい IAM ロールを作成し、任意の名前を設定します。
    • 例えば「LambdaEdgeDemoRole」と設定します。
  9. ブループリントを選択しているため、テンプレートポリシーはすでに設定されています。
  10. 関数を作成します。
    • コードは後で修正しますが、現時点ではプループリントの AB テストのコードを使用して作成します。
    • AB テストのブループリントを使用したため、すでにトリガー設定がされています。この設定がビューワーリクエストとなっていることを確認します。
  11. Lambda のアイコンと関数名があるボックスをクリックして、コードを修正します。
  12. 今回 Node.js でコードを作成しますが、サンプルを用意しているため、必要なメソッドをすぐにご利用いただけます。
  13. コードではリクエストを処理します。リクエストの詳細な情報はこちらのガイドをご確認ください。
  14. 次のコードをコピー&ペーストします。
    • サンプルコードはこちらのGitHubからも確認することができます。
    • 'use strict';    
      exports.handler = (event, context, callback) => {
          const request = event.Records[0].cf.request;
      
          if (request.uri !== '/some_data_file.dat') {
              // for the demo we only want to deal with requests for this file
              callback(null, request);
              return;
          }
      
          const pathNewData = '/NewFormat';
          const pathOldData = '/OldFormat';
      
          let revisedUri;
          
          const querystring = require('querystring');
          var queryObj = querystring.parse(request.querystring)
          if('version' in queryObj) {
              if(parseFloat(queryObj.version) > 10.1) {
                  revisedUri = pathOldData + request.uri;
              }
              else {
                  revisedUri = pathOldData + request.uri;
              }
          }
          else {
              // assume old format before we started passing in the version
              revisedUri = pathOldData + request.uri;
          }
      
          request.uri = revisedUri;
          console.log(`Request uri set to "${request.uri}"`);
          callback(null, request);
      };
  15. コードの作成ができたため、エッジサーバにデプロイする必要があります。Lambda の設定画面の「Designer」でトリガーをクリックします( CloudFront のデプロイが保留中と表示されます)。
  16. トリガーの設定で「Deploy to Lambda@Edge」をクリックします。
  17. 次の設定画面で、CloudFront イベントを ビューワーリクエストに変更します。
  18. そのほかの設定はデフォルトで設定を進めます。
  19. 「acknowledge」のチェックボックスをクリックして、デプロイをします。
  20. デプロイがされるまで数分待ってから、ブラウザで動作確認をします。
    • 設定したコードを確認するとプレイヤーが URL で送ったパージョンに関するキークエリパラメータを参照していることがわかります。例えば、http://a123abcdef1abc.cloudfront.net/some_data_file.dat?version=9.0 (もしくはキークエリパラメータ全体を除去した場合)では古いコンテンツを返し、http://a123abcdef1abc.cloudfront.net/some_data_file.dat?version=11.0 では新しいコンテンツを返します。

ゲームクライアント

GitHub で公開しているサンプルのゲームクライアントを利用することで、ファイルダウンロードにおける動作確認ができます。またこれまで確認したようにブラウザによる動作確認をすることもできます。

クライアントは非常にシンプルです。AWS C++ SDK を初期化し、ユーザが入力したバージョン番号に基づいて、クライアントはシンプルな HTTP の GET リクエストを生成し、適切なバージョンファイルを表示します。

本デモを試した後は作成したリソース(特に CloudFront のディストリビューションと Lambda )を削除することをオススメします。これにより不要な課金を防ぐことができます。

本ブログで紹介する内容は以上となります。Lambda@Edge の詳細はこちらのドキュメントをご確認ください。また、Lambda@Edge の役に立つ利用例はこちらをご確認ください。

もし質問がある場合は、こちらのフォーラムからご質問ください。

飜訳はSA三上が担当しました。原文はこちら