大規模サイトの画像配信アーキテクチャ - 設計パターンと実装
大規模画像配信の課題 - なぜ専用アーキテクチャが必要か
月間 1 億 PV を超えるサイトでは、画像配信が技術的・経済的に最大の課題の 1 つになります。1 ページあたり平均 20 枚の画像があるとすると、月間 20 億回の画像リクエストが発生します。これを安定的かつ高速に処理するには、専用の配信アーキテクチャが不可欠です。
技術的課題:
- 帯域幅: 画像 1 枚の平均サイズが 100KB とすると、月間 200TB の転送量が発生します。ピーク時 (セール、ニュース速報) には通常の 5-10 倍のトラフィックが集中します。
- レイテンシ: グローバルユーザーに対して 100ms 以内のレスポンスを実現するには、地理的に分散したエッジサーバーが必要です。
- バリアント管理: 1 枚の元画像から、フォーマット (AVIF/WebP/JPEG) × 解像度 (400/800/1200/1,600 px) × デバイスピクセル比 (1x/2x) = 24 バリアントが必要になる場合があります。
- 可用性: 画像が表示されないとユーザー体験が著しく低下するため、99.99% 以上の可用性が求められます。
経済的課題: CDN の転送量課金は $0.02-0.12/GB (リージョンにより異なる) で、月間 200TB の場合 $4,000-24,000 のコストが発生します。ストレージ、コンピュート (画像変換)、リクエスト課金を含めると、画像配信だけで月額 $10,000-50,000 に達する大規模サイトも珍しくありません。
これらの課題に対応するため、画像配信に特化したアーキテクチャパターンが発展してきました。以下のセクションで、主要なパターンとその選択基準を解説します。
パターン 1: 静的生成 + CDN 配信 (ビルドタイム変換)
最もシンプルなアーキテクチャで、ビルド時に全バリアントを事前生成し、S3 などのオブジェクトストレージに配置して CDN で配信します。
構成: ビルドパイプライン (sharp/imagemin) → S3 → CloudFront/Cloudflare
動作フロー: (1) CI/CD パイプラインで元画像から全バリアント (AVIF/WebP/JPEG × 複数解像度) を生成。(2) 生成されたファイルを S3 にアップロード。(3) CloudFront が S3 をオリジンとして配信。(4) HTML の <picture> 要素で適切なバリアントを参照。
メリット:
- リクエスト時のコンピュートが不要で、レイテンシが最小 (CDN キャッシュヒット時は 5-20ms)
- アーキテクチャがシンプルで障害点が少ない
- CDN のキャッシュヒット率が高い (URL ごとに 1 つのファイルが対応するため)
- コスト予測が容易 (ストレージ + 転送量のみ)
デメリット:
- バリアント数 × 画像数のストレージが必要 (10 万枚 × 24 バリアント = 240 万ファイル)
- 新しいフォーマットや解像度を追加する際、全画像の再生成が必要
- ビルド時間が画像数に比例して増加 (10 万枚で数時間)
適用条件: 画像数が 10 万枚以下、更新頻度が低い (日次以下)、バリアント数が限定的 (8 種類以下) なサイトに最適です。ブログ、コーポレートサイト、小〜中規模の EC サイトが該当します。
パターン 2: オンデマンド変換 + エッジキャッシュ
画像のバリアントを事前生成せず、リクエスト時に動的に変換してキャッシュするパターンです。imgix、Cloudinary、Cloudflare Images などの画像 CDN サービスがこのアーキテクチャを採用しています。
構成: クライアント → CDN エッジ → 画像変換レイヤー (Lambda@Edge / Workers) → オリジンストレージ (S3)
動作フロー: (1) クライアントが /images/hero.jpg?w=800&f=avif&q=75 のような URL でリクエスト。(2) CDN エッジにキャッシュがあればそのまま返却。(3) キャッシュミスの場合、画像変換レイヤーがオリジンから元画像を取得し、パラメータに従って変換。(4) 変換結果をエッジにキャッシュして返却。
メリット:
- オリジンには元画像 1 枚のみ保持すればよく、ストレージコストが最小
- URL パラメータで任意のサイズ・フォーマット・品質を指定でき、柔軟性が高い
- 新しいフォーマットの追加が変換レイヤーの更新だけで完了
- 画像数が増えてもビルド時間に影響しない
デメリット:
- キャッシュミス時のレイテンシが高い (変換処理に 100-500ms)
- 変換レイヤーのコンピュートコストが発生 (リクエスト数に比例)
- CDN サービスの料金体系が複雑 (変換回数、配信量、ストレージの複合課金)
- 変換レイヤーの障害がサイト全体の画像表示に影響
適用条件: 画像数が 10 万枚以上、バリアント数が多い (デバイス・フォーマットの組み合わせが多数)、画像の追加頻度が高い (UGC サイト、大規模 EC) サイトに最適です。初期コストは高いですが、スケーラビリティに優れます。
パターン 3: ハイブリッド構成 - 静的 + オンデマンドの組み合わせ
実際の大規模サイトでは、パターン 1 と 2 を組み合わせたハイブリッド構成が最も多く採用されています。アクセス頻度に応じて配信方式を使い分けることで、コストとパフォーマンスを最適化します。
構成例:
- 高頻度画像 (上位 20%): ビルド時に全バリアントを事前生成し、CDN で配信。ヒーロー画像、カテゴリバナー、人気商品画像など、アクセスの 80% を占める画像群。
- 中頻度画像 (中位 30%): 主要バリアント (AVIF + WebP の 2 解像度 = 4 種) のみ事前生成。残りのバリアントはオンデマンド変換。
- 低頻度画像 (下位 50%): 元画像のみ保持し、全バリアントをオンデマンド変換。アクセスが少ないため、キャッシュミスのレイテンシ増加の影響が限定的。
実装のポイント:
アクセスログの分析に基づいて画像を分類し、上位 20% の画像リストを定期的に更新します。新商品の追加や季節変動により、高頻度画像は変化するため、週次でリストを再計算するのが理想です。
CDN のキャッシュ TTL も階層化します。高頻度画像は TTL 1 年 (immutable)、中頻度画像は TTL 30 日、低頻度画像は TTL 7 日に設定し、キャッシュストレージの効率を最大化します。
コスト最適化の効果: 全画像をオンデマンド変換する場合と比較して、ハイブリッド構成ではコンピュートコストを 60-70% 削減できます。上位 20% の画像が全リクエストの 80% を占める (パレートの法則) ため、この部分を事前生成するだけで大半のリクエストがコンピュート不要になります。
キャッシュ戦略の設計 - 多層キャッシュとインバリデーション
画像配信のパフォーマンスとコストは、キャッシュ戦略の設計に大きく依存します。適切なキャッシュ設計により、オリジンへのリクエストを 95% 以上削減できます。
多層キャッシュ構成:
- L1: ブラウザキャッシュ -
Cache-Control: public, max-age=31536000, immutableで 1 年間キャッシュ。ファイル名にコンテンツハッシュを含めることで、更新時に自動的に新しい URL が生成されます。 - L2: CDN エッジキャッシュ - ユーザーに最も近いエッジサーバーにキャッシュ。キャッシュヒット率 90% 以上を目標とします。
- L3: CDN オリジンシールド - エッジとオリジンの間に配置される中間キャッシュ層。複数のエッジサーバーからのキャッシュミスを集約し、オリジンへのリクエストを削減します。CloudFront の Origin Shield や Cloudflare の Tiered Cache がこれに該当します。
- L4: オリジンストレージ - S3 などのオブジェクトストレージ。最終的なデータソース。
キャッシュキーの設計: コンテンツネゴシエーション (Accept ヘッダーによるフォーマット切り替え) を使用する場合、キャッシュキーに Accept ヘッダーの正規化値を含める必要があります。Accept ヘッダーはブラウザごとに微妙に異なるため、そのまま使うとキャッシュが分散します。Lambda@Edge で Accept ヘッダーを avif、webp、default の 3 値に正規化し、キャッシュキーに含めます。
キャッシュインバリデーション: 画像を更新する場合、ファイル名にハッシュを含める方式 (cache busting) が最も確実です。/images/product-abc123.avif のように、画像の内容が変わればファイル名も変わるため、古いキャッシュが返される問題が発生しません。CDN のパージ API による明示的なインバリデーションは、緊急時のみ使用します (全エッジへの伝播に 5-30 秒かかるため)。
障害対策とフォールバック設計
画像配信の障害はユーザー体験に直結するため、多重のフォールバック機構を設計します。
CDN 障害時のフォールバック: プライマリ CDN (例: CloudFront) が障害を起こした場合に、セカンダリ CDN (例: Cloudflare) に自動切り替えする DNS フェイルオーバーを設定します。Route 53 のヘルスチェックで CDN のエンドポイントを監視し、応答がない場合に 30 秒以内でフェイルオーバーします。
画像変換レイヤーの障害時: オンデマンド変換が失敗した場合、元画像 (JPEG/PNG) をそのまま返すフォールバックを実装します。変換レイヤーのタイムアウトを 3 秒に設定し、超過した場合はオリジンの元画像を直接配信します。品質は最適ではありませんが、画像が表示されないよりはるかに良い体験です。
オリジンストレージの障害時: S3 のクロスリージョンレプリケーションで、別リージョンにバックアップを保持します。プライマリリージョンの S3 が応答しない場合、CDN のオリジンフェイルオーバーでバックアップリージョンに切り替えます。
画像の段階的劣化 (Graceful Degradation):
- AVIF 変換失敗 → WebP で配信
- WebP 変換失敗 → JPEG で配信
- リサイズ失敗 → 元サイズで配信
- 全変換失敗 → プレースホルダー画像を表示
監視とアラート: CDN のエラー率 (4xx/5xx)、オリジンへのリクエスト率、キャッシュヒット率、レイテンシの p99 を常時監視します。エラー率が 0.1% を超えた場合、キャッシュヒット率が 85% を下回った場合にアラートを発報し、即座に対応できる体制を整えます。画像配信の SLO (Service Level Objective) として、可用性 99.95%、レイテンシ p99 200ms 以内を設定するのが一般的です。