content-delivery について
システム設計やクラウドアーキテクチャ向けのスキルです。CDNを導入する判断から、キャッシュ、ルーティング、オリジン保護の選定、障害対応や監視までを扱います。
主な機能
- 01プル型・プッシュ型配信の選択
- 02キャッシュキー、TTL、無効化の設計
- 03エッジルーティングとオリジン保護
- 04障害モードと監視指標の評価
活用シーン
- 01世界各地への静的アセット配信の高速化
- 02トラフィック急増時のオリジン負荷軽減
- 03キャッシュ方針の選定と転送量コストの推定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ proyecto26/ system-design-skills --skill content-deliveryインストール後、Claude Code で/content-deliveryを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/deep-dive.mdreferences/providers/aws.mdazure.mdgcp.mdgeneric.md詳細
コンテンツ配信
バイト列をネットワークのエッジにプッシュすることで、リクエストをユーザーの近くで終端させ、オリジンまで到達させないようにします。CDN はシステムの最外層にあるキャッシュレイヤーです。適切に設定すれば、静的コンテンツやメディアのトラフィックの大半と、レイテンシーの一部をサーバーに届く前に解消できます。設定を誤ると、古いアセットを配信したり、オリジンの負荷を増やしたり、エグレス料金を二重に支払うことになります。
どんなときに導入するか
地理的に広く分散したユーザーが、同じファイル(画像、動画、JS/CSS バンドル、ダウンロードファイル、フォント)を繰り返し読み込む場合、オリジンまたはその帯域幅が静的コンテンツの読み取りにおけるボトルネックになっている場合、あるいはファーストバイトまでのリージョン間レイテンシーが問題になる場合です(大陸をまたぐ往復は約 100 ms です。back-of-the-envelope を参照)。CDN を使えば、レイテンシーの低減とオリジン負荷の軽減を同時に実現できます。
導入すべきでないとき
リクエストごとに大きくパーソナライズされた動的レスポンスで、キャッシュできる形式がない場合(CDN は経由点を増やすだけで、何もキャッシュしません)。オリジンが読み取りを問題なく処理できるほど小規模な単一リージョンのユーザー層の場合(YAGNI です。CDN を導入すると、ベンダー、請求、無効化の問題がそれぞれ増えます)。鮮度が厳密に求められ、古い状態を一切許容できないデータの場合は、オリジン、または consistency-coordination の背後で扱うべきであり、TTL ベースのエッジに置くべきではありません。静的コンテンツの読み取りや地理的な分散が問題だと数値で確認する前に CDN の導入を決めるのは、要注意です。
まず確認すること
- コンテンツの内訳 — キャッシュ可能な静的コンテンツ/メディアと、キャッシュできない動的コンテンツ/パーソナライズされたコンテンツの割合はどれくらいですか?(メリットがあるのはキャッシュ可能な部分だけです。)
- 更新頻度と許容できる古さ — アセットはどのくらいの頻度で変わり、エッジのコピーがどの程度古くても許容できますか?(TTL と無効化戦略を決めます。)
- 地理的な分布 — ユーザーはどこにいて、どの程度集中していますか?(エッジ PoP と地理的ルーティングが必要かどうかを判断します。)
- オブジェクトサイズとエグレス量 — 平均アセットサイズ × リクエスト数 = エグレス量です。これが請求額とオフロード量を左右します(→
back-of-the-envelope)。 - オリジンの構成 — オブジェクトストア(S3/GCS/blob)ですか、それとも動的なアプリケーションサーバーですか?エッジのキャッシュがフラッシュされた際の、コールドキャッシュへの大量アクセスに耐えられますか?
選択肢
配信モデル — コンテンツをエッジに届ける方法
- プル(オリジンからプル): 最初のキャッシュミス時にエッジが取得し、TTL の間キャッシュして、その後のリクエストに配信します。適している場合:トラフィックが多く、コンテンツのサイズが大きい、または頻繁に変わる場合です。エッジには実際にリクエストされたものだけが保持されます。ほとんどのシステムでのデフォルトです。
- プッシュ: 需要が発生する前に CDN にアセットをアップロードし、URL を書き換えます。適している場合:カタログが小規模または静的な場合や、公開直後の急増時に最初のキャッシュミスによる遅延を許容できない場合です(事前にウォームアップします)。ストレージとアップロードを自分で管理する必要があります。
キャッシュキーと TTL — エッジが何をキーに、どの期間キャッシュするか
- 長い TTL とフィンガープリント付き URL(
app.4f9a.js、image.png?v=2):変更されないアセットを数か月間キャッシュします。コンテンツが変わったら無効化するのではなく、新しい URL を使います。適している場合:アセットの URL を自分で管理できるときです。最もすっきりした方式です。 - 短い TTL/
stale-while-revalidate: スケジュールに従って変わるコンテンツの古さを制限します。適している場合:URL は固定でも、コンテンツは更新されるときです。
エッジへの近接性とルーティング — ユーザーを最寄りの PoP に接続する方法
- エニーキャスト/DNS による地理的ルーティング: 各ユーザーを最も近い正常なエッジにルーティングします。適している場合:ユーザー層が複数のリージョンにまたがる場合です(CDN ではほぼ常に当てはまります)。
load-balancingと共通する内容です。ルーティングの仕組みについてはそちらを参照してください。
オリジン保護 — オリジンが外部にさらされる範囲を縮小する
- オリジンシールド/中間キャッシュ: オリジンの前に単一リージョンのキャッシュレイヤーを置き、すべてのエッジがそこを経由してプルすることで、N 個のエッジのキャッシュミスを 1 回のオリジン取得に集約します。適している場合:コールドミス時に少しレイテンシーが増えることより、オリジン負荷の軽減や大量アクセスの防止を重視するときです。
トレードオフ
| 選択肢 | 解決できること | 悪化すること | 変更するタイミング |
|---|---|---|---|
| プル型 CDN | エッジには要求されたコンテンツのみが保持され、アップロードパイプラインは不要です | オブジェクトごとの初回リクエストはキャッシュミスにより遅くなります。コンテンツが変わる前に TTL が切れると、再取得が重複します | キャッシュミス時のレイテンシやローンチ時の急増が問題になる場合 → プッシュ型/事前ウォームアップ |
| プッシュ型 CDN | キャッシュミスがなく、何をいつキャッシュするかを完全に制御できます | アップロード、ストレージ、URL の書き換えを自分で管理する必要があり、ほとんど読まれないアセットの保存にも費用がかかります | カタログが拡大する、または変更頻度が高くなる場合 → プル型 |
| 長い TTL +フィンガープリント付き URL | ほぼ永続的にキャッシュできます。更新時は新しい URL を使うため、無効化の競合がありません | ビルドと URL を制御する必要があります。古いバージョンは期限切れになるまでエッジに残ります | URL を自分で制御できない場合 → 短い TTL |
| 短い TTL/stale-while-revalidate | 安定した URL 上で古いコンテンツの許容範囲を限定できます | オリジンへの再検証トラフィックが増えます。期限切れが同期するとリクエストが集中します | コンテンツが本当に不変の場合 → フィンガープリント+長い TTL |
| 地理的ルーティング/エニーキャスト | ユーザーは最寄りのエッジに接続し、レイテンシが低下します | 考慮すべき PoP が増え、ルーティングによってユーザーが劣化した PoP に送られることがあります | ユーザーが単一リージョンに集中している場合 → 使用しない |
| オリジンシールド | エッジのキャッシュミスを 1 回のオリジン取得に集約し、オリジンを保護します | キャッシュミス時にホップが 1 つ増えます。また、単一リージョンの場合、シールドが新たな単一障害点(SPOF)になります | オリジンが堅牢で、すでに十分な負荷軽減ができている場合 → 取り除く |
高負荷時の動作
CDN は通常、負荷の急増を吸収します。それが CDN の役割です。ただし、CDN にも独自の障害パターンがあり、障害が発生するとオリジンに直接負荷が集中しがちです。
- コールドキャッシュ/大量のキャッシュ削除: パージ、設定の反映、または TTL の同期した期限切れの後、エッジのヒット率が急落し、すべての PoP が一斉にオリジンから取得します。これはグローバル規模の
cachingにおけるサンダリングハードです。緩和策: オリジンシールドでキャッシュミスを集約する、TTL にジッターを加える、エッジが古いコンテンツを配信しながら再取得するようにstale-while-revalidateを使う、段階的にパージする。 - キャッシュのバイパス/低ヒット率: クエリ文字列の無制限なバリエーションやキャッシュキー内の Cookie によってキー空間が爆発し、何もキャッシュに残らなくなります。その結果、CDN の費用を払いながら、オリジンにはすべてのトラフィックが届きます。緩和策: キャッシュキーのパラメーターを正規化または許可リストに限定する。静的パスでは Cookie を除去する。
- ホットオブジェクト: 拡散した 1 つのファイルが単一 PoP の容量を超える可能性がありますが、CDN はオリジンよりはるかに適切にスケールします。本当のリスクは、キャッシュできないホットパスがオリジンに突き抜けることです。
- CDN の障害/一部 PoP の障害: エッジがすべての前段にある依存先になります。オリジンへのフォールバック(クライアントまたは DNS によるオリジンへのフェイルオーバー)を計画し、オリジンが短時間すべての負荷を引き受けることを許容するか、2 つ目の CDN(マルチ CDN)を使用します。
- 想定外の送信トラフィック:
no-cacheの設定ミスや、キャッシュできないホットアセットによって、気付かないうちにオリジンの送信トラフィック料金が 10 倍になることがあります。
監視項目: エッジのヒット率(キャッシュヒット率)、オリジンのオフロード率、オリジンへのリクエスト率(エッジ障害時に急増する値)、リージョン別のエッジレイテンシの p95、送信バイト数、エッジとオリジンそれぞれの 4xx/5xx。
適用方法
- 入力を明確にする — コンテンツの内訳(キャッシュ可能な割合)、許容できる古さ、地域、オブジェクトのサイズ × 数量、オリジンの構成を確認します(
Clarify firstを参照)。キャッシュ可能な割合がほぼゼロの場合、または利用者が単一リージョンに限られる場合は、ここで中止します。 - 配信モデルとキャッシュキーを選ぶ — トレードオフ表を参考にします。既定では プル とし、キャッシュミス時のレイテンシやローンチ時の急増が問題になる場合に限り、プッシュ/事前ウォームアップに切り替えます。URL を管理できる場合は 長い TTL +フィンガープリント付き URLを優先し、それ以外の場合は **短い TTL/
stale-while-revalidate**を使います。 - 設定値を決める —
Cache-Control(max-age、immutable、stale-while-revalidate)、キャッシュキー(URL パス + 許可リストに含めたパラメーター。静的パスでは Cookie を除外)、実際に差異がある場合に限ったVaryを設定し、オフロードやリクエスト集中の抑制が重要であればオリジンシールドを追加します。 - 設計に負荷をかけてテストする — グローバルなパージ、設定のプッシュ、CDN/PoP の障害を想定して確認します。TTL のジッター +
stale-while-revalidate+ シールドによってオリジンへの負荷が許容範囲に収まること、またクライアント/DNS によるオリジンへのフォールバックがあることを確かめます。 - 数値で規模を見積もる — ヒット率(目標は 90% 以上)、オリジンオフロード率、エグレス(
requests × avg object size)をback-of-the-envelopeで見積もります。エグレスやオリジンへのリクエストレートに懸念がある場合は、キャッシュキーと TTL を見直します。 - プロバイダーを選ぶ — 既定では汎用レシピを使います。クラウドが指定されている場合は、サービスの対応付けと制限について、該当プロバイダーのファイルを確認します(
Choosing a providerを参照)。 - 回答でトレードオフを示す — ここで選んだ項目ごとに 1 行を設けた
Decision | Solves | Worsens | Change it when…テーブルを提示します(例:オプション、保存先、障害時のポリシー)。最後の列には、具体的な切り替え条件(数値または観測可能なシグナル)を示します。そのうえで、最初に何が破綻するかを述べます。
やるべきことと避けるべきこと
やるべきこと
- 変更されないアセットにはフィンガープリントを付けて数か月間キャッシュし、無効化ではなく、新しい URL で更新します。
- キャッシュキーに使うパラメーターを許可リストで管理し、静的パスでは Cookie を除外して、ヒット率を高く保ちます。
stale-while-revalidateと TTL のジッターを追加し、有効期限が同期してもオリジンにリクエストが集中しないようにします。- 多数のエッジが同時にオリジンへのキャッシュミスを起こす可能性がある場合は、オリジンシールドを追加します。
- CDN または PoP の障害に備え、オリジン/DNS へのフォールバック(またはマルチ CDN)を計画します。
- エッジヒット率とオリジンへのリクエストレートを監視します。エッジに障害が起きたときに急増する数値です。
避けるべきこと
- 静的コンテンツへの読み取りや地域がボトルネックであることを数値で確認する前に、CDN を導入しないでください。
- 上限のないクエリー文字列や
Vary: Cookieによってキー空間が膨れ上がり、キャッシュがほとんど効かなくなるのを避けてください。 - 単一リージョンのオリジンシールドを無料のものとして扱わないでください。新たなボトルネック/単一障害点になります。
- 古さを一切許容できないデータを、TTL によるキャッシュで常に最新の状態に保とうとしないでください。
- ホットアセットに不用意な
no-cacheを設定しないでください。気付かないうちにオリジンのエグレスが 10 倍になるおそれがあります。
重要な数値
判断を左右する値は、ヒット率(目標は 90% 以上。約 80% を下回る場合は、コンテンツをキャッシュできるか検討してください)、オリジンオフロード率(1 − オリジンへのリクエスト数/合計リクエスト数)、エグレス(requests × avg object size)です。エッジとオリジンのレイテンシの差が導入のメリットです。エッジヒットなら同一リージョン内の往復(数 ms〜数十 ms)で済みますが、異なる大陸間では約 100 ms かかります。エグレスとオフロードの計算にはback-of-the-envelopeを使い、ここで表の内容を繰り返さないでください。エグレスは通常、CDN の請求額を最も大きく左右する項目です。
インターフェースの概要
契約の大部分は、オリジンが設定し、エッジが従う HTTP キャッシュヘッダー です。
- フィンガープリント付きの静的コンテンツには、
Cache-Control: public, max-age=31536000, immutableを設定します。 - 定期的に更新される安定した URL には、
Cache-Control: public, max-age=60, stale-while-revalidate=600を設定します。 ETag/Last-Modifiedを使って、低コストで再検証(304 Not Modified)できるようにします。Varyは、実際に配信内容を変えるヘッダーにのみ指定します(不注意にVary: Cookieを設定すると、キャッシュヒット率が大きく低下します)。- キャッシュキー:URL パスと、明示的な許可リストに含まれるクエリパラメーターです。該当する場合、どの Cookie/ヘッダーをキーに含めるかも決めます。
無効化には、PURGE/無効化 API の呼び出し、または(できれば)URL のバージョン更新を使います。バージョン付き URL を使えば、無効化の伝播競合を完全に回避できます。
プロバイダーの選択
まずは上記の汎用的な方法を使ってください。ユーザーがクラウドを指定した場合は、マネージドサービスへの対応付け、クォータ/制限、プロバイダー固有のトレードオフについて、references/providers/<provider>.md を参照してください。そのプロバイダーのファイルが存在しない場合は、汎用的な方法が答えになります。
図
エッジ → シールド → オリジンのプル経路(および破線で示すコールドミス時の矢印と、クライアントから最寄りの PoP への地理的ルーティング)を視覚化するには、プラグイン内の architecture-diagram スキルを使ってください。エッジノードにはキャッシュの色、オリジンにはストアの色を使ってください。ここに Mermaid を埋め込まないでください。
関連する構成要素
caching— 概念の担当範囲: 無効化、エビクション、TTL、サンダリングハード理論を扱います。CDN は、アプリ/分散キャッシュの上位にあるエッジ階層であり、静的コンテンツ/メディアではオリジンからの読み取りの代替手段です。load-balancing— 概念の担当範囲: ユーザーを最寄りのエッジに誘導する、地理的ルーティング/エニーキャストルーティングとオリジンのヘルスチェックを扱います。back-of-the-envelope— この項目への入力: CDN の導入を正当化する、エグレス、オフロード率、レイテンシ改善の数値を提供します。data-storage— 依存先: 通常 CDN のオリジンとなるオブジェクトストアを扱います。consistency-coordination— この項目の代替手段: 古さを許容できる時間枠が一切ないデータに使います(TTL ベースのエッジではなく、オリジンから配信します)。system-design— 組み合わせて使う項目(逆リンク):静的コンテンツ/メディアを地理的規模で配信する設計から、ここへ誘導するオーケストレーターです。
参考資料
references/deep-dive.md— キャッシュキーの正規化、Cache-Controlディレクティブの意味、プッシュとプルの仕組み、オリジンシールド/階層型トポロジー、無効化とバージョン管理の競合、マルチ CDN、メディアに特化した配信(セグメント化された HLS/DASH、範囲リクエスト、署名付き URL)を扱います。エッジ層を詳細に設計する際に参照してください。references/providers/{generic,aws,azure,gcp}.md— 各環境におけるサービスの対応付け、制限、落とし穴をまとめています。