Zstandard と Pingora でペタバイト級のキャッシュストレージを節約できる可能性
We could save petabytes of cache storage with Zstandard and Pingora (blog.cloudflare.com)
要約
Cloudflare は、Zstandard (zstd) を使用してキャッシュ内のアセットを圧縮し、ストレージ容量とデータセンター間の帯域幅を大幅に節約する「Cache Transcoding」というシステムを開発しました。このアプローチは、CPU 使用率のわずかな増加と引き換えに、キャッシュ効率を向上させ、インフラストラクチャコストを削減します。
全文翻訳
ZstandardとPingoraでペタバイト級のキャッシュストレージを節約できる可能性
メモリコストは劇的に増加しています。RAMとハードディスクドライブの両方の価格が、過去1年間で急騰しました。Cloudflareでは、有名なCDNを含むいくつかの大規模分散ストレージ製品を運用しており、これらは展開しているメモリを効率的に活用することに依存しているため、すべての顧客にサービスを提供し続けることができます。
これを念頭に、私たちは有効なキャッシュ容量を拡張する方法のプロトタイプを作成しました。対象となるアセットをZstandardでPingora内でエンコードすることで、アーキテクチャはCPUのわずかな増加と引き換えに、ストレージとデータセンター間の帯域幅の大幅な節約を実現します。
私たちはキャッシュトランスコーディングと呼ばれるシステムのプロトタイプを作成しており、これは1.1.1.1 Intern Programの一環として、Cloudflareでのインターンシップ中に私が構築したものです。対象となるレスポンスがキャッシュに入ると、ディスクに書き込む前にZstandard、つまりzstdを使用してエンコードします。アセットがキャッシュ内に存在し、Tiered Cacheを介してデータセンター間を移動する間、この圧縮形式を保持し、クライアントにレスポンスを配信する前にデコードします。
初期テストでは、このエンコードにより、対象となるアセットは平均でディスク上の元のサイズの3分の1に縮小されました。オリジン向きのプロキシでの推定CPUコストの増加はわずかでしたが、それがトレードオフです。CPUのわずかな増加は、Cloudflareにペタバイト級の有効なキャッシュ容量をもたらし、データセンター間で転送されるデータを削減します。エンコードコストは、アセットがキャッシュに入るときに一度だけ支払われます。ストレージと帯域幅の節約は、そのアセットが再利用されるたびに継続します。
Zstandardとは?
Zstandard、またはzstdは、Yann Collet氏がFacebookで開発し、2016年にオープンソース化されたロスレス圧縮アルゴリズムです。ロスレスとは、圧縮されたデータをデコードした後、すべてのバイトがオリジナルと同一であることを意味します。アセット自体を変更することなく、ディスク上でのアセットの表現方法を変更できます。
zstdは、圧縮率と速度のバランスを取るように設計されています。以前のブラウザ圧縮テストでは、Brotliよりも42%高速にデータを圧縮し、ファイルサイズはほぼ同等で、gzipと比較しても同等の速度で11.3%小さいファイルを作成しました。キャッシュトランスコーディングは大量のトラフィックに影響するため、エンコードとデコードの両方が高速である必要があるため、このバランスが重要です。
プロトタイプではzstdレベル3を使用しており、キャッシュフィルがCPUボトルネックになることなく、圧縮のメリットのほとんどを得ることができます。
Cloudflareは従来、オリジンから提供されたコンテンツエンコーディングを使用してアセットを保存しています。オリジンが非圧縮レスポンスを送信した場合、その非圧縮バイトをディスクに保存し、同じ形式でデータセンター間で転送します。キャッシュトランスコーディングは、キャッシュ自体の中に圧縮を追加します。
全てを圧縮する価値があるわけではない
トランスコーディングは、全てを圧縮することを意味しません。画像、ビデオ、フォントは通常、既に圧縮されています。私たちのトラフィックサンプルでは、このメディアのスライスはリクエストの21.4%を占めましたが、バイト数の63.3%を占めました。これを再度圧縮しても、CPUを無駄に消費するだけです。
圧縮可能なテキストは異なります。HTML、JSON、CSS、JavaScriptはリクエストの67.3%とバイト数の22.3%を占めました。このテキストスライス内では、約71%がContent-Encodingが設定されておらず、圧縮しやすい状態で到着しました。
管理されたテストコーパスでは、対象となるアセットは約2.8倍に圧縮されました。
| 測定項目 | 値 |
|---|---|
| 圧縮率 | 2.834倍 |
| エンコードコスト | 1バイトあたり4.31 ns、約232 MB/s、フィルごとに一度支払い |
| デコードコスト | 1バイトあたり1.56 ns、約641 MB/s、サーブごとに支払い |
エンコードはバイトあたりのコストが高いですが、アセットはフィルされるよりもはるかに頻繁にサーブされます。
アセットの表現方法を変更することで、既存のハードウェアはより多くの顧客コンテンツを保存できるようになります。
ディスク上のバイト数が少なくなれば、各サーバーはより多くのオブジェクトを保持できます。これによりキャッシュ密度が増加し、非圧縮形式が必要以上にスペースを消費したために有用なコンテンツが追い出される可能性が低くなります。
より小さい表現は、アセットがTiered Cacheを移動する際にも役立ちます。なぜなら、Cloudflareのデータセンター間でのデータ転送が削減され、バックボーンの使用がより効率的になるからです。
圧縮コストは一度だけ支払う
圧縮は決して無料ではありません。エンコードとデコードの両方でCPUが使用されるため、重要なのはバイト節約が処理コストに見合うかどうかです。
zstdレベル3(多くの場合、速度と圧縮サイズ出力のデフォルトのバランス)では、私たちのモデルはテストしたトラフィックと再利用の仮定の下で、追加のCPUコストを数パーセントに抑えました。
当初は、ホットなアセットはより頻繁に再利用されるため、トランスコーディングを人気のあるコンテンツに限定することを検討しましたが、うまくいきませんでした。デコードはアセットがサーブされるたびに発生するため、この機能を最もホットなコンテンツのみに限定すると、CPUを同じ量削減することなくストレージの節約が減少しました。
よりシンプルなポリシーの方がパフォーマンスが向上しました。対象となる圧縮可能なテキストを4キビバイト(KiB)以上でトランスコーディングすると、CPU予算内に収まりながら、測定されたストレージメリットのほぼ全てを捉えることができました。
キャッシュトランスコーディングの仕組み
キャッシュミスが発生した場合、Pingoraベースのプロキシは、ディスクに書き込む前にzstdを使用してボディをエンコードします。キャッシュメタデータは、保存されている表現が圧縮されていることを記録し、元のコンテンツ長を保持します。レスポンスがプロキシを離れる前に、ボディは元のアイデンティティ表現にデコードされます。
キャッシュヒットの場合、保存されているzstdオブジェクトがディスクから読み込まれ、デコードされます。Tiered Cacheを使用する場合、圧縮された表現は圧縮された形式で上位ティアから下位ティアに転送されます。デコードはクライアント側のホップでのみ発生します。
完全なキャッシュミスの場合、上位ティアはオリジンからアイデンティティバイトを取得します。これらのバイトは一度エンコードされ、zstdとして保存され、圧縮された形式で下位ティアに転送されます。下位ティアもzstd表現を保存し、リクエストパスのためにそれをデコードします。
[画像: unnamed (77).png]
下位ティアがミスをしたが、上位ティアが既にオブジェクトを持っている場合、オリジンは関与しません。圧縮されたオブジェクトは、キャッシュティア間で直接移動します。ワイヤー上およびディスク上では圧縮されたままで、下位ティアで一度デコードされます。
下位ティアが既にオブジェクトを持っている場合、ネットワーク転送やエンコードは必要ありません。下位ティアはディスクからzstdバイトを読み込み、デコードして、元の資産を転送します。
ストレージエンコーディングマーカーは、オブジェクトが複数回エンコードされるのを防ぎます。別のティアからオブジェクトを受け取ったキャッシュレイヤーは、それが既にzstdで保存されていることを確認し、その形式で保持できます。
なぜ特定のテキストのみをトランスコードするのか
最も高速な圧縮操作は、実行する必要のない操作です。したがって、キャッシュトランスコーディングは、メリットが期待できないコンテンツを回避するために、一連の適格性チェックを使用します。
プロトタイプは、Content-Encodingが設定されておらず、Content-Typeが圧縮可能なテキストであり、レスポンスのContent-Lengthが4KiB以上であることがわかっている場合にのみ、200 OKレスポンスをトランスコードします。スライスサブリクエスト、アクティブなアップストリーム圧縮を使用するレスポンス、レンジリクエスト、事前に圧縮されたレスポンス、不明な長さのボディ、バイナリコンテンツは変更されません。
4KiBのしきい値は、対象となるバイトの約1%しか除外せずに、多数の小さなリクエストを除外しました。これを下げることは、オブジェクトあたりのオーバーヘッドを追加するだけで、ストレージの節約はあまり増えません。
しきい値とzstdレベルは、どちらも永続的な制限ではなくパラメータです。zstdレベル3と4KiBの最小値から開始したのは、アーキテクチャを測定するための保守的な方法を提供してくれたからです。初期のCPU予算が理解できたので、より高い圧縮レベルが追加コストを正当化するのに十分な比率を改善するかどうかをテストできます。
100万件以上のリクエストをキャッシュ経由でテスト
管理されたテストゾーンに対してプロトタイプを実行し、リクエストログ、Prometheusメトリクス、Jaegerトレース全体で各リクエストを相関させました。
正当性キャンペーンは、キャッシュミス、キャッシュヒット、シングルホップフィル、Tiered Cacheフィルなどをカバーしました。各リクエストが特定のパスをたどるようにキャッシュキーを変更し、トレースを使用してエンコードとデコードが発生した場所を確認しました。
パフォーマンスキャンペーンでは、10台のキャッシュサーバーで100万件以上のリクエストを送信しました。キャンペーンの半分はTiered Cacheを無効にして実行し、もう半分は有効にして実行しました。これにより、キャッシュティア間の転送とは別に、ローカルキャッシュの動作を測定できました。
2つのアセットは約195KiBと272KiBで、どちらも約2.8倍に圧縮されました。これは意図的に圧縮可能なテストコーパスでした。アーキテクチャの検証のための明確なシグナルを提供しましたが、インターネット上のすべてのテキストオブジェクトを代表するものではありません。測定された圧縮率をフリート全体で一定と見なす前に、より広範なコーパスが必要です。
一度圧縮して、何度もメリットを得る
この実験でわかったのは、キャッシュサービス全体に展開できる大幅な効率があり、それがすべての顧客にメリットをもたらすということです。キャッシュトランスコーディングのために構築したものは、テストした条件下でトレードオフが有利であることを示しています。アーキテクチャはコンテンツを保持し、CPU予算内に収まりました。
次のステップとして、より高いzstdレベルの評価、より広範なコンテンツタイプとオブジェクトサイズのテスト、適格性基準からのさまざまなパラメータの調整などを計画しています。将来の作業では、レンジリクエスト、事前に圧縮されたオリジンレスポンス、およびデコードせずに既にサポートしているダウンストリームコンポーネントに圧縮オブジェクトを直接渡すことも検討できます。
インターンシップ throughout、私はCloudflareのエンジニアリングチームと協力し、グローバルネットワーク全体でコンテンツを保存および配信する実際のインフラストラクチャに取り組む素晴らしい機会を得ました。より良いインターネットを構築することに貢献してキャリアをスタートさせたい場合は、インターンシップの機会と求人情報をご覧ください。