
この記事は、Andrew Alexeevの紹介でOwen Garrettが講演した「ウェビナー」を基に編集したものです。
目次
はじめに
0:00 はじめに1:22 本ウェビナーについて2:17 コンテンツキャッシュの基本原則2:35 基本原則3:59 HTTPキャッシュの仕組み7:46 NGINXは何をキャッシュするのか
コンテンツキャッシュとNGINX
9:55 NGINXの動作10:06 NGINXの設定11:14 キャッシュ処理15:32 キャッシュはHTTPだけのものではない17:10 動作状況を把握する方法17:38 キャッシュの計測19:08 キャッシュの計測(続き)20:09 キャッシュのステータス21:57 NGINXにおけるコンテンツキャッシュの仕組み22:40 動作の仕組み23:53 キャッシュされたコンテンツの保存方法26:36 ディスクからのキャッシュの読み込み28:07 ディスクキャッシュの管理29:22 ディスクからのコンテンツのパージ
キャッシュの制御
31:27 キャッシュの制御32:30 遅延キャッシュ34:45 キャッシュ時間の制御36:18 キャッシュする/しない37:25 複数のキャッシュ39:07 簡単なおさらい – キャッシュが必要な理由39:44 ページ速度が重要な理由40:46 Googleによるルール変更41:28 パフォーマンス低下によるコスト43:00 NGINXのキャッシュでできること45:21 まとめ
質疑応答
47:20 バイト範囲リクエスト49:13 プロキシキャッシュの再検証50:07 同容量のディスクへのキャッシュ分散50:50 Varyヘッダー51:25 キャッシュの基本要素51:52 アップストリームのヘッダーとデータ52:13 *‑Encodingヘッダー52:56 SPDY53:15 Varyヘッダー、第2弾53:45 PageSpeed54:00 その他のキャッシュ
0:00 はじめに

Andrew Alexeev:最新のウェビナーへようこそ。Andrewです。NGINXは、世界中のWebサイトの高速化と応答性の向上、容易なスケーリングを実現するという構想のもと、Igor Sysoevによって開発されました。現在、NGINXは上位サイトの30%以上、インターネット上の全Webサイトの20%以上で利用されています。[編集者注:これらの統計は、ウェビナーが配信された2014年5月時点のものです。現在の数値はこちらをご覧ください。]このウェビナーの内容が、現在運用中または導入予定のNGINX環境に役立ち、活用できるものとなれば幸いです。
それでは、Owen Garrettをご紹介します。OwenはNGINXの製品開発を担当しています。本日は、NGINXの強力なキャッシュ機能を活用し、同じコンテンツを繰り返し生成する負荷からアプリケーションを解放する方法について説明します。
1:22 本ウェビナーについて

Owen Garrett:Andrew、ありがとうございます。皆さん、これから45~50分ほどお付き合いいただき、ありがとうございます。まず、NGINXにおけるコンテンツキャッシュの仕組みについてご説明します。次に、パフォーマンスを向上させる方法をいくつかご紹介します。さらに、コンテンツキャッシュの実際の仕組みを少し掘り下げ、NGINX内部で起きていることをデバッグし、診断できるようにします。最後に、キャッシュ可能なコンテンツをNGINXで非常にきめ細かく制御するための便利なヒントやコツをご紹介します。
Andrewが説明したとおり、いずれも目指すところは同じです。繰り返し生成されるコンテンツによるアップストリームサーバーの負荷を軽減し、ビジネスに不可欠なアプリケーションの実行にリソースを振り向けられるようにします。さらに、これらのサーバーの処理能力を超えて拡張することで、エンドユーザーにより高いレベルのサービスを提供し、インターネットからのトラフィックの急増や、場合によってはアップストリームサーバーの障害が発生しても、サービス全体の信頼性を高めます。
2:17 コンテンツキャッシュの基本原則

NGINXの実装設定について説明する前に、全員が共通の基本情報を踏まえて始められるよう、コンテンツキャッシュの基本的な仕組みを簡単に確認しておきます。
2:35 基本原則

コンテンツキャッシュの基本原則は、繰り返し行われる処理をアップストリームサーバーからオフロードすることです。最初のユーザー(青色のアイコンと線で表示)がWebサイト上のコンテンツをリクエストすると、そのHTTPリクエストはNGINXに転送され、そこから右側に灰色で示されたアップストリームサーバーへ転送されます。
レスポンスはリモートユーザーに返されますが、キャッシュ可能な場合(その意味については後ほど説明します)、NGINXはそのレスポンスのコピーを保存します。その後、オレンジ色で示された別のユーザーが同じコンテンツをリクエストすると、NGINXはアップストリームサーバーにリクエストを転送する代わりに、ローカルキャッシュから直接コンテンツを配信できます。
キャッシュ可能で変更されないコンテンツを保存するという基本原則は、WebブラウザやCDN、アクセス先のサイトが利用するCDN、他のデバイス上のNGINXで採用されています。NGINXは通常、WebコンテンツやWebアプリケーションをホストするオリジンサーバーの近くにあるデータセンターまたはクラウドに導入され、リバースプロキシキャッシュとして動作します。
3:59 HTTPキャッシュの仕組み

オリジンサーバーは、広く理解され、よく知られている複数のHTTPレスポンスヘッダーのうち、1つ以上を使用してコンテンツがキャッシュ可能であることを宣言します。もちろん、キャッシュサーバーは、この動作を無視、上書き、または変更できます。ただし、設定されたコンテンツキャッシュを理解するには、まず、コンテンツがキャッシュ可能で変更されないこと、および[キャッシュされた]コピーに一定の有効期間があることをオリジンサーバーがどのように示すのかを十分に理解する必要があります。
コンテンツキャッシュは、ExpiresというシンプルなHTTPレスポンスヘッダーから始まりました。オリジンサーバーはコンテンツを提供し、そのコンテンツがExpiresヘッダーに記載された日付まで有効であることを宣言していました。この方法は、より効果的で柔軟なCache-Controlヘッダーにすぐに取って代わられました。
Expires はやや扱いにくく、効率的ではありません。日付を適切に書式設定して解析する必要があります。一方、Cache-Control ははるかに簡潔で、コンテンツキャッシュに求められる要件と処理速度に適しています。Cache-Control はコンテンツをpublicまたはprivateとして指定し、publicの場合は、キャッシュオブジェクトがそのコンテンツを再リクエストするまでにキャッシュできる秒数である max-age を指定します。
キャッシュを直接制御する3つ目のヘッダーはX-Accel-Expiresです。このヘッダーはNGINX固有であり、NGINXのみが解釈します。上記のヘッダーの動作をオーバーライドし、コンテンツをキャッシュする正確な期間をNGINXに直接指定する場合に使用します。
Webブラウザにはコンテンツを長期間キャッシュさせる一方で、プロキシキャッシュ(オリジンサーバーの前段に配置されたNGINX)でのキャッシュ期間は短くしたい場合があります。これにより、変更をより迅速に反映して新しいクライアントに配信しつつ、既存のクライアントが不要なときにコンテンツを繰り返しリクエストすることを防げます。
ただし、この方法は、後者2つのヘッダーを使用して実装することもできます。オリジンサーバーは、コンテンツが最後に変更された日時を示すとともに、そのコンテンツを識別する「ETag」(エンティティタグ)と呼ばれる不透明な文字列(多くの場合はハッシュ値)を示すことができます。
その後、クライアントは条件付きGETを使用してリクエストを送信できます。リクエストには、If-Modified-SinceまたはIf-None-Matchヘッダーを含めることができます。これにより、特定の日付に最終更新された、または特定のETagを持つコンテンツのキャッシュ済みバージョンを保持していることをクライアントが示します。サーバーが保持する最新バージョンがクライアントのバージョンと一致する場合、サーバーは単に304 Not Modifiedを返します。これは高速なレスポンスであり、ネットワーク帯域幅を節約するとともに、キャッシュされたコンテンツが引き続き有効かどうかをクライアントが必要に応じて確認できるようにします。
これら5つのヘッダーは、オリジンサーバーの観点から、コンテンツをどのようにキャッシュできるかを定義します。具体的には、コンテンツの有効性、鮮度、およびETagによって示されるコンテンツ自体の詳細です。
7:46 NGINXは何をキャッシュするのか

NGINXなどのプロキシキャッシュには、それらのヘッダーにどの程度厳密に従うかについて、比較的広い裁量があります。キャッシュ不可とされているものをキャッシュすべきでないことは明らかですが、オリジンサーバーがキャッシュ可能と指定していても、必ずキャッシュする義務があるわけではありません。
NGINXの基本的な動作では、レスポンスにGETおよびHEADリクエストがオリジンサーバーによってキャッシュ可能と示され、かつレスポンスにSet-Cookieヘッダーが含まれていない場合、それらをすべてキャッシュします。これは、Set-Cookieヘッダーには通常、各リクエストに固有のデータが含まれており、デフォルトではその値をキャッシュすることが適切ではないためです。
NGINXは、各リソースを特定のキー(キャッシュキー)に基づいてキャッシュします。同じキャッシュキーを生成するすべてのリクエストには、同じリソースが返されます。デフォルトでは、キャッシュは未加工のURL、または設定内ではこのスライドに示す文字列[$scheme$proxy_host$uri$is_args$args]にマッピングされます。NGINXがコンテンツをキャッシュする際、[有効期間]は、優先順位に従い、存在する場合はX-Accel-Expiresヘッダー、次にCache-Controlヘッダー、最後に従来のExpiresヘッダーによって定義されます。
NGINXを調整することで、これらのヘッダーの一部を無視したり、オリジンサーバーの指定にかかわらず固定のキャッシュ時間を設定したりできます。参考までに、RFC 2616では、HTTPに関するプロキシキャッシュの望ましい動作が定義されています。
以上で、キャッシュの汎用性と、通常は安全にキャッシュできるコンテンツをキャッシュしてWebサイトを高速化し、オリジンサーバーの負荷を軽減するNGINXの基本的なデフォルト動作について、概要をご理解いただけたかと思います。
9:55 NGINXの動作

それでは、実際に動作するNGINXを少し見てみましょう。NGINXでコンテンツキャッシュを有効にする設定は、非常に簡単です。
10:06 NGINXの設定

NGINX設定ファイルに数行記述するだけです。1行目では、ファイルの配置方法、[そのキャッシュ内のオブジェクトの]有効期限、キャッシュのサイズを指定する一連のパラメーターを使用して、ディスク上にキャッシュを作成します。次に、2行目の「proxy_cache」ディレクティブをNGINXプロキシに関連付け、コンテンツ(結果やレスポンス)を指定した名前のキャッシュに保存するよう指示します。
ここでは、oneというキャッシュを1つ作成しました。メタデータ用のメモリサイズは10 MBですが、キャッシュされるコンテンツのディスクサイズに上限はありません。コンテンツはキャッシュされ、60分間アクセスがない場合は削除されます。oneというキャッシュは、デフォルトサーバーで使用されます。NGINXのプロキシサーバーで信頼性と一貫性のあるキャッシュを有効にするには、次の2つの[ディレクティブ]、proxy_cache_pathとproxy_cacheで十分です。
11:14 キャッシュ処理

NGINXがリクエストを受信し、キャッシュを照会する際の処理は次のとおりです。まず、リクエストを読み取り(このスライドの左上のボックス)、そのリクエストに対応するリソースを識別するために使用する未加工のURIとその他のパラメーターから、キャッシュキーを組み立てます。次に、メモリ内のメタデータにアクセスしてディスク上のキャッシュを確認し、そのリクエストに対する有効かつ最新のレスポンスのコピーがあるかどうかを調べます。
有効で最新のレスポンスのコピーがあれば、キャッシュヒットとなります。その場合、NGINXはキャッシュから直接レスポンスを返すことができます。NGINXが静的コンテンツを配信する場合とまったく同じ方法で、ディスク上のコンテンツを配信します。そのため、NGINXが提供するよう設計されたレベルのパフォーマンス、信頼性、スケーラビリティを得られます。NGINXのコンテンツキャッシュからコンテンツを配信する場合も、静的コンテンツとまったく同等の[パフォーマンス]が得られます。
一方、キャッシュを確認した際に、キャッシュミスが発生することがあります。これは、コンテンツがキャッシュに存在しないか、キャッシュ内のコンテンツが古く、更新が必要であることを意味します。最も単純なケースでは、オリジンサーバーにそのコンテンツをリクエストし、レスポンスを受信して、キャッシュ可能かどうかを確認します。
キャッシュ可能であれば、NGINXがプロキシモードで処理する大きなレスポンスと同様に、そのレスポンスをディスクにストリーミングします。レスポンスのディスクへのストリーミングが完了したら、それをキャッシュにコピーし、キャッシュから直接応答します。この方法の課題の1つは、NGINXが同じコンテンツに対する複数のリクエストを同時に受信し、そのすべてでキャッシュミスが発生する場合です。
通常、NGINXはこれらのリクエストをすべてオリジンサーバーに転送するため、サーバーに過剰な負荷がかかる可能性があります。特に、レスポンスの生成に時間がかかるリクエストでは、その可能性が高まります。そのため、キャッシュロックを使用できます。proxy_cache_lockディレクティブを使用すると、コンテンツの更新中にアップストリームサーバーへ送信されるリクエストが一度に1件だけになるようにできます。
先ほど説明したシナリオでは、最初のリクエストはアップストリームサーバーに送信されますが、同じコンテンツに対する残りのリクエストは、レスポンスが返されてキャッシュに格納される(この時点ですべてのリクエストに応答できます)か、[proxy_cache_lock_timeout]ディレクティブで設定されたタイムアウトに達するまで保留されます。タイムアウトに達した場合、NGINXはサーバーからコンテンツが返されるまで十分な時間待機したと判断し、その時点で保留されていたリクエストをオリジンサーバーに転送します。
このように、proxy_cache_lockとタイムアウトを使用すると、同じコンテンツへのリクエストが多い高トラフィックのサイトで、そのコンテンツのキャッシュが期限切れになった場合でも、複数のリクエストによってオリジンサーバーに急激な負荷がかからないよう、効果的に制御できます。
NGINXのキャッシュ処理には、もう1つ要素があります。フローチャートのほぼすべての段階に関係するため、この図にはうまく収まりません。それは、proxy_cache_use_staleディレクティブで設定する機能です。どの段階でも、何らかの理由で処理に失敗した場合、たとえばコンテンツの更新中にタイムアウトが発生した場合、アップストリームサーバーから不正な応答が返された場合、またはその他のエラーが発生した場合には、キャッシュされたコンテンツが古くても、キャッシュから直接応答できます。
これは、アップストリームサーバーがトラフィックの集中で過負荷になった場合や、メンテナンスまたは致命的なエラーによって停止した場合に非常に強力なツールです。クライアントにエラーメッセージを返す代わりに、キャッシュ内の古いコンテンツを使用してNGINXがコンテンツの配信を継続できるようにします。
15:32 キャッシュはHTTPだけのものではない

NGINXのキャッシュはHTTPだけに対応しているわけではありません。NGINXは、リクエストをアップストリームのWebサーバーに転送する単なるHTTPプロキシではありません。通常、これらのアップストリームWebサーバーは、FastCGIやSCGIなどのAPIとのインターフェースとして機能します。NGINXは、HTTPプロキシとよく似たプロキシ方式で、これらのAPIと直接やり取りできます。
NGINXでは、キャッシュ機能をHTTPプロキシ、FastCGIプロキシ、uWSGIプロキシ、およびSCGIプロキシに利用できます。いずれもHTTPプロキシとほぼ同様に動作し、キャッシュをディスクに保存して直接応答するため、これらのアップストリームサービスにプロキシする必要がありません。
NGINXはmemcachedサーバーと連携することもできますが、これはやや異なるキャッシュ方式です。この場合、NGINXはコンテンツを直接保存せず、memcachedをプロキシとして使用します。また、必要なコンテンツをmemcachedに格納する処理は外部エージェントに依存します。これも有用な手段となり得ます。必要に応じて、外部サイトからmemcachedにコンテンツを格納する方法は数多くあります。そのため、ビジネス要件に合う場合は、NGINX用のキャッシュにデータを事前投入する方法と考えることができます。
17:10 状況を把握する方法

複数の階層からなる大規模なインフラストラクチャでは、キャッシュを行う階層と行わない階層、コンテンツを生成する階層と生成しない階層が混在するため、キャッシュが非常に複雑になる可能性があります。その場合、何が起きているのか、コンテンツがどこから来ているのかを追跡し、発生した問題を診断してデバッグするのは容易ではありません。NGINXでは、こうした作業をできる限り簡単に行えるようにしています。
17:38 キャッシュの計測

高度な計測機能を使用すると、計測を動的に制御し、コンテンツの取得元、キャッシュ内の保存場所、キャッシュ内でのステータスを追跡できます。
最初のステップは、$upstream_cache_status変数を使用することです。この変数は、キャッシュからの応答かどうかにかかわらず、NGINXが応答したリクエストごとに算出されます。そして、add_headerディレクティブを使用して、この変数の値を継続的にレスポンスへ追加します。通常、この値はレスポンスのX-Cache-Statusヘッダーに格納します。この変数には7つの値のいずれかが設定され、コンテンツがどのように配信されたかを示します。たとえば、キャッシュをバイパスしたか、再検証によって配信されたか、キャッシュヒットだったかを確認できます。
これは、レスポンスの送信元を把握するための第一歩です。ローカルのNGINXキャッシュから送信されたのか、それともアップストリームサーバーから送信されたのかを確認します。このレスポンスヘッダーは、curlなどのツールを使用してコマンドラインから確認する方法や、一般的にはWebブラウザーの対話型デバッガーを使用する方法など、さまざまな方法で調べることができます。
もちろん、その値をすべてのエンドユーザーに公開したくない場合もあります。レスポンスヘッダーにその値を挿入するタイミングは、選択できるようにする必要があります。
19:08 キャッシュ計測(続き)

NGINXの設定言語では、条件に応じて柔軟に処理を選択できます。この例は、その数ある方法の1つです。リモートアドレスを取得し、それがlocalhostである場合は、$upstream_cache_statusを一時変数($cache_status)に格納します。最後に、レスポンスを返す際に、その一時変数をレスポンスに含めます。
このようにすると、指定したIPアドレスからのリクエストに対してのみ、$upstream_cache_status変数の値が表示されます。このほかにもさまざまなことができます。次に、コンテンツがディスクに保存される仕組みを見ていきます。ディスク上の保存場所の算出に使用するキーをレスポンスに含めることもできます。また、キャッシュの稼働中に問題を診断できるよう、さまざまなパラメーターをレスポンスに含めることもできます。
20:09 キャッシュステータス

NGINX Plusは、NGINXの商用版であり、キャッシュなどのユースケースに役立つさまざまな追加機能を提供します。NGINX Plusは、モスクワのエンジニアリングチームが開発し、正常な動作を確認するための広範な回帰テストを実施した、商用サポート付きのNGINXビルドです。
NGINX Plusには、NGINXをリバースプロキシおよびロードバランサーとして使用する企業向けの機能も数多く搭載されています。これには、負荷分散、ヘルスチェック、高度な動画ストリーミングに関する機能のほか、拡張ステータス、診断機能の強化、NGINXの稼働状況の可視化に関する機能が含まれます。
[編集者注 – 上のスライドと次の段落は、ここで当初説明していた個別のステータスモジュールに代わり、同モジュールを非推奨とするNGINX Plus APIを参照するよう更新されています。]
デモを確認するには、demo.nginx.com/dashboard.htmlにアクセスしてください。NGINX Plus APIを使用してNGINX Plusから公開されたステータスデータを表示するWebページが開きます。また、指定されたcurlコマンドを実行すると、NGINX Plusバイナリから直接取得された未加工のJSONデータを確認できます(ここでは、各要素を1行ずつ階層的にインデントして表示するため、jqユーティリティにパイプしています)。
そのJSONデータには、NGINX Plus環境内の各キャッシュの状態に関するリアルタイムデータが含まれています。このデータを$upstream_cache_status変数やその他のキャッシュ計測方法と組み合わせることで、NGINXがコンテンツをキャッシュする仕組みを詳しく把握できます。また、個々のリクエストを掘り下げ、そのリクエストがキャッシュから返されたかどうかや、キャッシュ内での現在のステータスを確認できます。
21:57 NGINXにおけるコンテンツキャッシュの仕組み

ここまではコンテンツキャッシュを外部から確認する方法を見てきました。次に、内部の仕組みを見てみましょう。NGINX内ではどのように機能するのでしょうか。先ほど説明したように、NGINXのコンテンツキャッシュは、ディスク上のファイルとほぼ同じように処理されます。そのため、コンテンツキャッシュから配信する場合も、静的コンテンツを配信する場合と同等のパフォーマンスと信頼性が得られ、同じオペレーティングシステムの最適化が適用されます。これこそが、NGINXが高く評価されているパフォーマンスです。
22:40 仕組み

コンテンツキャッシュは、永続キャッシュとしてディスクに保存されます。オペレーティングシステムと連携してディスクキャッシュをメモリにスワップし、メモリに保持すべきコンテンツをオペレーティングシステムのページキャッシュに通知します。これにより、必要なときにキャッシュからコンテンツを極めて高速に配信できます。
キャッシュに格納されている内容やその有効期限など、キャッシュに関するメタデータは、すべてのNGINXプロセスで共有される共有メモリ領域に別途保存され、常にメモリ上に保持されます。そのため、NGINXはキャッシュへの問い合わせや検索を極めて高速に実行できます。ページキャッシュへのアクセスが必要になるのは、レスポンスを取得してエンドユーザーに返すときだけです。
コンテンツがキャッシュに保存される仕組み、起動時に永続キャッシュが空のNGINXワーカープロセスに読み込まれる仕組み、NGINXがキャッシュに対して自動的に行うメンテナンスについて説明します。最後に、特定の状況でキャッシュからコンテンツを手動で削除する方法を紹介します。
23:53 キャッシュされたコンテンツはどのように保存されるか

コンテンツキャッシュは、proxy_cache_pathというディレクティブを使用して宣言します。このディレクティブでは、ファイルシステム上のキャッシュの保存場所、キャッシュ名、メタデータ用のメモリ内キャッシュサイズ、ディスク上のキャッシュサイズなどのパラメーターを指定します。この例では、ディスク上のキャッシュサイズは40 MBです。
コンテンツの保存場所を把握するには、キャッシュキー、つまりキャッシュ可能な各リソースにNGINXが割り当てる一意の識別子を理解することが重要です。デフォルトでは、この識別子はリクエストの基本パラメーター(スキーム、Hostヘッダー、URI、任意の文字列引数)から構成されます。
ただし、必要に応じて、Cookieの値や認証ヘッダー、さらには実行時に算出した値などを使用して、キャッシュキーを拡張できます。たとえば、英国のユーザー向けと米国のユーザー向けに異なるバージョンを保存する場合があります。これはすべて、proxy_cache_keyディレクティブを設定することで実現できます。
NGINXはリクエストを処理する際に、proxy_cache_keyを計算し、その値からMD5ハッシュを算出します。スライドの下部に示したコマンドラインの例を使って、同じ処理を再現できます。キャッシュキーhttplocalhost:8002/time.phpをmd5sumに渡します。シェルから実行する場合は、改行まで一緒に渡さないよう注意してください。
これにより、キャッシュ可能なコンテンツに対応するMD5ハッシュ値が計算されます。NGINXはそのハッシュ値を使用して、コンテンツを保存するディスク上の場所を算出します。proxy_cache_pathでは、1文字のディレクトリと、その下の2文字のディレクトリからなる2階層のキャッシュを指定しています。文字列の末尾からこれらの文字を取り出し、4というディレクトリと、その下に9bというサブディレクトリを作成します。その後、キャッシュのコンテンツ(ヘッダーと少量のメタデータを含む)をディスク上のファイルに格納します。
コンテンツキャッシュをテストできます。キャッシュキーをレスポンスヘッダーの1つとして出力し、その値を「md5sum」で処理して、対応するハッシュ値を計算できます。その後、ディスク上の値を調べて、実際に保存されていることと、NGINXがキャッシュしたヘッダーを確認すれば、全体の仕組みを把握できます。
26:36 ディスクからのキャッシュの読み込み

コンテンツがディスクに保存され永続化されたため、NGINXの起動時には、そのコンテンツをメモリに読み込む必要があります。より正確には、ディスクキャッシュを順に処理してメタデータを抽出し、各ワーカープロセスが使用する共有メモリセグメントにそのメタデータを読み込みます。この処理は、キャッシュローダーと呼ばれるプロセスによって実行されます。
キャッシュローダーは起動時に立ち上がり、1回だけ実行されます。メタデータを一度に100ファイルずつ、200 ms以内で小分けに読み込み、処理の合間に50 ms停止します。キャッシュ全体の処理が完了し、共有メモリセグメントにメタデータが格納されるまで、この動作を繰り返します。
その後、キャッシュローダーは終了します。NGINXが再起動または再構成され、共有メモリセグメントの再初期化が必要にならない限り、再度実行する必要はありません。非常に高速なディスクを使用し、負荷が軽い場合などは、キャッシュローダーの動作を調整できます。実行速度を上げることもできます。一方、低速なディスクに膨大な数のファイルをキャッシュしており、NGINXの起動時にキャッシュローダーがCPUを過剰に使用するのを避けたい場合は、実行速度を少し抑えることもできます。
28:07 ディスクキャッシュの管理

キャッシュがメモリに読み込まれ、ファイルがディスクに保存されると、一度もアクセスされないキャッシュファイルがいつまでも残り続ける可能性があります。NGINXはファイルを初めて検出したときに保存しますが、その後リクエストがなければ、何らかの処理によって削除されるまで[そのファイル]はディスク上に残り続けます。
この処理を担うのが「キャッシュマネージャー」です。キャッシュマネージャーは定期的に実行され、一定期間アクセスされていないファイルをディスクから削除します。また、キャッシュが大きくなりすぎて指定サイズを超えた場合にも、ファイルを削除します。削除は、最終アクセス時刻が古いファイルから順に行われます。キャッシュローダーと同様に、proxy_cache_path[ディレクティブ]のパラメーターを使用して、この処理を設定できます。
- The
inactive時間のデフォルト値は10分です。 - The
max-sizeパラメーターにはデフォルトの上限がありません。キャッシュにmax-sizeの上限を設定すると、一時的にその上限を超えることがありますが、キャッシュマネージャーの実行時に、使用時期が最も古いファイルが削除され、上限未満に戻ります。
29:22 ディスクからのコンテンツのパージ

最後に、ディスクからコンテンツを消去したい場合もあります。対象のファイルを見つけて削除するには、前述の手法、つまりキャッシュキーを「md5sum」に渡す方法、またはファイルシステム全体で「grep」を再帰的に実行し、削除対象のファイルを特定する方法を知っていれば、比較的簡単に対応できます。
また、NGINX Plusを使用している場合は、製品に組み込まれているキャッシュパージ機能を利用できます。この機能では、リクエストの特定のパラメーターを使用できます。通常、キャッシュパージリクエストであることを識別するために、PURGEというメソッドを使用します。
パージは、URIを検査し、そのURIに一致するすべてのファイルをキャッシュから削除する、NGINX Plusの専用ハンドラーによって処理されます。URIの末尾にアスタリスクを付けると、プレフィックスとして使用できます。ここでは、パージ機能を使用して、localhostのポート8001から配信されるすべてのファイルを削除しますが、サブディレクトリを指定することもできます。
どの方法を使用する場合でも、ディスク上のキャッシュからファイルを削除したり、キャッシュディレクトリ全体に対して rm -rf を実行したりしても、いつでもまったく問題ありません。NGINXは中断することなく、ディスク上にファイルが存在するかどうかを引き続き確認します。ファイルが存在しなければ、キャッシュミスとなります。その後、NGINXはオリジンサーバーからコンテンツを取得し、ディスク上のキャッシュに再度保存します。そのため、キャッシュから個々のファイルを消去する必要がある場合でも、常に安全かつ確実に安定した動作を維持できます。
31:27 キャッシュの制御

ここまで、キャッシュの仕組みやNGINXでの実装、さらに静的コンテンツと同等のパフォーマンスを実現するためにファイルをディスクへ保存する方法について詳しく見てきました。次は、キャッシュをもう少し高度に活用する方法を見ていきましょう。
シンプルなサイトでは、キャッシュを有効にするだけで、通常は求めるレベルのパフォーマンスとキャッシュ動作を維持できます。ただし、最適化の余地は常にあり、デフォルトの動作が要件に合わない場合も少なくありません。
オリジンサーバーが適切なレスポンスヘッダーを設定していない場合や、オリジンサーバーの指定をNGINX自体で上書きしたい場合もあります。NGINXには、キャッシュの動作を細かく調整するためのさまざまな設定方法があります。
32:30 遅延キャッシュ

キャッシュを遅延させることもできます。大量のコンテンツがあり、その多くが1時間または1日に1~2回しかアクセスされない場合によくある状況です。たとえば、ほとんど読まれない会社案内をキャッシュしようとしても、多くの場合は時間の無駄になります。遅延キャッシュでは、しきい値を設定できます。コンテンツのキャッシュ版が保存されるのは、一定回数リクエストされた場合のみです。proxy_cache_min_usesのしきい値に達するまでは、キャッシュに保存されません。
これにより、キャッシュに格納するコンテンツをより細かく選別できます。キャッシュ自体は有限のリソースであり、できる限りキャッシュをメモリ上に保持する必要があるため、通常はサーバーに搭載されているメモリ容量によって上限が決まります。そのため、特定の種類のコンテンツを制限し、リクエストの多いコンテンツだけをキャッシュに格納することが一般的です。
キャッシュ再検証は、NGINX Open SourceとNGINX Plusに最近追加された機能です。この機能は、If-Modified-Sinceの動作を変更します。NGINXがキャッシュ済みの値を更新する際、コンテンツの新しいバージョンを取得するために単純なGETを実行するのではなく、「この日時に更新されたキャッシュ済みバージョンを保持している」と伝える条件付きGETを実行します。
オリジンサーバーは、304 Not Modifiedを返すことで、キャッシュされているバージョンが依然として最新であることを示せます。これにより、アップストリームの帯域幅を節約できます。オリジンサーバーは変更されていないコンテンツを再送信する必要がなく、ディスクへの書き込みも削減できる可能性があります。また、NGINXはそのコンテンツをディスクにストリーミングしてから、古いバージョンを上書きして置き換える必要がありません。
34:45 キャッシュ時間の制御

コンテンツをキャッシュする期間は、きめ細かく制御できます。多くの場合、オリジンサーバーはブラウザに適したキャッシュヘッダーを付けてコンテンツを配信します。これは、比較的頻繁にコンテンツの更新をリクエストしながら、長期間キャッシュする設定です。ただし、変更をより迅速に反映するために、オリジンサーバーの直前に配置したNGINXプロキシでは、ファイルをもう少し頻繁に更新することもできます。
ブラウザーのキャッシュタイムアウトを60秒から10秒に短縮すると負荷が大幅に増加しますが、NGINXのキャッシュタイムアウトを60秒から10秒に短縮した場合、負荷の増加はごくわずかです。後者では、リクエストごとにオリジンサーバーへのリクエストが1分あたり5件増えるだけですが、リモートクライアントの場合は、サイト上で同様のコンテンツを利用しているクライアントの数によって異なります。
そのため、オリジンサーバーが指定するロジックや意図を上書きできます。また、特定のヘッダーをマスクするか、NGINXで無視するように設定できます。対象となるヘッダーは、X-Accel-Expires、Cache-Control、またはExpiresです。さらに、NGINX設定でproxy_cache_validディレクティブを使用して、デフォルトのキャッシュ時間を指定できます。
36:18 キャッシュする/キャッシュしない

オリジンサーバーがキャッシュ可能としているコンテンツをキャッシュしない場合や、NGINXに保存されているバージョンのコンテンツを確実にバイパスしたい場合があります。proxy_cache_bypassディレクティブとproxy_no_cacheディレクティブを使用すると、このような制御が可能です。
これらのディレクティブを使用すると、HTTP認証など、特定のリクエストヘッダーのいずれかが設定されている場合や、リクエストパラメーターが存在する場合に、キャッシュをバイパスするよう簡潔に指定できます。これにより、NGINXのキャッシュを自動的に更新するか、キャッシュを完全にスキップして常にオリジンサーバーから取得できます。
通常、これらはかなり複雑なキャッシュ処理の判断に使用します。Cookieや認証ヘッダーの値に基づいて、キャッシュするもの、常にオリジンサーバーから取得するもの、NGINXキャッシュに一切保存しないものをきめ細かく制御できます。
37:25 複数のキャッシュ

最後に、非常に大規模なデプロイでは、いくつかの理由から、1つのNGINXインスタンス内で複数のキャッシュを使用することを検討してもよいでしょう。サイトの特性やパフォーマンスの重要度、さらには共有ホスティング環境で各テナントが契約しているプランに応じて、NGINXプロキシ上のテナントごとに異なるキャッシュポリシーを設定できます。
また、NGINXホストに複数のディスクがある場合は、各ディスクに個別のキャッシュを配置するのが最も効率的です。基本原則は、ディスク間のコピー回数を最小限に抑えることです。そのためには、各ディスクにキャッシュを固定し、そのキャッシュを使用する各プロキシの一時ファイルを適切なディスクに固定します。
標準的な動作では、NGINXがアップストリームプロキシからコンテンツを受信すると、そのコンテンツが十分に小さくメモリに収まる場合を除き、ディスクにストリーミングします。ディスクへのストリーミングが完了すると、コンテンツをキャッシュに移動します。キャッシュ用の一時ファイルの保存先(その一時ファイルが保存されるディスク)とキャッシュが保存されるディスクが同じであれば、この処理の効率が飛躍的に向上します。
39:07 簡単なおさらい – キャッシュを使用する理由

ここまで、キャッシュ、NGINXで使用される方式、NGINX内部での実装、チューニング方法について説明してきました。終わりに近づいてきましたので、そもそもコンテンツをキャッシュする理由を簡単に振り返ってみましょう。NGINXは世界中の1億1,400万のWebサイトに導入されており、その多くがWeb高速化とコンテンツキャッシュの機能を理由にNGINXを採用しています。[編集者注:この統計は、2014年5月にウェビナーが配信された時点のものです。]
39:44 ページ速度が重要な理由

これらの機能はWebサイトを高速化し、エンドユーザー体験を向上させます。Webページの表示速度は極めて重要です。アナリストは長年にわたりユーザーの行動を観察し、一般に「N秒ルール」と呼ばれる基準を導き出してきました。これは、平均的なユーザーがページの読み込みと表示を待てる時間を示すものです。この時間を超えると、ユーザーは退屈して待ちきれなくなり、競合他社など別のWebサイトへ移ってしまいます。
標準が向上し、ユーザーの期待がますます高まるにつれて、ユーザーが待てる時間はどんどん短くなっています。少々怪しい計算でこの傾向を延長すると、2016年頃にはユーザーの忍耐力がマイナスになる、という結論に達するかもしれません。
40:46 Googleがルールを変えた

しかし実際には、テクノロジーの進歩が私たちを追い越しています。数年前、GoogleはGoogle Instant Searchの導入時に、このことを視覚的に示しました。現在のGoogleでは、検索ボックスに検索語を入力すると、入力を終える前から検索結果の候補が表示されます。これは、現代のインターネットに対する期待が大きく変化したことを示しています。Google自身が述べているように、「今やユーザーは、書籍のページをめくるときと同じように、ウェブページがすばやく、シームレスかつ滑らかに反応することを期待しています」。
41:28 パフォーマンス低下によるコスト

そのレベルのパフォーマンスを達成できない場合、WebサイトやWebサービスに設定したKPIに大きな影響が及ぶ可能性があります。たとえば広告のクリック率について、Googleは、検索ページの読み込み時間が0.5秒長くなると、広告のクリック率が20%低下することを確認しています。また収益については、AmazonがWebページの表示速度低下による影響を調査するため、ページの読み込み時間を意図的に100 ms単位で延ばしたところ、影響を受けた顧客からの収益は、100 ms延びるごとに通常1%減少することがわかりました。
ほかにも多くのアナリストやWebサイト、調査機関が、ページ滞在時間や直帰率など、Webサイトの指標に同様の影響があると報告しています。最近では、Googleが検索結果のページランクを算出する際に、ページ速度を考慮するようになりました。評価に影響するのは、最初の1バイトを受信するまでの時間とみられます。ページ読み込み時に最初の1バイトを受信するまでの時間が長いほど、ページランクに対するペナルティが大きくなります。その結果、Googleの検索結果で3ページ目、4ページ目、5ページ目に表示され、Webサイトにアクセスすらされない可能性があります。
43:00 NGINXキャッシュでできること

NGINXのキャッシュ機能を使用すると、最初の1バイトが届くまでの時間を短縮し、Webコンテンツをより軽快かつ迅速に応答させることで、エンドユーザーエクスペリエンスを向上できます。
NGINXを使用すると、Webインフラストラクチャを統合して簡素化できます。NGINXは、単独のWebキャッシュにとどまりません。WebオリジンサーバーやFastCGIなどのAPIへの直接ゲートウェイを備えており、NGINX Plusでは、高度でエンタープライズ対応のロードバランサーとアプリケーションデリバリーコントローラーを構築する機能も提供します。これにより、Webインフラストラクチャ内のさまざまなネットワークコンポーネントをNGINXまたはNGINX Plusという単一のコンポーネントに統合し、信頼性が高く、デバッグしやすい、より高速なソリューションを実現できます。
NGINXを使用すると、反復的なタスクをアップストリームサーバーから切り離し、サーバー容量を増やすことができます。実際、キャッシュできないように見えるコンテンツ(ブログサイトのトップページなど)でも、NGINXプロキシでわずか1秒間キャッシュするだけで効果があります。
100人のユーザーが同じ1秒間に同一のコンテンツをリクエストした場合、NGINXはオリジンサーバーへのリクエストを1件に集約し、キャッシュから各ユーザーにコンテンツを配信します。このとき、コンテンツの遅延は最大でも1秒です。ブログサイトや同様のWebサイトでは、多くの場合これで十分であり、アップストリームサーバーの負荷だけでなく、十分なキャパシティの管理と導入にかかるコストも抑えられるため、パフォーマンスが大幅に向上します。
最後に、NGINXの戦略的な用途の1つを忘れてはなりません。プロキシキャッシュの「use stale」機能を利用することで、アップストリームサーバーの障害による影響を回避できます。アップストリームサーバーの動作が遅い場合、エラーが返される場合、または何らかの障害が発生した場合、NGINXはローカルにキャッシュされたコンテンツへフォールバックし、アップストリームサーバーが復旧するまでそのコンテンツを引き続き使用できます。
45:21 まとめ

世界で最もアクセス数の多いWebサイトの38%が、主にWeb高速化とコンテンツキャッシュの機能を目的としてNGINXを使用しています。[編集者注:この統計は、2014年5月にウェビナーを配信した時点のものです。現在の値はこちらでご確認ください。]その他のソリューションや詳細については、nginx.comのブログや機能概要をご覧ください。NGINXおよびNGINX Plusの機能について紹介しています。また、今後開催予定のウェビナーに加え、このシリーズで過去に開催され、近日中に追加予定のウェビナーも掲載するウェビナー一覧もご覧ください。
これらの機能をさらに詳しく確認するには、nginx.orgおよびnginx.comでドキュメントやソリューションをご覧いただけますが、実際にダウンロードして試すのが一番です。オープンソース製品はnginx.orgから入手できます。また、負荷分散、アプリケーションデリバリ、管理、使いやすさに関する追加機能とサポートを備えた商用製品は、nginx.comから入手できます。
皆さま、お時間を割いてご清聴いただき、誠にありがとうございました。今回のプレゼンテーションとNGINXのコンテンツキャッシュに関する解説が、多くの皆さまにとって有益で理解を深める一助となれば幸いです。
質疑応答
それでは、質問に順番に取り組み、理解度を確認していきましょう。
47:20 バイト範囲リクエスト
バイト範囲リクエストについて質問があります。バイト範囲リクエストは、クライアントがコンテンツをリクエストする際、その一部だけを必要とする場合に使用されます。たとえば動画ファイルの一部分だけが必要な場合です。また、よくあるのがPDFファイルです。ユーザーがPDFファイル内の位置を示すインデックスを含むヘッダーを読み込み、クライアントが特定のページだけをダウンロードしようとする場合です。これはNGINXのコンテンツキャッシュではどのように機能しますか?
処理の流れは次のとおりです。NGINXがリソースに対するバイト範囲リクエストを受信し、そのリソース全体がすでにキャッシュに格納されている場合、クライアントがリクエストしたバイト範囲をキャッシュから返します。そのリソースがキャッシュにない場合、NGINXはアップストリームサーバーにリソース全体をリクエストし、取得したリソースをキャッシュに格納します。現在、この時点ではNGINXはバイト範囲リクエストに従わず、リソース全体をクライアントに返します。ほとんどの場合、この動作で問題ありません。
たとえば、クライアントがPDFドキュメントをダウンロードする場合、最初のリクエストはいずれにしてもドキュメント全体を対象とします。ドキュメントがストリーミングされる場合に限り、クライアントは接続を中断し、バイト範囲リクエストを開始します。したがって、キャッシュ済みのコンテンツに対しては、NGINXはバイト範囲リクエストに従います。キャッシュされていないコンテンツの場合、NGINXはアップストリームサーバーからコンテンツ全体を取得し、そのときに限りコンテンツ全体をクライアントに返します。
49:13 プロキシキャッシュの再検証
これは、プロキシキャッシュの再検証機能に関する質問です。この機能により、NGINXはコンテンツが変更されたかどうかを確認するため、アップストリームサーバーに条件付きGETリクエストを送信できます。質問は次のとおりです。
プロキシキャッシュの再検証では、コンテンツのETagも考慮されますか。それとも、If-Modified-Sinceの日付のみが考慮されますか?
答えとしては、確認されるのはコンテンツの「If-Modified-Since」の日付のみです。実務上は、レスポンスに常に「If-Modified-Since」を含め、「ETag」は任意として扱うことを一般に推奨します。これは、「ETag」がレスポンスで扱う「最終更新」日時ほど一貫して広く処理されていないためです。
50:07 同容量のディスクへのキャッシュの分散
最高のパフォーマンスを得るために、NGINXで単一サイトのキャッシュを同一仕様の複数ディスクに負荷分散することはできますか?
はい、可能ですが、多少の作業が必要です。一般的には、RAIDを使用せずに複数のディスクを導入し、各ディスクに固定した個別のキャッシュを配置します。そのためには、追加の設定とトラフィックの分割が必要です。設定についてサポートが必要な場合は、コミュニティにお問い合わせいただければ、個別のご要望に対応します。NGINX Plusをご利用の場合は、サポートチームまでお問い合わせください。喜んでお手伝いします。
50:50 Varyヘッダー
NGINXはキャッシュのヒットとミスの判定にVaryヘッダーを考慮しますか?
いいえ、NGINXはVaryヘッダーを自動的には処理しません。これが問題となる場合は、Varyヘッダーをプロキシキャッシュキーに追加するだけで、レスポンスの保存に使用する一意のキーにVaryヘッダーの値を含めることができます。これにより、複数の異なるバージョンを保存できます。
51:25 キャッシュの基本要素
すべてのキャッシュプリミティブとディレクティブが正しく適用されていますか?
概ね対応しています。ただし、Varyヘッダーが考慮されないなど、いくつかのエッジケースがあります。多くの場合、各種キャッシュによるRFC要件の解釈にはある程度の幅があります。可能な限り、信頼性と一貫性があり、設定しやすい実装を採用しています。
51:52 アップストリームのヘッダーとデータ
アップストリームのヘッダーとデータの両方がキャッシュされていますか?
はい、どちらもキャッシュされます。キャッシュからレスポンスを受信した場合、レスポンス本文だけでなくヘッダーもキャッシュされています。
52:13 *‑Encodingヘッダー
ヘッダー値はレスポンス本文とともにキャッシュされるため、さまざまな組み合わせのTransfer-EncodingでNGINXが正しく動作しないとすれば、かなり驚きです。Accept-Encodingは多くの場合、Varyヘッダーを通じて実装されるため、キャッシュキーにVaryヘッダーを含める必要があるという前述の説明は、ここにも当てはまります(これをサポートしていないクライアントの場合)。
52:56 SPDY
キャッシュはSPDYでも機能しますか?
もちろんです。NGINXのフロントエンドプロキシと考えることもできますが、実際にはNGINXカーネルと非常に密接に統合されています。SPDYでもキャッシュは機能します。
53:15 Varyヘッダー、第2ラウンド
ここで、Varyヘッダーに関する別の質問です。確認ですが、Varyヘッダーを含むレスポンスとgzipを使用している場合は、その解決策について、TracまたはF5のコミュニティサイトでのディスカッションをご覧ください。最も一般的な方法は、Varyヘッダーをキャッシュキーに組み込むことです。
53:45 PageSpeed
Q:PageSpeedはNGINXのキャッシュを使用しますか、それとも独自のキャッシュメカニズムを使用しますか?
この質問については、PageSpeed開発者にお問い合わせください。
54:00 その他のキャッシュ
Q:他のコンテンツキャッシュとNGINXを比較すると、どのような違いがありますか?
CDNは、非常に効果的なコンテンツキャッシュソリューションです。CDNはサービスとして導入されるため、コンテンツのキャッシュ方法や有効期限の設定に対する制御は限定されますが、コンテンツをエンドユーザーの近くに配置するうえで極めて有効なツールです。NGINXもWebアプリケーションの高速化に非常に効果的であり、CDNとNGINXを併用する構成が広く採用されています。Varnishなどのスタンドアロンキャッシュも、さまざまな点でNGINXと同様に機能する、非常に優れたテクノロジーです。NGINXの利点の1つは、オリジンサーバー機能、アプリケーションゲートウェイ、キャッシュ、ロードバランシングを単一のソリューションに統合できることです。これにより、インフラストラクチャを簡素化して集約できるため、導入や管理が容易になり、問題が発生した場合のデバッグや診断もしやすくなります。
この記事の基になったウェビナーは、こちらからご覧いただけます。
NGINX Plusをお試しいただくには、今すぐ30日間無料トライアルを開始するか、お問い合わせのうえ、ユースケースについてご相談ください。
著者について

関連するブログ記事
チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する
OpenTelemetryを使用してNGINXサーバーの状態と健全性をより明確に把握することで、問題のトラブルシューティングを行い、Webアプリケーションのパフォーマンスを向上できます。
AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正
Akita SoftwareのAPIオブザーバビリティ向け新しいNGINX Plus Certified Moduleを活用して、APIエンドポイントの問題をより迅速に特定し、修正できます。
JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法
Red HatのシニアプリンシパルデベロッパーアドボケイトであるDaniel Oh氏が、Javaプロジェクトをゼロから構築するためのひな形を作成し、Kubernetesネイティブアプリケーションとしてビルドする方法を解説します。
F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング
NGINXaaS for Azureは、NGINX PlusをAzureネイティブのSaaSとして利用できるサービスです。NGINXの設定を容易にリフト&シフトできるため、クラウド導入とモダンアプリの展開を加速できます。

SELinuxでNGINXおよびNGINX Plusを使用する
Red Hat Enterprise Linux(RHEL)および関連ディストリビューションでSecurity-Enhanced Linux(SELinux)を有効にすると、デフォルト設定によりNGINXとNGINX Plusの一部の操作が制限されます。この記事では、すべての機能を利用できるようにSELinuxの設定を変更する方法について説明します。
NGINX PlusによるAWS Auto Scalingグループの負荷分散
新しいnginx-asg-sync統合ソフトウェアにより、NGINX PlusはAWS Auto Scalingグループの負荷を直接分散できるため、クラウドでの負荷分散がさらに柔軟になります