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業者にない機能を使いたい、より柔軟性がほしいなどで今述べたように自前で作る人達もいます(DIY 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が有名です。

他にもあると思います。

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のコンテンツ改ざん
このようなものは外部業者を使う以上一定のリスクは存在しうると考えていますので
結局ここは信用できるところを選ぶといった対策しかできません。
しかし、これをもってそもそも使うのがNGというのは個人的には賛同できません。
多くのCDNではPCI DSSに対して配慮しているものがありますが(Akamai Fastly CloudFrontなど)
そもそも動的コンテンツを流すことが想定されていなかったりすればこのような対応はされていないと思います。
参考程度ですがシティバンクはログインフォームも含めて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

このことからわかるように銀行のようなすごくセンシティブな情報を扱うサイトでもCDNをつかうのはナンセンスではないということです
結局のところは得られるメリットに対してどれだけリスクやデメリットがあるかの話でそもそもNGというわけではないです。(もちろん正しく使えればという話ですが)
例えば、2017年の今にクラウドはセキュリティが怖いからオンプレミスにするとか言うエンジニアがいたら割と?と思われると思いますが(たまにTwで見かけますが・・)
これはCDNでも同じ話が言えると思います。
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で違う場合といったものはいれてませんが、そういうものがあればキーに含めてください。
※ユーザ情報はクッキーに含まれていることを想定しています。なにか他のものに入ってる場合は適宜消す対象を含めてください

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

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

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

またテストも重要です。
CDNのテストってどうやるんだ・・・という話もありますが、大抵の場合はそのオブジェクトがキャッシュ対象かなどをレスポンスヘッダで教えてくれたりします。(Akamai)
なのでその辺を元にテストを行えば問題ないでしょう。
テストパタンを考えるのはそんなに難しくはないと思います。
ユーザ情報を含むページも通してるのであれば複数ユーザ+非ログインユーザで条件が変わるパス毎にテストをすればよいだけです。

あと、この辺の複雑な制御は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)