🔖

CDNを活用して高速なWebサービスを提供する

2023/06/21に公開

https://techfeed.io/events/techfeed-experts-night-21#1_1870977dc5f268

TechFeed Experts Night#21 発表資料

自己紹介

ISUCON本書影

本発表の対象者

  • ISUCON本は既に読んでいて、実際にCDNを活用して高速なWebサービスを提供したい人
    • 初心者向けの一般論の話はしないので、一般論を知りたい方はISUCON本を読むのがおすすめです
  • ISUCON本で紹介されていない、更に先の応用事例を知りたい人
    • 今回紹介する画像最適化の話題はISUCONだと出題が難しいので書籍内では触れられていません
    • 本の趣旨上、特定のサービスを紹介することは難しいので、書籍内だとCDNに関する紹介も一般論中心
  • Webの最先端技術に興味がある人
    • 今すぐ役に立つわけでもないし、今後変わりうる話なので気をつけてください

ISUCON本アップデート

ISUCON本では8章6節「CDN上にHTTPレスポンスをキャッシュする」で、CDN-Cache-Controlヘッダーが策定中であることが軽く紹介されているので、その後のアップデートを紹介します

  • これまでのCache-Controlヘッダー
    • 仕様が非常に複雑で、未実装のパラメータがある可能性もあるので動作確認が難しい
    • ブラウザ・CDNなどがそれぞれCache-Controlヘッダーを解釈するので、1つのヘッダーで複数のキャッシュレイヤーを考える必要がある
  • CDN-Cache-Controlヘッダーは2022/06にRFC 9213として公開
    • RFCのAuthorsはAkamai・Fastly・Cloudflare社の人の連名だが、今のところCloudflareしか実装していなそう
      • 他に実装しているCDN事業者を知っていたら教えてください
  • CDN-Cache-Controlヘッダーの登場によって、ブラウザのCacheの設定をCache-Controlヘッダー、CDNのCacheの設定をCDN-Cache-Controlヘッダーで行うようにすることで、設定がすっきりしたり、CDN間の移行も容易になる可能性が出てくるかもしれません
    • なおCDN-Cache-Controlヘッダーの仕様はCache-Controlヘッダーとほぼ同様のため、CDN-Cache-Controlヘッダーの登場によってできることが増えたわけではありません
    • あえて言えば、ブラウザとCDNの間にProxyがあった場合、ProxyとCDNのCacheの設定を分けることがCache-Controlヘッダーでは不可能だったのができるようになったというのはあるかも
      • ただし現在では常時HTTPS化が当たり前なので、ブラウザとCDNの間のProxyの存在を考慮する必要はほとんどなくなってきています

CDNに関するよくある勘違い

  • 🙅‍♀️最近のCDNには色々便利な機能がある
    • 🙆‍♀️CDN事業者によって提供している機能も違えば、柔軟度も違う
  • 🙅‍♀️最近のCDNは設定反映は高速であるし、キャッシュ反映もすぐにできる
    • 🙆‍♀️CDN事業者によって設定反映にかかる時間やキャッシュ削除にかかる時間は異なり、すぐに反映されるとは限らない

今回は比較的機能が多いCDNであるFastlyでの利用例を紹介しますが、必要な機能や要件をよく調べて、どのCDNを利用するか選ぶ必要がある

Fastlyについて

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

画像フォーマット・サイズについて

  • 画像フォーマットは昔から使われているJPEG・PNGが一般的だが、現在ではよりよい選択肢がある
    • JPEGは非可逆圧縮(一部のデータが欠損する代わりに圧縮率が高い)でPNGは可逆圧縮(欠損はない代わりに圧縮率が低い)
    • PNGは透明度を扱いたい・劣化を避けたい場合によく利用されるが、ファイルサイズが大きくなるので慎重に利用するべき
    • JPEGは透明度を扱えないデメリットがあるが、圧縮率が高いため、常に非可逆圧縮のフォーマットを利用できないか考えるべき
  • 画像サイズはRetinaディスプレイなどの高解像度ディスプレイを搭載したデバイスが一般的になっているため、表示サイズの2倍〜3倍の画像サイズの画像を用意する必要がある
    • 最低でも2倍以上はないときれいに見えないため、十分な画像サイズの画像を用意しないとUXに影響が出る
    • 画像サイズを大きくしすぎると、ファイルサイズが増えすぎてパフォーマンスが落ちるため、それもUXを損ねる
      • 必要ならユーザーのディスプレイサイズによって複数種類のサイズのサムネイル画像を用意しておくとベスト
  • 画像のファイルサイズの個人的な方針は以下
    • 基本的に200KB以下にする
    • 500KB以上は論外なので必ず対応する
      • このケースはそもそも画像サイズがおかしいことが多いので、まず画像サイズを見直したり、qualityを調整する
    • いずれにも該当しないケースはグレーゾーン

サムネイル画像の昔と今

  • 昔の常識
    • サムネイル画像を生成するコストは高く、かつ解像度が低いディスプレイのPCでしか表示しない
      • 決まった小さいサイズのサムネイル画像を投稿時に生成し、常にそのサムネイル画像を表示すればよい
  • 今の常識
    • 現代のコンピュータの性能では適切にキャッシュを利用すれば動的にサムネイル画像生成は可能
      • CPU・メモリーなどの性能向上、クラウドサービス利用の一般化
    • 解像度が高いディスプレイを持つスマートフォン・PCが一般的になっており、ユーザーが利用するデバイス種類が多く、適切な画像サイズはデバイスにより異なる
      • 適切なサムネイル画像を動的に生成し、それをCDNレイヤーでキャッシュすればよい
        • CDN上でキャッシュ使用率を上げるために、サムネイル画像サイズは数種類に限定する方が良い
        • ディスプレイサイズから単純に計算するのはキャッシュ使用率を下げるのでおすすめしない
      • 画像を動的に変換できるサービスは多くあるので、そういったサービスを利用することで比較的簡単に動的にサムネイル画像を生成できる
        • 今回はFastly Image Optimizerを紹介しますが、それ以外にも色々あるので調べてみてください
    • 保存する画像はオリジナル画像のみか、もしくはオリジナル画像+サムネイル生成用の画像の2種類のみでよい
      • サムネイル生成用の画像を用意すると、Amazon S3のような画像を保存するストレージサービスからCDNへの転送量を下げられるので、クラウド利用料のコスト削減に繋がることがある
      • この辺りはサービスによって異なる

cf:

WebP/AVIFについて

  • 現代のサムネイル画像の問題を解決するために新しい画像フォーマットであるWebP/AVIFが開発された
  • Googleが開発したWebPは高品質な画像を保ちつつ、ファイルサイズを小さくできる
    • WebPはChromeやAndroidでは昔から使えたため、比較的利用できる可能性は高い
  • 最近のブラウザは新しい画像フォーマットであるAVIFを利用できるようになり、将来的にAVIFがスタンダードになる可能性がある
    • AVIFはモダンブラウザはほとんどサポートしているが、モダンブラウザ以外のサポートは期待できない
  • WebP/AVIFは透明度を扱える
  • WebP/AVIFはどちらも非可逆圧縮・可逆圧縮を選ぶことができるが、広く使われているのは非可逆圧縮の方で、基本的には非可逆圧縮であると考えて問題ない
    • 今回も非可逆圧縮のWebP/AVIFを利用する前提で説明します
  • WebP/AVIFどちらも「高品質で大きな画像からいい感じのサムネイル画像を生成する」ことが得意
    • 高品質なオリジナル画像を持っていなければ逆効果になり得る
    • 特にWebPで生成するサムネイル画像は大きい画像サイズにしないと粗が目立ちやすい(と個人的に思う)
  • AVIFはエンコード・デコードのコストが比較的高いことが知られている
    • 外部の画像変換サービスを利用するなら利用者側はあまり気にしなくてよいはず
    • いずれにしろ生成したサムネイル画像をCDN上で適切にキャッシュすることは重要

cf:

画像最適化サービス利用方法

おすすめは以下の2つの方法(工数低めで導入するなら前者、しっかり作り込みたいなら後者がおすすめ)

  • 同一URLでAcceptヘッダーを利用してデバイスによって返す画像フォーマットを変える(今回事例紹介する方法)
    • WebPに対応しているデバイスにはWebP、WebPに対応していないデバイスにはJPEGを同一URLで返すことができる手法
    • Fastly Image Optimizerならformat | Fastly Developer Hubを使えばauto=webpなどで利用可能なので一番簡単に利用可能
      • CDNのキャッシュはVaryヘッダーを利用してAcceptヘッダーの内容毎に分けている(Fastly Image Optimizerなら自動でやってくれるので気にする必要はない)
  • pictureタグを利用してデバイスによって画像URLを変える
    • ディスプレイサイズによって画像URLを変更することもできるので、画像サイズも含めて細かく制御したいならこちらがおすすめ
    • HTMLを書き換える必要があるので、工数が高い可能性がある
<picture>
  <!-- 900px以上のディスプレイで表示する画像 -->
  <source media="(min-width: 900px)" srcset="large-image.webp" type="image/webp">
  <!-- 480px以上900px未満のディスプレイで表示する画像 -->
  <source media="(min-width: 480px)" srcset="medium-image.webp" type="image/webp">
  <!-- 480px未満のディスプレイで表示する画像 -->
  <source srcset="small-image.webp" type="image/webp">
  <!-- WebP非対応ブラウザ用の画像 -->
  <img src="fallback-image.jpg" alt="画像の説明">
</picture>

https://developer.mozilla.org/ja/docs/Web/HTML/Element/picture

PR TIMES STORY事例紹介

Brotliでデータ圧縮

BrotliはGoogle社が2015年に公開した比較的新しい圧縮アルゴリズムです。特徴としてはWebでの利用を念頭においており、Web上でよく利用される単語が多く登録されている辞書ファイルを圧縮に使用することで、容量の小さなHTMLファイルなども効率よく圧縮できる点が挙げられます。現在使われているほとんどのブラウザは対応しており、gzipほどではありませんがブラウザをターゲットにするWebサービスならば普及率は高いと言っていいでしょう。
(中略)
・圧縮レベルによるがgzipよりも圧縮に時間がかかるため、gzipのようにリクエストされたタイミングで動的に圧縮するという使い方は難しいと考えられる
 ・事前に圧縮しておける静的ファイルの配信では利用できる
(中略)
Brotliを簡単に使用できるCDNなどもあるので、適切に使用すれば転送量を減らせます
(ISUCON本 6章5節 nginxによる転送時のデータ圧縮)

  • Fastlyでは設定を有効にすればBrotliを利用できる
    • ただしCDN上でキャッシュできるコンテンツでしか利用できず、動的圧縮は利用できない
      • ISUCON本の記述の内容「圧縮レベルによるがgzipよりも圧縮に時間がかかるため、gzipのようにリクエストされたタイミングで動的に圧縮するという使い方は難しい」が理由と思われる
      • 圧縮レベルを下げると圧縮率がgzipと大差がなくなるので、わざわざ利用する意味は薄い
  • CDN上でキャッシュできるCSSやJavaScriptの容量がgzipよりもより小さくできるので、CSSやJavaScriptファイルを配信するなら有効にしておくのがおすすめ
  • ISUCON本に記述があるのと、詳しい挙動はCDNのドキュメントを参照してください

cf:

自動圧縮を有効にする | Fastly ヘルプガイド

HTTP/3

  • ISUCON本だとHTTP/2は紹介しているが、HTTP/3は未掲載
    • こちらも2022/06にRFC 9114として発表
    • HTTP/2についてはISUCON本内でも紹介している詳解HTTP/2などを参考にしてください
  • HTTP/3自体の細かい説明は割愛し、簡単に紹介
    • HTTP/2にはTCPのHead of Line Blockingがあり、不安定な通信環境ではHTTP/1.1よりもパフォーマンスが落ちる問題がある
    • HTTP/3ではTCPではなく、UDPを利用し、再送制御など必要な処理をアプリケーション側で行うことで、不安定な通信環境でも最低限の再送制御で済むように
    • HTTP/3が使えるなら有効にした方がパフォーマンス的には良い可能性が高い
  • nginxでは2023/05にリリースされた1.25.0からHTTP/3に対応

HTTPSレコード

  • HTTP/2はHTTP/1.1と同様にTCPを利用している
    • 通信はTCP→TLS→HTTPという順番になる
    • TLSのALPN拡張を利用してHTTP/2が利用できることをサーバーからクライアントに伝えることで最初からHTTP/2で通信できる
  • HTTP/3はHTTP/3が使えることをサーバーからクライアントに伝えるために、Alt-Svcヘッダーを利用する
    • HTTPヘッダー経由で伝えるしかないので、HTTP/2のコネクションを作ってHTTPの通信を始めた後に、HTTP/3のコネクションをUDP上に改めて確立する必要がある
    • パフォーマンス的にはHTTP/2のコネクションを作るコストが明らかに無駄なので、最初からHTTP/3で通信を始めたい
  • サーバーとTCPで通信をする前にHTTP/3を利用できることをクライアントに伝えられれば、最初からHTTP/3の通信を行うことができる
    • サーバーと通信をする前にDNS経由でサーバーのIPアドレスを取得している→DNS経由でHTTP/3が利用できることを伝えられれば技術的には可能
    • そのためのHTTPSレコードが現在策定中
    • HTTPSレコードがあればAレコードやCNAMEレコードの代替にできる上に、IPアドレス以外の様々な情報をDNS経由で伝えられるようになる
    • 将来的にセキュリティ・パフォーマンスのためにHTTPSレコードを設定するのが常識になる時代が来るかも

DNSの問題点

  • DNSは1つのUDPパケットに収めるためにメッセージサイズを512バイト以下にする必要がある
    • それ以上の場合はTCPにフォールバックする仕様があるが、対応していないルーターもあるので、基本は512バイト以下にする必要があるという理解でいる必要がある
  • DNSパケットは暗号化されていないので、傍受可能で、改竄も可能
    • 昨今のフルHTTPS化の流れから逆行
  • これらの問題を解決するためにDoH (DNS over HTTPS)やDoT (DNS over TLS)が策定中
    • まだ広く使われているとは言い難いが、DNSを暗号化する流れはある
  • HTTPSレコードはFirefoxだとDNS over HTTPSを有効にしないと利用できない模様
    • 理由は知らない(知っている人がいたら教えてください)が、おそらく従来のDNSだと改竄可能であることも関係があると思われる
    • 実はHTTPSレコード経由で鍵ファイルも配布しようとしているので、512バイトで収まらない可能性を考慮している可能性もある
      • ECH (Encrypted Client Hello)と呼ばれているTLS拡張で、元々はESNI (Encrypted Server Name Indication)と呼ばれていた
  • 現時点だとHTTPSレコードは利用できるケースが少ない上に、そもそも設定できるDNSサービスが少ない
    • 今後セキュリティやパフォーマンスのために、HTTPSレコードを活用する時代が来るかもしれない
    • ちなみに私はCloudflare DNSを利用して、HTTPSレコードの挙動を確認しています

cf:

まとめ

  • CDNを利用してWebパフォーマンスを上げる方法を紹介
    • WebP/AVIFを利用した画像の最適化
    • 新しいデータ圧縮技術Brotliの利用
    • HTTP/3の導入
  • 将来パフォーマンスのために利用される可能性がある技術も紹介
    • HTTPSレコードとDoH/DoT
  • これからも状況は変わっていく
    • 必ず最新情報を調べて利用してください
    • 自分たちのWebサービスの仕様から最適なものを選ぶことも忘れないでください

Discussion