7月 022017
 

最近何かと話題なCDNですが、そもそもCDNってなんだろう・・・どんなことに使えるんだろう?的なことを書いてみようと思います。
一応先に言っておくと、私はCDN業者に所属したことないのであくまでも利用者として見た時の話を書きます。
また、私の考えであり、様々なワークロードがあるなかでこれがすべてではありませんので、こんな考えもあるんだなぁぐらいに思ってもらえると助かります。

そもそもCDNってなんだろうか

そもそもCDNはContent Delivery Networkの略であってCache Delivery Networkの略ではありません。
要はコンテンツをクライアントに対して高速・効率的に配信するためのネットワークです。
良くCDNといえばその成り立ちからキャッシュというイメージはありますが、重要な要素の一つではあるもののCDNの全てではありません。
さらに言えばAkamaiのIntelligent PlatformやFastlyのEdge cloud platform、CloudFlareでは特に特有の名前はついていませんがOur platformと表現していますが、要はCDNはプラットフォームなのです。その上にキャッシュや後述する様々な機能などがアドオンされていると思ってもらえると良いかと思います。

CDNの作り方

CDNの正体とはなんでしょうか?
割とブラックボックスのように思えると思いますが、実はProxy(配信サーバ)とGSLB(広域負荷分散)を基本としたものです。
なので言ってしまうと、AWSのRoute53のGeo, Latencyベースのルーティングを使い、Proxyをいろんなロケーションに立ててしまえばあなたもCDN管理人に!
そして、作ったプラットフォーム上にサムネイルサーバを置いてしまえば動的サムネイル生成機能が付いたCDNになりますし、エンコーダーを・・・WAFを・・・みたいな感じにどんどん機能が増えていきます。
誤解を恐れずにすごく乱暴に言ってしまえば、これを大規模にやっているのがいわゆるCDN業者といえるでしょう。
また、既存CDN業者にない機能を使いたい、より柔軟性がほしい、そもそも大トラフィック扱っていて自社のほうがコストメリットがあるなどで自前でCDNを作る人達もいます(DIY CDN)。もちろん全部置き換える人もいればハイブリッドで使う人達もいます。
例えばSpotifyは去年から自社でCDNを立ち上げてハイブリッドで使っているようです。(Spotifyの日本インフラ)

CDNの機能について

さて、ここまででCDNはプラットフォームでそこにキャッシュなどの様々な機能をアドオンしたものとわかったと思います。
では、CDNにはどんな機能があるでしょうか?メジャーなものを幾つか紹介します。
当然ですが、ここで紹介する機能すべてがどのCDNにもあるもものではなく、あったりなかったりします。(なのでうまく選んでいきたいですね)

キャッシュ
やはりCDNといえばやはりキャッシュ!
貴方が例えばスマホゲームのインフラ担当の場合、当然アセットのダウンロードをどうしようか考えると思います。
リリース時に一気にダウンロードが走り、そのトラフィックは数百Gbpsを超えるケースもあります。
リリース時や更新時の一瞬のためだけにインフラを構築して・・・というのはやはり無駄があります。
ここでCDNの出番です。
アセットは一般的には静的なコンテンツなので、キャッシュが可能です。
まずユーザがリクエストしてきたら、最寄りのCDNのエッジサーバにリクエストがされます。
キャッシュがあればそこでレスポンスしますし、なければ自社のサーバ(オリジンサーバ)に対してリクエストが着ます。
このことによりオリジンサーバへのトラフィックを減らすことができ、なおかつユーザーからみるとネットワーク的に近いエッジサーバから取得することでより快適にダウンロードすることが出来ます。
そして一般的にCDNの帯域キャパシティは大きく、また大抵の場合は配信サイズ(1GBあたりいくら)での課金になります。
なので100Gbpsでようが1Gbpsだろうがピークをあまり気にせずユーザにアセットを配信することが出来ます。
(※CDN業者によってはQuotaが設定されていたりするので予め予定がわかってる場合は相談しておくと良いかと思います)
また他にも動的コンテンツのキャッシュもありますがあとで触れます。

Dynamic site acceleration(DSA)
キャッシュ以外での高速化を行う技術群です。
AkamaiFastlyではそれぞれDSAという名称で提供されており、他のCDNでも特に明記はしてないもののなんらかのDSA技術を使っているところがほとんどだと思います。
ざっくり触れていきます

・コンテンツのgzip圧縮
圧縮することで転送サイズを小さくして高速にします。

・エッジでのTLS終端
ユーザに近いエッジ(レイテンシが小さいことが期待できる)でTLS終端を行うことでハンドシェイクを高速に行えます。

・TCPの最適化
モバイルNW向けにリアルタイムにパラメータ調整したりするものや
エッジからオリジンまでを最適化したりなど
様々な手法でいろいろあります。

・コンテンツのプリフェッチ
ベースページを通した場合、その中に含まれる画像などのコンテンツをプリフェッチしてエッジを温めておくことでRTTを小さくします。

・ルーティングの最適化
インターネットは様々な経路でつながっています。
そのため、物理的な直線距離と実際の通信経路での距離は違い、またレイテンシも変わってきます。
身近な例で例えるのであれば、自宅のISPを乗り換えたら経路が変わって特定のサイトが早くなるということも十分考えられます。
その経路を最適化するのがこの技術です。代表例だとAkamaiのSureRouteが有名です。

他にもあると思います。
ちなみにsonotsさんがDSAについてふれて、実際にベンチをとっておられました(CloudFrontをかますとキャッシュなしのAPIコールでも速くなるようだ

Front End Optimization(FEO)
クライアントが実際にページを表示するにはざっくりいって

  1. ベースページのDL
  2. CSSやJSなどのリソースをDL
  3. ページレンダリング

といった流れがあると思います。
DSAの場合ではあくまでも配信を改善することが目的なので1,2でのネットワーク通信部分しか改善することはできません。
コンテンツに対してはほぼ手を加えず、手を加えるにしてもgzip圧縮程度で非可逆的な処置は行いません。(その分安全とは言える)
しかしサイトがリッチになるにつれ、ページレンダリングの処理が重くなっていき、場合によっては秒単位でかかるページも存在します。
じゃぁ、そこに対して何かできることはないか?というのがということで出てきたのがFEOです。
すごくざっくりいうとコンテンツの改変を含む最適化行うことで速度を向上しようと言うものです。
mod_pagespeedのようなことをCDNでやると言った感じです。
ベースページを解析して改変したり、画像をより圧縮効率の高い別フォーマットや端末に合わせた変換をしたり、CSSを調整したりなどいろいろ行います。
近年デバイス数も増えているので(モバイル)、すべての端末に対して最適化を手作業で行うのは難しいですが、CDN業者がその辺をメンテしてくれるのであれば楽ということです。

セキュリティ
最近は様々なサイバー攻撃が存在し、それをガードするためにCDNを入れることもあります。
DDoS対策やWAF、bot対策など様々なメニューがあります。

一つ注意としてですが、オリジンドメインがバレると直接そこにアタックされてどうしようもなくなったりするので
アクセス元を制限したり、類推しづらい名前にしとくのがよいかなとか・・(オリジンがわかりやすい名前でオリジンにアタックきたという事例を聞いたことあるので・・・)

最新技術への対応
最近であればHTTP/2など、様々な技術が出てきます。
しかし自前でやるには難しい環境の場合もあるかと思います。その場合でもCDNがproxyしてやってくれたりすると最新技術を利用でき、またそれで高速化のメリットを享受できます。

その他
他にもいろんな機能があります。
ビデオストリーミングや
機械学習でページ中の動的・静的部分を判別して静的部分をフラグメントキャッシュするもの
エッジ側にコードがおける(FastlyのVCL,CloudFrontのLambda@Edgeなど)などいろいろあります。

ユーザ情報を含むなどキャッシュできない動的なコンテンツをCDNに通すべきか通さないべきか

答えはどちらでもないだと思います
通す・通さないべきではなく、必要であれば通せばよいとおもいます。
先に触れたとおりCDNにはいろんな機能があります。
同一ドメインにある静的コンテンツをキャッシュしたいから通すみたいなケースが多いと思いますが
そもそも仮に全部キャッシュが出来なかったとしても、他の機能(セキュリティ対策など)が有用であれば使えばいいという単純な話だと思います。

しかし、動的コンテンツをCDNに通すのを気にしている人で個人情報が・・セキュリティが・・と気にする人がいます。
実際にCDN業者でもいろんな事件がありました
CloudFlareでのデータ流出バグ
CDNetworksのコンテンツ改ざん
このようなものは外部業者を使う以上一定のリスクは存在しうると考えていますので
結局ここは信用できるところを選ぶといった対策しかできません。
※CloudFlareやCDNetworksの事故を取り上げてますが、この2社が現在もヤバイとは思っていません。例えばCloudFlareはFBIに使用されてますし、CDNetworksはログインドメインは使用していないもののりそな銀行が使用しています。
しかし、これをもってそもそも使うのがNGというのは個人的には賛同できません。
多くのCDNではPCI DSSに対して配慮しているものがありますが(Akamai Fastly CloudFront CloudFlare CDNetworksなど)
そもそも動的コンテンツを流すことが想定されていなかったりすればこのような対応はされていないと思います。
参考程度ですがCitibank・USBank・ジャパンネット銀行・マネックス証券はログインフォームも含めてAkamaiを使用していました。


$ nslookup online.citi.com
Server:         127.0.0.1
Address:        127.0.0.1#53
Non-authoritative answer:
online.citi.com canonical name = online.citibank.com.edgekey.net.
online.citibank.com.edgekey.net canonical name = e11515.b.akamaiedge.net.
Name:   e11515.b.akamaiedge.net
Address: 184.30.150.120

$ nslookup onlinebanking.usbank.com
Server:         127.0.0.1
Address:        127.0.0.1#53

Non-authoritative answer:
onlinebanking.usbank.com        canonical name = onlinebanking.usbank.com.edgekey.net.
onlinebanking.usbank.com.edgekey.net    canonical name = e3644.ksd.akamaiedge.net.
Name:   e3644.ksd.akamaiedge.net
Address: 104.78.14.64

$ nslookup login.japannetbank.co.jp
Server:         127.0.0.1
Address:        127.0.0.1#53

Non-authoritative answer:
login.japannetbank.co.jp        canonical name = login.japannetbank.co.jp.edgekey.net.
login.japannetbank.co.jp.edgekey.net    canonical name = e2588.ksd.akamaiedge.net.
Name:   e2588.ksd.akamaiedge.net
Address: 184.26.113.168

$ nslookup mst.monex.co.jp
Server:         127.0.0.1
Address:        127.0.0.1#53

Non-authoritative answer:
mst.monex.co.jp canonical name = mst.monex.co.jp.akadns.net.
mst.monex.co.jp.akadns.net      canonical name = mst.monex.co.jp.edgekey.net.
mst.monex.co.jp.edgekey.net     canonical name = e4187.g.akamaiedge.net.
Name:   e4187.g.akamaiedge.net
Address: 69.192.239.108


このことからわかるように銀行や証券のようなすごくセンシティブな情報を扱うサイトでもCDNをつかうのはナンセンスではないということです。
結局のところは得られるメリットに対して、どれだけリスクやデメリットの程度、そして見合うコストなのかの話で、そもそもNGというわけではないです。(もちろん正しく使えればという話ですが)
例えば、2017年の今にクラウドはセキュリティが怖いからオンプレミスにするとか言うエンジニアがいたら割と?と思われると思いますが(たまにTwで見かけますが・・)
これはCDNでも同じ話が言えると思います。
CDN業者のセキュリティが怖いから自社でCDN作るぞ!みたいなこと言ったら流石にあなた達はAkamaiやAWSたち以上のことできるんですか・・・と思ってしまいます。
CDNであまりこういう意識がないのは単純な話としてCDNを使うエンジニアの母数が少なく、その辺の声が小さいんだろうなーとか考えています。

キャッシュをする、そしてしない

キャッシュを行うときに注意するべきなのはなんでしょうか?
一番重要なことは、一つのキャッシュするオブジェクトに対して複数の意味を持たせないということです。
例えばhttp://example.net/mypageというページがあって、仮にこれをキャッシュするとします。
名前からわかるようにこれはユーザ情報を含んでいます。
通常CDNなどでキャッシュする際はホスト名とパスが指定されますので
それに基づくとキーは「example.net + /mypage」になります。
しかしこれだとユーザ情報をキーに含んでいないため、Aさんが見た後にBさんが見た場合、Aさんのmypageが出る可能性があります。
この場合はmypageはAさんのページでもあるしBさんのページでもあるという複数の意味をもたせたから起きることです。
正しいキーは「example.net + /mypage + Aさん」といったようになります。
これはCDNでのキャッシュに限った話ではなく、例えばmemcachedやRedisといったKVSでも言えますし、他のキャッシュと呼ばれるものでも共通の考え方だと思います。
ちなみにこれはログイン情報に限った話ではなく、例えば、同じ画像URLでSP向けとPC向けでサイズやフォーマットが違う場合も、同様にキーにSPなのかPCなのかを含める必要があります。
また、クッキーの取扱にも注意が必要です。
2014年にキャッシュ設定の不備からクッキーが他ユーザに共有されてしまったというサイトがありました。
当事者じゃないので推測しかできませんが、おそらくキャッシュしたオブジェクトにSet-Cookieがついてしまってそれが使いまわされてしまったんだろうと考えています。

これは私がキャッシュを行うときの基本的なポリシーを図にしたものです。
必ずしもこれがすべてのワークロードで正しいものではありません。
例えば元々オリジンが静的コンテンツを置いているだけであればここまで複雑なルールは必要ないですが
動的・静的・かつユーザ情報含む全コンテンツを通すケースにおいては、参考にはなると思います

※この図では先程ふれたSP/PCで違う場合といったものはいれてませんが、そういうものがあればキーに含めてください。
※ユーザ情報はクッキーに含まれていることを想定しています。なにか他のものに入ってる場合は適宜消す対象を含めてください(POSTとか注意したいですね)
※他にキーに設定したりするものとしてはCORSのOriginヘッダなどがあります。これはVaryで分けてもいいですが、オブジェクト自体が変わらないのが通常なので、CDNに条件付きでレスポンスヘッダのみの書き換えができるのであればでそちらで行うのが良いでしょう。(AkamaiやFastlyなどはできる)

まず、キャッシュをしない場合は必ずCDN側で抑制すべきと考えています。(もちろんCache-Controlを適切に設定するのも大事です)
CDN業者によって挙動が変わるため、確実なのはCDNの機能で「確実にキャッシュさせない」ことです。
なぜかというと単純な話として出口で対策をかけるのが確実だからです。
また、基本的に静的コンテンツしか載せないのであればブラックリスト方式(基本キャッシュで一部非キャッシュ)でもいいとおもうのですが、
サイトすべてをCDNに流していて、そこにユーザ情報を含むもの(キャッシュ・非キャッシュにかぎらず)があるのであればホワイトリスト方式(基本非キャッシュで一部キャッシュ)で運用すると安心できると思います。

また、私はあまりオリジンのアプリを信用していません。
例えばこのパスはキャッシュが可能でユーザ情報によって変化もしないのが確実だからCookieをオリジンに送信するとします。
しかし実際はアプリはCookieからユーザ情報を読み取って、ユーザ毎にコンテンツを生成していたらどうなるでしょうか? 流出です。
なので、大事なのはキャッシュをする場合はオリジンに対して不要なキー(Cookie)は送信しないということです。
Cookieを消しておけば、仮にアプリがユーザ情報を元になんらかのコンテンツを生成しようとしても情報がないので、ログインページに飛ばされるなどクリティカルではない障害ですみます。

また、同様にSet-Cookieも消すべきです。
これはセッションが共有されるのを防ぐというのが主目的です。
ではなぜキャッシュキーにユーザ情報が含んでる状態でも消すのかというと、cookieのexpiresの問題や何らかのワンタイムトークン的なものが含まれていたりとか合ってよくわからない動作になるのを防ぐためです。

TTLの取扱に気をつけよう
静的コンテンツの場合は、頻繁に同一URLでのコンテンツの差し替えがないのであればTTLはあんまり気にする必要はないのですが(その場合もpurgeすればいい話ですが)
動的コンテンツの場合はモノによっては注意が必要です。
例えば5分毎に更新されるコンテンツが1日もキャッシュされたら困るからです。
静的・動的と言ってますが要はそのURLで別のコンテンツが提供される可能性があるのであれば(差し替えであれ、定期更新であれ)TTLには注意しようと言う話です。
ここはそのコンテンツの特性に応じていろんな考え方があるのでこれで確実といったのは特にないので・・・(例えば必ず毎時0分に切り替わる必要があるとか、多少ぶれてもいいものとかいろいろある)

また、テストも重要です。
CDNのテストってどうやるんだ・・・という話もありますが、大抵の場合はそのオブジェクトがキャッシュ対象かなどをレスポンスヘッダで教えてくれたりします。(Akamai)
なのでその辺を元にテストを行えば問題ないでしょう。
テストパタンを考えるのはそんなに難しくはないと思います。
ユーザ情報を含むページも通してるのであれば、複数ユーザ+非ログインユーザで条件が変わるパス毎にテストをすればよいだけです。
そして意図した動作(Cookie/Set-Cookie消してるとかキャッシュしてるしてないなど)になっているかの確認をしましょう。
先程の図では触れていませんがキャッシュのテストをするときは、割とTTLも重要なので動作確認をしましょう。
TTLの動作(特に切れた場合)もCDN事業者によっていろいろあると思いますし設定可能です。
すべてのパタンではないですが、例をあげると
TTLが切れた場合でオリジンがエラーを吐いている場合はとりあえず古いオブジェクトを使う
という動きをするケースもあります。(Optionでなど)

ここまで言っておいてなんですが、すべてのCDN業者でこれほど自由度のある設定ができるとも限りません。
特にユーザ情報を含むコンテンツをキャッシュする際はかなりキャッシュキーに注意する必要がありますので、CDN業者でここまで設定できないのであればユーザ情報を含むコンテンツのキャッシュは辞めるべきです(そもそもやって意味があるのか的な話は後で触れます)、単純にキャッシュせずに通すだけであれば問題ないです。
他にも、CDN側で抑制出来ないパタンがあるのであれば、オリジン側のProxyでリクエスト・レスポンスヘッダのクレンジングするのも良いと思います。(同様のルールでクッキーの制御を行う)
オリジン側のProxyでクッキーを消していれば仮にCDNでキャッシュされても、やっぱり非ログインページになるのでそこまで痛くないでしょう。(もちろん2重管理にはなるのと完全にはカバーできないのでその辺は注意)
その場合はよりCDNの仕様を注意深く読み・問い合わせ、入念にテストを行うべきです。

あと、この辺の複雑な制御はFastlyがCDNの中でもトップレベルと考えています。
理由としてはVCLというDSLでキャッシュするしないの制御を始め、クッキーからユーザ情報を抽出してそれをキャッシュキーに設定するなど細かい制御が可能なためです。
例えば/admin/というページはキャッシュをしてはならないというルールをVCLで書くと


sub vcl_recv{
  if(req.url ~ "^/admin/"){
    return(pass);
  }
}

と言った感じになります。

ユーザ情報を含む動的コンテンツを効率的にキャッシュするには

ユーザ毎に生成されている動的コンテンツをキャッシュするのは難しいですし、普通はやりません(ある意味最終手段とも言える)
難しい理由としては

  • キャッシュ混じりなどが起きないようなキーの管理
  • ページを構成する要素のTTLが同期していない
  • そもそも再利用されるのかという話

こんな感じです。
キャッシュを行うのは何度も使用されることを期待して行うものですが、当然ながらユーザ毎にキャッシュしたものはユーザしか使用しません。
TTLが同期していないという話は例えば、ページ中に5分おきに更新される直近売れたものリストとユーザの行動によって変わるレコメンドがあるなど(これならXHRでやればいいという話もありますがたとえなので)
一体どれだけのTTLでキャッシュすればいいかがわからないケースです。
じゃぁどうすればいいかというと、少し発想を変えてみて部品単位で考えてみることです。
そうすると案外再利用されたりするものがあったりします。
もちろんそのための規格もあって、Edge Side Includes(ESI)というものです。
ざっくりいうとフラグメントキャッシュ持っておいてCDN側で結合する感じです。
これにより

  • 部品単位で適切なTTL、更には適切なキーでキャッシュできる(そのページがユーザ毎だったとしても部品がユーザ毎とは限らない)
  • 再利用しやすい

というふうになりますので
うまくすれば通常のサイトの場合は半分以上は実際キャッシュできると思います。
もちろんキーの管理とか難しいですし、バラバラに分解した上でユーザ情報を含まないところだけでキャッシュするだけでもかなり効果があります。
また、ゲームのようなあまり向かないものもありますのできちんとワークロードにあってるかは検討する必要がありますが良いかなと思います。
かなり昔に書いた資料ですが参考になるかもなのでよかったらどうぞ(VarnishではじめるESI)

まとめ

ここまで割とCDN使っていこうぜてきなノリで書いてきましたが、それぞれメリットやデメリット等いろいろあります。そしてもちろんコストもかかりますので、見合う見合わないを適切に判断した上で楽になれば使えば良いと思いますし、そうでないなら使わなければ良いと思います。
しかし動的コンテンツをそもそも流すのはナンセンスではないということは理解していただけたんじゃないかなーとか思います。
CDNも結局一つのレイヤーとして考えてもらって、もし貴方がCDNが解決できるかもしれない課題で悩んでるのであればこの記事が助けになれば良いかと思います。


 Posted by at 2:23 AM

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

(required)

(required)