AWS Summit 2014 Tokyo「Amazon CloudFrontを利用したサイト高速化およびセキュア配信」レポート

2014.08.30

こんにちは、虎塚です。

先月のAWSサミットで行われたセッション「Amazon CloudFrontを利用したサイト高速化およびセキュア配信」をタイムシフト聴講しましたので、レポートします。

CloudFrontといえば、つい先週も新しい機能が追加されて盛り上がっていましたね。

- 【新機能】Amazon CloudFrontがSSLセッションチケットに対応しました | Developers.IO

今回の講師は、メディア系、コンテンツ配信系、アーカイブのお仕事をされている、アマゾン データサービス ジャパンの北迫清訓さんです。

1. Amazon CloudFront

おさらい

Contents Delivery Network (CDN)
世界各国にあるエッジサーバのキャパシティを効率的に利用して、配信を高速化するサービス
  • ユーザから見るとレスポンス向上: エッジサーバがユーザに近いところにあるので、配信が高速化する
  • 配信側から見ると負荷軽減: エッジサーバがコンテンツをキャッシュするので、オリジンサーバの負担を減らすことができる

動きを簡単に説明すると、次のようになる。

  • クライアントがオリジンサーバに向けてアクセスすると、CDNの近いキャッシュに誘導される
  • キャッシュがなければオリジンサーバから取得する
  • それ以降はキャッシュから配信する

最適なエッジサーバへの誘導の仕組み

最適なエッジサーバへの誘導は、AWSのDNSを使って実現している。

  • CloudFrontを作成すると、ディストリビューションというものができる
    • これの名前を管理するDNSがAWS側にある
  • クライアントがCloudFrontの生成した名前を解決しようとして、AWSのDNSに問い合わせる
  • AWSのDNSが位置情報を持っているので、ユーザのアクセス元地域を判定して、クライアントに近いエッジサーバを返す

エッジサーバは世界各地にあり、2014年7月に52個目のエッジサーバがメルボルンにできた。日本では、東京の2ヶ所と大阪の1ヶ所にある。

CloudFrontによるサイト高速化

一般的なサイト高速化の裏側の動き

データを取得する流れは、HTTPリクエストをしてDNS lookupを行い、見つけたサーバにTCPコネクションを作って、GETリクエスト等のデータをSendし、データ取得する、といった動きになる。ふつうはページを取得してから、付随する画像やCSSなどのファイルを取得する。

じつは「HTTPリクエストにおける80:20の法則」では、メインのページは全体の20%、それ以外の静的コンテンツ(画像、動画、CSS、JavaScript、静的HTMLなど)の取得に80%の時間がかかっているといわれている。たとえば、Amazonのトップページでは約280個のリクエストを投げている。

動的なページの高速化にフォーカスしがちだが、静的コンテンツのダウンロード時間がじつは長い。リクエストに必要な様々な処理の時間をそれぞれ少しずつ短くすることで、サイトアクセス全体の時間を短くできる。

AWSがやっているように適切な場所にエッジサーバを置くことは、それに寄与する。

  • ユーザに物理的に近いので、ネットワークアクセスが速い
  • レスポンスキャパシティを活用する
  • オリジンサーバのデータをフェッチする

CDN Edge Locationの活用

通常、クライアントからのリクエストは、いくつかのISPを経由してデータを取得する。CloudFrontのエッジサーバは、IX(インターネット相互接続ポイント)にあり、そこから複数の足を出している。そこにキャッシュを置いている。あらゆるクライアントから平均的にアクセスを短縮できるように、エッジサーバを配置している。

そのため、グローバル配信でない、日本からのアクセスだけがあるサイトではCloudFrontを使うメリットがない、という人がいるが、東京のキャッシュサーバを使うだけでも、サイト高速化に意味がある

また、クライアントが増えると、セッションを張る量やデータ転送時のネットワークのやり取りが増えるため、通常はオリジンサーバの負荷が増える。しかし、CloudFrontでは大量のキャッシュサーバを構えているので、アクセスが増えてもサーバやネットワークのキャパシティをあまり気にしなくて済む。

CDNの種類

CDNには2種類あり、CloudFrontは集中型CDNにあたる。

分散型CDN
ユーザに最も近いISPに、ごく小さなキャッシュを置く
物理的にユーザに近いのでレスポンスタイムが速くなるが、エッジサーバのキャパシティが小さいのでキャッシュヒット率が下がる傾向がある
集中型CDN
回線の真ん中となるIXに大規模データセンターを構えて、大量のキャパシティを持つ。キャッシュは共有される。
キャッシュヒット率が高い傾向がある

昔はクライアントへのラスト1マイルの回線が細く、安定もしていなかったので、分散型CDNが注目されていた。現在では回線が安定しているので、集中型CDNが注目を集めている。IXにどれだけ足を出しているかと、太い回線を持っていることがポイントになる。

コンテンツの種類によるキャッシュの使い方のポイント

静的コンテンツが80%、動的コンテンツが20%といわれる中で、静的コンテンツをキャッシュする際のポイントについて。

  • 静的コンテンツは、可能な限りすべてエッジサーバにキャッシュさせる
    • キャッシュTTLは可能な限り長くする
    • クライアント側にもキャッシュさせる

一方、動的コンテンツには2種類あるので、それぞれに合った使い方をすることが大事。

1. ページ共通で使われる動的コンテンツ
一定期間は更新されないもの

  • たとえば、商品詳細(foo.html?item_id=012345)、カテゴリ一覧(foo.html?category=cloud)、人気商品一覧(foo.html?top=10)などのように、Query Stringsがよく使われる
これらの動的コンテンツについては積極的にキャッシュすべき

  • 日、時間、分、秒単位でキャッシュを存続させる
  • Cache-Control HeaderとMinimum TTLを調整して、キャッシュの有効時間を設定する
2. リクエストごとに内容が変更される動的コンテンツ
  • ある程度グルーピングできるもの
  • たとえば、HTTPヘッダを判定してクライアントの端末に適した形で返されるコンテンツや、パーソナライズされた情報
これらの動的コンテンツについては短時間でキャッシュを更新する

  • FAQ: 数分、数秒単位でキャッシュを無効にすると、エッジサーバを経由することでかえってオーバーヘッドがかからないか? → LastModifiedなどのETag(エンティティタグ)をヘッダをつけることで、キャッシュからIf-Modified-Sinceでオリジンサーバに問い合わせてくれる
    • コンテンツが更新されていなければオリジンサーバが304(Not Modified)を返し、キャッシュが再利用されるので、無駄なデータ転送が起きない
  • 更新があると、最適化された通信でエッジサーバにデータを送り出す。クライアントに送り返しながらキャッシュを行う

キャッシュヒット率の向上方法

CloudFrontはURLの完全一致でキャッシュを共有するのが、重要なポイント。

  • キャッシュ時間
    • 長ければ長いほどヒット率は上がる
  • URLの共通化
    • CloudFrontでQuery StringsオプションをONにすると、Query Stringsも含めてキャッシュする。完全一致しないとキャッシュが共有されないので、パラメタも固定化する
    • パラメータにユーザのセッションIDなどをつけるケースがよくあるが、ヒット率が落ちてしまうので注意
  • Etagの活用
  • 転送対象Header値の固定化
    • Headerも完全一致でチェックするので、コンテンツの内容を分類化しやすいHeaderをキャッシュ対象にする

キャッシュヒット状況の確認方法

HTTPクライアントでレスポンスヘッダのX-Cacheを確認する
「Miss from cloudfront」: オリジンサーバからフェッチした
「Hit from cloudfront」: キャッシュにヒットした
「RefreshHit from coudfront」: キャッシュ有効期限切れだが、オリジンサーバが304を返すなどして、キャッシュが再利用された

動的コンテンツでの活用

キャッシュができない動的ページは、エッジサーバをプロキシとして利用するとよい。

Dynamic Contents Acceleration: エッジサーバとオリジンサーバ間の通信が最適化される機能。

POST/PUT, Header, Cookie対応
CloudFront: POST/PUTリクエストがきたときPROXYとして動作する機能や、オリジンサーバへHeaderやCookie情報を転送する機能を持つ
キャッシュできない動的コンテンツもエッジサーバを経由して扱えることで、以下の機能が効いてくる
Keep-Alive Connection
通常: ユーザごとに3way handshakeをするので、オリジンサーバへの負荷が増加する上にレスポンスタイムもかかる
CloudFront: エッジサーバとオリジンサーバの間でTCPコネクションを貼り続けてくれる。3way handshakeをクライアントからエッジサーバの間だけ行う。エッジサーバとオリジンサーバの間は、最初の1回だけ行う。これによって速度が2倍程度速くなる
TCPスロースタート
通常: ネットワークの輻輳回避のため、受信に成功したら少しずつパケットを増やして通信する。これをユーザごとに行うため時間がかかる
CloudFront: 同じオリジンサーバに対して別のユーザがアクセスした時、スロースタートシーケンスをバイパスする機能。1人目のアクセス時は従来と同じ動作だが、2人目からはいきなり大量のデータをエッジサーバから受け取れる

DNS Lookupの高速化

CloudFrontではDNSのLookupについても高速化の施策が打てる。

ClourFrontからオリジンサーバに接続する時のホスト名の名前解決には、Route 53を使うとDNS lookupが速くなる
Route 53はCloudFrontと同じように世界52箇所にDNSサーバを構え、レプリケーションしている
Anycast IPアドレスが使用されるので、DNSレコードが引かれた時に、同じIPアドレスでもリゾルバに最も近いDNSサーバが応答する
Route 53でAlternative Domain Nameの名前解決をするときは、CNAMEでなくAレコードのAliasとしてレコードを登録することで、クエリの回数を減らせる
CNAME: 1回目の問い合わせで、内部ではまず独自ドメインのCNAMEを引き、それからもう一度CloudFrontのCNAMEを引き直す
AレコードとAliasの組み合わせ: 1回目の問い合わせで直接CloudFrontのアドレスが返ってくる

ここまでのまとめ: Webアクセスの高速化

CloudFrontを使うことで、サイトへのWebアクセスを高速化するための様々な施策を打てる。

  • 物理的なネットワーク速度
  • エッジサーバでのキャッシュおよびキャパシティの活用
  • オリジンサーバとの通信の最適化
  • DNSクエリ

CloudFrontでの設定

静的コンテンツと動的コンテンツが混在する環境では、CloudFront Behaviorを上手く活用して、コンテンツの種類ごとにキャッシュ時間を制御する。

静的コンテンツ
アクセスURLごとに正規表現が指定して、キャッシュの時間を設定できる
(例)image/ディレクトリ以下に入っているファイルのキャッシュ期間をすべて長くする
動的コンテンツ
オリジンサーバ側でキャッシュヘッダにno-chaceh, no-storeを設定することで、コンテンツの種類によって、CloudFront側で一定期間キャッシュさせる
それ以外はUse Originの設定をすると、キャッシュせずバイパスする(プロキシとして動作する)

オリジンサーバ側でコンテンツごとに細かい設定をしなくても、CloudFrontで設定するだけで、ある程度キャッシュの方法を制御できる。

CloudFrontによるセキュア配信

  • HTTPSサポート
  • GEO Restriction: 特定の地域からのアクセスだけ許可する
  • Signed URL: 署名付きURL

これらの機能があるが、今回はSigned URLにフォーカスして紹介する。

Signed URL

有効期限を指定したワンタイムのURLを生成する機能。何らかのユーザ認証とあわせて使う。認証が成功した時に、裏でCloudFrontの秘密鍵を使って、CloudFront経由でアクセスするコンテンツのURLに署名し、クライアントに返す。この時に、URLの有効時間を設定できる。クライアントがURLからアクセスした時、署名されたURLの有効性をチェックし、コンテンツか、もしくはエラーを返す。

FAQ: クライアントからオリジンサーバのコンテンツに直接アクセスされたら、どうやって保護すればよいか
オリジンサーバがS3なら、Origin Access Identity機能で、特定バケットに特定のCloudFrontからしかアクセスできないように設定する
オリジンサーバがWebサーバなら、CloudFrontのIPアドレスは公開されているので、そこからしかアクセスできないように設定する

Signed URLの仕組み

指定可能なポリシーには次のものがある。

規定ポリシー(canned policy)
コンテンツ1つに対して1つの署名が必要
  • アクセス有効終了時刻
  • 許可コンテンツのフルパス
カスタムポリシー
パスにワイルドカードを指定できるので、同じ署名を使い回せる
  • アクセス有効 開始・終了時刻
  • アクセス元IPアドレス制限

Signed URLの作成方法

  1. ポリシー定義をJSONフォーマットの文字列で準備
  2. AWSアカウントのCloudFront用秘密鍵で1を暗号化
  3. コンテンツURLのQyery Stringsに、2をパラメタとしてセット

CloudFront用の秘密鍵を鍵生成サーバに置き、URLをプログラム側で作成するだけで利用できる。

Signed URLの利用領域

  • プレミアムコンテンツ配信に(プレミアムコンテンツとは: 特定の会員や購入者にだけダウンロードを許可するコンテンツ)
  • ストリーミング配信に

既存の認証システムと連携して、簡単かつセキュアに配信できる。

Signed URL Tips

ダウンロードコンテンツ配信時の有効期間の設定
有効期間を短く設定してよい
コネクションが生きている期間は、有効期限が切れてもダウンロードを続けられる
大きなサイズのコンテンツの場合も、ユーザがコネクションを張り始める時間だけを気にすればよい
ストリーミングコンテンツ配信時の有効期間の設定
再生時間(動画の尺)+αを設定する
CloudFrontの設定
BehaviorごとにURLの有無が指定できる
アクセスURLの正規表現を利用することで、Distributionを分けずに特定のコンテンツだけ保護できる
アクセス方法
HTTPSやGeo Restrictionと組み合わせる
キャッシュの有効活用
キャッシュされたコンテンツにもSigned URLは有効
クライアントにキャッシュさせたくないコンテンツは、Cache-Controlヘッダのno-cache, no-storeとCloudFrontのカスタムTTLで調整する

まとめ

  • CloudFrontを活用してインフラアプローチでもサイトを高速化できる
  • これまでCDNの活用が難しかった動的コンテンツにもCloudFrontは使える
  • セキュアコンテンツ配信もCloudFrontで容易に高速化できる

チリも積もれば速くなる」: 数msecの節減が、サイトを大きく速くする!

感想

情報量たっぷりのセッションでした! CloudFront関連では、聞き慣れない用語が次々と出てきますが、まとめて聞くと理解が深まりますね。

コンテンツのURLに対して正規表現でキャッシュの制御ができるのは、とても便利そうです。また、SignedURLの仕組みは、これまでずっとアプリケーション側で対応しなければいけなかった問題を解決するものですよね。こういった要件が必要な局面では、上手く活用していこうと思います。

それでは、また。