Amazon Web Services ブログ

Lambda@Edge – エッジで HTTP リクエストを”賢く”処理する

昨年、Lambda@Edgeプレビューをアナウンスし、お客様に近いロケーションでHTTPリクエストを”賢く”処理する方法を説明しました。プレビューに応募しアクセスを許可された開発者は Lambda@Edge を有効に活用し、非常に有用な多くのフィードバックをいただきました。プレビュー中に HTTP レスポンスの生成、CloudWatch Logs のサポートを追加し、フィードバックに基いてロードマップを更新しました。

一般提供の開始

本日、 Lambda@Edge の一般提供を開始できることを嬉しく思います。次のように利用できます:

  • A/B テストを行うために cookie を検査し、 URL を書き換える
  • ユーザーエージェントヘッダに基づいて、特別なオブジェクトを返す
  • オリジンにリクエストを許可する前に、特定のヘッダを検索することでアクセスコントロールを実装する
  • ヘッダを追加、削除または変更して様々なキャッシュ済オブジェクトに誘導する
  • HTTP レスポンスを生成する
  • 古い URL 形式のサポート
  • ヘッダや URL を変更または簡略化して、キャッシュ使用率を改善する
  • 他のインターネットリソースへ HTTP リクエストを行い、その結果を使用してレスポンスをカスタマイズする


Lambda@Edge により、リッチでパーソナライズされた Web ベースのユーザー体験を作ることができます。(訳注:エッジロケーションは) 今日世界中に急速に普及し続けているため、サーバーをプロビジョニングしたり、管理する必要はありません。単にコードをアップロードし ( Lambda 関数は Node.js で書かれています )、ディストリビューション ID 、キャッシュ動作、希望する CloudFront をそれぞれ1つ選択するだけです:

このケースでは、関数 ( 想像力を働かせて名付けた EdgeFunc1 ) は、指定されたディストリビューション ID 内の image/* オリジンリクエストに対して実行されます:


ビューアリクエスト – このイベントはイベントがビューワー ( HTTP クライアント、一般的には web ブラウザやモバイルアプリ ) から届いた時にトリガーされ、受信した HTTP リクエストにアクセスできます。ご存じのように、各 CloudFront エッジロケーションはオブジェクトの大きなキャッシュを維持しているため、繰り返しリクエストに効率的に応答できます。このイベントはリクエストされたオブジェクトが既にキャッシュされているかに関わらずトリガーされます。

オリジンリクエスト – このイベントはエッジロケーションにリクエストされたオブジェクトがキャッシュされておらず、オリジンにリクエストを転送する時にトリガーされます。オリジン ( 多くの場合は S3 バケットや EC2 インスタンス上で動作するコード ) に転送するリクエストにアクセスできます。

オリジンレスポンス – このイベントはオリジンがレスポンスを受け取る時にトリガーされます。オリジンからのレスポンスにアクセスできます。

ビューワレスポンス – このイベントはエッジロケーションがビューワにレスポンスを返す前にトリガーされます。レスポンスにアクセスできます。

関数はグローバルに複製され、実行リクエストは自動的に最適なロケーションにルーティングされます。一度コードを書くだけで、他に何もする必要はありません。世界中のユーザーが低レイテンシで利用可能です。

コードはヘッダ、 cookie、HTTP メソッド ( GET, HEAD など ) 、 URI を含むリクエストとレスポンスの全てにアクセスできます。いくつかの制限がありますが、既存のヘッダの変更や追加が行えます。

Lambda@Edge インアクション

ビューワリクエストイベントに応答して実行される簡単な関数を作成してみましょう。 Lambda コンソールを開き、新規関数を作成します。 Node.js 6.10 ランタイムを選び、cloudfront のサンプルコードを検索します:

cloudfront-response-generation を選択し、関数を呼び出すためにトリガーを設定します:

Lambda コンソールは関数の実行環境についていくつかの情報を表示します:

いつも通り、関数の名前と説明を入力します:

サンプルコードは完全に機能する関数が含まれます。HTTP 200 レスポンスと非常に簡単な レスポンス body を生成します:

このコードを出発点として使用し、リクエストからいくつかの興味深い値を取得し、テーブルに出力します:

'use strict';
exports.handler = (event, context, callback) => {

    /* Set table row style */
    const rs = '"border-bottom:1px solid black;vertical-align:top;"';
    /* Get request */
    const request = event.Records[0].cf.request;
   
    /* Get values from request */ 
    const httpVersion = request.httpVersion;
    const clientIp    = request.clientIp;
    const method      = request.method;
    const uri         = request.uri;
    const headers     = request.headers;
    const host        = headers['host'][0].value;
    const agent       = headers['user-agent'][0].value;
    
    var sreq = JSON.stringify(event.Records[0].cf.request, null, ' ');
    sreq = sreq.replace(/\n/g, '<br/>');

    /* Generate body for response */
    const body = 
     '<html>\n'
     + '<head><title>Hello From Lambda@Edge</title></head>\n'
     + '<body>\n'
     + '<table style="border:1px solid black;background-color:#e0e0e0;border-collapse:collapse;" cellpadding=4 cellspacing=4>\n'
     + '<tr style=' + rs + '><td>Host</td><td>'        + host     + '</td></tr>\n'
     + '<tr style=' + rs + '><td>Agent</td><td>'       + agent    + '</td></tr>\n'
     + '<tr style=' + rs + '><td>Client IP</td><td>'   + clientIp + '</td></tr>\n'
     + '<tr style=' + rs + '><td>Method</td><td>'      + method   + '</td></tr>\n'
     + '<tr style=' + rs + '><td>URI</td><td>'         + uri      + '</td></tr>\n'
     + '<tr style=' + rs + '><td>Raw Request</td><td>' + sreq     + '</td></tr>\n'
     + '</table>\n'
     + '</body>\n'
     + '</html>'

    /* Generate HTTP response */
    const response = {
        status: '200',
        statusDescription: 'HTTP OK',
        httpVersion: httpVersion,
        body: body,
        headers: {
            'vary':          [{key: 'Vary',          value: '*'}],
            'last-modified': [{key: 'Last-Modified', value:'2017-01-13'}]
        },
    };

    callback(null, response);
};

ハンドラを設定し、 基本的なエッジ Lambda のアクセス権限 を持つ、新しい IAM ロールの作成を行います:

次のページで設定を確認し ( 通常の Lambda 関数と同じです ) 、 関数の作成 をクリックします:

関数を作成すると、ディストリビューションにトリガーを関連付け、グローバル複製を開始します。複製中は、ディストリビューションのステータスが In Progress に変わります ( 通常 5分 – 8分かかります ):

複製が完了すると、ステータスはすぐに Deployed に戻ります:

それからディストリビューションのルートにアクセスすると (https://dogy9dy9kvj6w.cloudfront.net/)、関数が実行され、こちらを確認できます:

コードを実行するには、画像をクリックしてください ( ディストリビューションのルートにリンクされています )

いつものように、これは非常に簡単な例であり、もっと良くすることができます。こちらに始めるためのいくつかのアイディアがあります:

サイト管理 – メンテナンスやディザスタリカバリ作業中に全ての動的 web サイトをオフラインにし、 Lambda@Edge 関数でクリティカルなページを置き換える。

大量アクセスコンテンツ – エッジで利用可能で、早さと費用対効果を両立した、スコアボード、天気予報、公衆安全ページを作成する。

なにかクールなものを作成し、このブログポストのコメント欄でシェアしてください。

知っておくべきこと

Lambda@Edge をアプリケーションで使用することを考える際は、以下の点に注意してください:

タイムアウト – オリジンリクエストとオリジンレスポンスイベントのタイムアウトは 3 秒です。ビューワーリクエストイベントとビューワーレスポンスイベントのタイムアウトは 1 秒です。

バージョニング – Lambda コンソールでコードを更新した後、新しいバージョンを発行し、新しいトリガーを設定する必要があります。そして複製が完了するのを待ちます。常にバージョン番号を使用してコードを参照する必要があります。 $LATEST とエイリアスは適用されません。

ヘッダ – コードを見るとわかるように、HTTP リクエストヘッダは配列としてアクセスできます。ヘッダは4つのカテゴリに分類されます:

  • アクセス可能ヘッダ – 読み取り、書き込み、削除、変更が可能
  • 制限されたヘッダ – 制限されたヘッダをオリジンに転送するよう設定されている場合のみ、書き込み、変更が可能
  • 読み取り専用ヘッダ – 読み取りのみ可能
  • ブラックリスト – コードから読み取り不可、追加も不可

実行環境 – 関数ごとのメモリは 128 MB 利用できますが、ビルトインライブラリは存在せず、 /tmp へのアクセスは許可されていません。

Web サービスへのアクセス – オリジンリクエストとオリジンレスポンスイベントのタイムアウトは 3 秒です。これらのイベントでは、関数は HTTP 経由 で AWS API やコンテンツを取得できます。これらのリクエストは常にオリジナルのリクエストやレスポンスと同期して行われます。

関数の複製 – 既に記載の通り、関数はグローバルに複製されます。複製 (Replica) された関数は Lambda コンソールの”他の” region から確認できます:

CloudFront – CloudFront と CloudFront キャッシュ動作 についての全ては Lambda@Edge に関連しています。複数のキャッシュ動作 ( それぞれ最大4つの Lambda@Edge 関数 ) でヘッダと cookie の転送などをカスタマイズすることが可能です。また、( 関数のバージョンを含む ARN を使用して ) CloudFront キャッシュ動作を編集する際に、イベントと関数を関連付けることもできます:

いますぐ利用可能

Lambda@Edge はいますぐ利用可能で本日から使用開始できます。価格は関数が呼び出された回数と実行された時間に基いています。 ( 詳細については Lambda@Edge の価格ページを確認してください )

Jeff;

原文:Lambda@Edge – Intelligent Processing of HTTP Requests at the Edge

(翻訳:SA藤原)