本セッションの登壇者
セッション動画
「CDNを活用して高速なWebサービスを提供する」というタイトルで発表していきます。
まず自己紹介なんですけども、本名は金子です。今、PR TIMESという会社でCTOをやってます。紹介があったISUCON本の著者の一人で、6章「リバースプロキシの利用」、7章「キャッシュの活用」、8章「押さえておきたい高速化手法」を担当しています。ISUCONは9の予選と6の本選の運営を担当していて、実は今年のISUCONもアドバイザーとして運営に関わっています。SNSはcatatsuyというIDでやっているのでよろしくお願いします。かたついと呼ばれることが多いかなと思います。

早速なんですけど、本発表の対象者です。発表の対象者はISUCON本はすでに読んでいて、実際にCDNを活用して高速なWebサービスを提供したい人です。初心者向けの一般論の話は今回はしないので、一般論を知りたい方はISUCON本を読んでください。

CDN-Cache-Controlヘッダーが登場
では早速、ISUCON本アップデートです。ISUCON本では8章6節「CDN上にHTTPレスポンスをキャッシュする」で、CDN-Cache-Controlヘッダーが策定中であることが軽く紹介されていますが、実はその後のアップデートがあります。
そもそもこれまでのCache-Controlヘッダーは仕様が非常に複雑で、未実装のパラメータがある可能性もあって動作確認が難しいという問題がありました。ブラウザとCDNがそれぞれCache-Controlヘッダーを解釈するので、1つのヘッダーで複数のキャッシュレイヤーを考える必要もありました。そこに対して、CDN-Cache-Controlヘッダーが2022年6月にRFC9213として公開されています。このRFCのオーサーはAkamai、Fastly、Cloudflareの連名なんですけど、今のところCloudflareしか実装しなさそうでした。なので、他に実装しているCDN事業者がいたら教えてください。CDN-Cache-Controlヘッダーの登場でブラウザのキャッシュの設定をCache-Controlヘッダー、CDNのキャッシュの設定をCDN-Cache-Controlヘッダーで行うことで、設定がすっきりしたり、CDN間の移行も容易になる可能性が今後出てくるかもしれません。

ちなみにCDN-Cache-Controlヘッダーの使用はCache-Controlヘッダーとほぼ同様なので、CDN-Cache-Controlヘッダーの登場によってできることが増えるわけではないです。あえて言えばブラウザとCDNの間にProxyがあった場合、ProxyとCDNのキャッシュ設定を分けることがCache-Controlヘッダーではこれまで不可能だったんですけど、それができるようになったというのはあるかもしれないですね。ただ、最近だと常時HTTPS化が当たり前なので、ブラウザとCDNの間のProxyの存在を考慮する必要がほとんどなくなってきているのが実情です。

ディスプレイサイズに合わせて複数サイズのサムネイル画像を用意
CDNに関するよくある勘違いはスライドのとおりです。今回は比較的機能が多いCDNであるFastlyの利用事例や利用例を紹介するんですけど、必要な機能や要件をよく調べてどのCDNを利用するか選んでください。

Fastlyは無料で利用できるVCLという設定ファイルによって、かなり柔軟な設定ができます。VCLは設定で、if文が使えたり、暗号内周りをはじめ複雑な計算ができる関数も存在するので、柔軟な処理を実装できます。VCLはfor文がないので無限ループにならないので、タイムアウトするおそれがありません。設定なのでFastly社の細かいサポートを受けられるというメリットもあります。Fastlyは設定反映もキャッシュ削除もいずれも一瞬で終わります。なのですごく扱いやすいサービスかなと思います。今回紹介するFastly Image Optimizerという画像最適化サービスを使えば、URLのパラメータを工夫するだけで画像サイズ、画質、フォーマットなど動的に変換できます。画像は多くのWebサービスの通信量の大半を占めることが多いので、ファイルサイズを下げられればパフォーマンスへの寄与が大きいです。

画像フォーマットとサイズはスライドのようになっています。PNGは透明度を扱いたいとか、劣化を避けたい場合によく利用されるんですけど、ファイルサイズが大きくなるので基本は慎重に利用するべきです。JPEGは、透明度を扱えないデメリットがありますが圧縮率が高いので、常に非可逆圧縮のフォーマットを利用できないか考えるべきです。Retinaディスプレイなどの高解像度ディスプレイを搭載したデバイスが一般的になっているので、画像サイズは最低でも2倍以上はないときれいに見えません。十分な画像サイズの画像を用意してないとUXに影響が出ます。画像サイズを大きくし過ぎるとファイルサイズが増えすぎてパフォーマンスが落ちるので、それもUXを損ねます。ユーザーのディスプレイサイズによって複数種類のサイズのサムネイル画像を用意しておくとベストかなと思います。

これは本当に僕の個人的な方針なんですけど、画像のファイルサイズは基本的に200KB以下にしてほしいなと思ってます。個人的な意見です。

今の常識はサムネイル画像は動的に生成、CDNでキャッシュ
サムネイル画像を生成するコストは高いし、かつ解像度が低いディスプレイのPCでしか表示しないので決まった小さいサイズのサムネイル画像を投稿時に生成して、常にそのサムネイル画像を表示すれば良いというのが昔の常識です。今の常識は全然違っていて、適切にキャッシュを利用すれば動的にサムネイル画像生成が可能です。これにはCPUやメモリなどの性能向上だったり、クラウドサービス利用の一般化があるかなと思います。
解像度が高いディスプレイを持つスマートフォンやPCが一般的になっていて、適切な画像サイズがデバイスによって異なっているので、適切なサムネイル画像を動的に生成して、それをCDNレイヤーでキャッシュすれば良いというのが今の常識ですね。画像を動的に変換できるサービスというのは多くあるので、そういったサービスを利用することで、比較的簡単に動的にサムネイル画像を生成できます。今回はFastly Image Optimizerを紹介するんですけど、それ以外にもいろいろあるので調べてみてください。

保存する画像はオリジナル画像のみか、もしくはオリジナル画像+サムネイル生成用の画像の2種類のみで良いです。サムネイル生成用の画像を用意すると、たとえばAmazon S3のような画像を保存するストレージサービスからの転送量が下げられるので、クラウド利用料のコスト削減につながることがあります。このあたりはサービスによって全然違うので、自分たちのサービスでどうかというのはよく考えてください。弊社の会社ブログにも事例が載っていますので、興味のある方はぜひ見てみてほしいです。

画像の新フォーマット
WebP/AVIFについて紹介します。今回、不可逆圧縮のWebP/AVIFを利用する前提で説明します。WebP/AVIFのどちらも高品質で大きな画像からいい感じのサムネイル画像を生成するというのが得意なので、高品質なオリジナル画像を持っていなければ逆効果になり得ます。特にWebPで生成するサムネイル画像は大きい画像サイズにしないと粗が目立ちやすいと個人的に思うので、結構注意して使う必要があるかなと思います。

AVIFはエンコードやデコードのコストが比較的高いことが知られています。ただ、これについては外部の画像変換サービスを利用するのであれば、利用者側はあんまり気にしなくていいかなと思います。いずれにしろ、生成したサムネイル画像をCDN上で適切にキャッシュするのが重要です。

このあたりをもう少し詳しく知りたい方は、WebPについては以前僕が書いたこちらの記事を読んだり、AVIFに関する記事もいくつか紹介しているので、興味のある方はぜひ見てみてください。

許容できる工数で選ぶ画像最適化サービス
画像最適化サービスの利用方法なんですけど、お勧めの方法は2つあります。1つは同一URLでAcceptヘッダーを利用してデバイスによって返す画像フォーマットを変える方法です。工数低めで導入するんだったらこっちがお勧めです。

もう1つは、下のコードのようにpictureタグを利用してデバイスによって画像を変える方法です。いいところはディスプレイサイズによって画像のURLを変えるとかもできるので、画像サイズも含めて細かく制御したいんだったらこっちがお勧めです。ただHTMLを書き換える工数が高い可能性があります。

PR TIMES STORYの事例紹介です。WebパフォーマンスはUXだけでなくSEO上の重要な指標になっていて、ビジネス上の重要度が上がっています。特に、以前の発表でもあったと思うんですけど、Lighthouseスコアというのを確認するべきです。これは会社ブログで紹介している事例ですね。今回、画像のURLの変更以外、変更を一切入れていません。画像URLを変更しただけでここまでの成果を出せるというのが実際の事例としてあるので、ぜひ活用してほしいなと思います。

画像以外の事例として、Brotliというのがあります。BrotliについてはISUCON本で紹介しているので、ここに抜粋を載せています。

ISUCON本では「Brotliを簡単に使用できるCDNもあるので、適切に使用すれば転送量を減らせます」としか書いていないですが、Fastlyでは設定を有効にすればBrotliを利用できます。ただし動的圧縮は利用できません。ISUCON本の記述に「圧縮レベルによるがgzipよりも圧縮に時間がかかるため、gzipのようにリクエストされたタイミングで動的に圧縮するという使い方は難しい」とありますが、これが理由かなと思っています。圧縮レベルを下げると圧縮率がgzipと大差なくなるので、個人的にはわざわざ利用する意味は薄そうです。CSSやJavaScriptファイルを配信しているなら容量がgzipよりもより小さくできるので、有効にしておくのがお勧めです。ISUCON本にも記述がありますし、詳しい挙動はCDNのドキュメントを参照してください。

パフォーマンス的にはHTTP/3が良い
HTTP/3です。このへんから最先端の話になってきます。HTTP/3は実はISUCON本が出た後にRFCとして発表されているので未掲載になっています。今回、HTTP/3の細かい説明は割愛して簡単に紹介します。HTTP/2にはTCPのHead of Line Blockingという、不安定な通信環境ではHTTP/1.1よりもパフォーマンスが落ちるという問題がありました。HTTP/3ではTCPではなくUDPを利用して再送制御など必要な処理をアプリケーション側で行うことで、不安定な通信環境でも最低限の再送制御で済むので、HTTP/3が使えるなら有効にした方がパフォーマンス的には良い可能性が高いと思います。ちなみにnginxでは先月リリースされた1.25.0というバージョンからHTTP/3に対応しています。nginxでHTTP/3を使う方法は先日書いたので、使いたい人は参考にしてもらえればいいかなと思ってます。

HTTPSレコードの話です。HTTP/2はHTTP/1.1と同様にTCPを利用しているので、実際に通信しようとすると、まずTCPのコネクションを作って、そこでTLSのハンドシェイクをして、その後にHTTPの通信を流すという順番になります。HTTP/2のコネクションを作ってHTTPの通信を始めた後にHTTP/3で通信できることがわかると、HTTP/3のコネクションをUDP上に改めて確立する必要があります。パフォーマンス的には明らかに無駄なんですよね。最初からHTTP/3で通信を始めた方がいいです。

サーバーとHTTPで通信をする前に、HTTP/3を利用できることをクライアントに伝えられれば、最初からHTTP/3の通信ができます。そのためのレコードとして、HTTPSレコードというのが現在策定中になっています。HTTPSレコードがあれば、AレコードとかCNAMEレコードの代替にできる上に、IPアドレス以外のさまざまな情報をDNS経由で伝えられるようになります。将来的にセキュリティやパフォーマンスのためにHTTPSレコードを設定するのが常識になる時代が来るかもしれません。

DNSは暗号化する流れ
ここでDNSの問題点について触れますが、DNSには1つのUDPパケットに収めるために、メッセージサイズを512バイト以下にしなければならない制約があります。しかも、DNSパケットは暗号化されていないので、傍受も改ざんもできます。これらの問題を解決するために、DNS over HTTPSとかDNS over TLSという仕様が策定中です。これはまだ広く使われているとは言い難いんですけど、DNSを暗号化する流れ自体はあります。

これはちょっと僕もあんまり詳しいことはわかってないんですが、HTTPSレコードはFirefoxだとDNS over HTTPSを有効にしないと利用できない模様です。理由を知ってる人がいたら教えてほしいんですけど、おそらく従来のDNSだと改ざん可能であることも関係しているのかなと思います。また、HTTPSレコード経由で鍵ファイルも配布しようとしていて、鍵ファイルを配布すると512バイト以下で収まらないかもしれないということを考慮している可能性もあるかなと思います。ECHと呼ばれているTLS拡張で、もともとEncrypted SNIと呼ばれていた仕様ですね。こういうことをやろうとしています。

このあたりの話は昔僕が書いた記事『政府によるインターネットの検閲とSNIについて』を読むと、ESNIがなぜ策定中なのかというのがわかるかなと思います。他の記事も参考に張ってあるのでぜひ参考にしてください。

まとめです。CDNを利用してWebパフォーマンスを上げる方法を紹介しました。ただ、これからも状況が変わっていくので、必ず最新情報を調べて利用してください。あと、自分たちのWebサービス仕様から最適なものを選ぶことも忘れないでください、というところで僕の発表を終わりにします。
ありがとうございました。
