先日発売された nginx 実践入門を読みながら、おー、なるほど、と思ったところをメモもしていたので、それを読書メモとして晒す。ちなみに当方、nginx 初心者。(見よう見まねで conf を書いたことがある、くらい。)
- なるほどポイント
- nginx 起動時に sudo が必要な理由
- 設定ファイルの include の注意点
- バーチャルサーバで使用するホスト名について
- worker_processes ディレクティブには auto が指定できる
- sendfile ディレクティブの効果
- location ディレクティブについて
- HTTP/2 や SPDY の nginx での設定について
- リクエストボディに関する設定
- Unicorn と nginx との組み合わせについて
- 内部リダイレクトについて
- proxy_cache_path と proxy_cache_valid について(疑問)
- nginx のキャッシュの仕組み
- キャッシュをコントロールするレスポンスヘッダ
- 画像サムネイルの作成
- Lua による拡張
- などと感動していたら、OpenRestyェ...
- 感想
- おまけ
- 作者: 久保達彦,道井俊介
- 出版社/メーカー: 技術評論社
- 発売日: 2016/01/16
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (5件) を見る
なるほどポイント
nginx 起動時に sudo が必要な理由
nginx は、初期設定では 80 番ポートを bind した状態で、デーモンとして起動するんだけど、一般ユーザでは 1024 番未満のポートを bind できないので。
設定ファイルの include の注意点
include sites-enabled/*.conf
などとして設定ファイルを include したとき、そのファイルの内容は include ディレクティブを記述した場所にそのまま読み込まれる。なので、location ディレクティブのブロック内に記述した場合、location コンテキストとして扱われることになる。
バーチャルサーバで使用するホスト名について
「 server_name
ディレクティブで指定したホスト名とアクセス時のドメインが一致している」から、そのバーチャルサーバにアクセスが振り分けられる、のではない!
複数のバーチャルサーバを定義している場合、nginx は以下の優先順位でどのバーチャルサーバを使用するかを決定している。
- listen ディレクティブのアドレスとポートに一致するバーチャルサーバ
- リクエストの Host ヘッダが
server_name
ディレクティブで指定したホストに一致したバーチャルサーバ - デフォルトサーバ
振り分けられるのは、これに合致しているから。それだけ。
ちょうど自分のサーバ(趣味運用)も複数ドメインをひとつのサーバで運用していて、なんとなく、「 server_name
で振り分けられるんだろうな」と思っていたけど、上記の説明で腑に落ちた。
...が、読み進めていくと、「HTTPS 通信の場合は、通信のすべてが暗号化されているため、暗号化を解除するまで Host ヘッダの内容を知ることはできません」「このため、どのバーチャルサーバの設定を使用すればよいか server_name
ディレクティブのマッチングを解決できず、1台のサーバで複数ドメインの HTTPS を実現できないという問題が」...という記述が。
これまたちょうど、自分のサーバで運用しているドメインのいずれも、Let's Encrypt を用いた HTTPS 化をしている。...が、上記のような問題は起きておらず。 「なんでや?!」と思いながらさらに読み進めていくと、SNI という、「暗号化されている HTTPS 通信においてリクエストするホスト名を送信先サーバに伝えるための仕様」が、僕が用いている nginx のバージョンではデフォルトで有効になっているため、問題が起きていなかった。ということがわかった。
こんなかんじで、いろんな点が繋がる瞬間、きもちいいぞ。
worker_processes
ディレクティブには auto
が指定できる
auto
を指定すると、CPU のコア数を自動検出しコア数と同じ数のワーカプロセスを起動できる。
sendfile
ディレクティブの効果
これを有効にすると、ファイルの読み込みとレスポンス送信に sendfile()
システムコールが使われるようになる。sendfile()
システムコールを使用すると、ファイルをオープンしているファイルディスクリプタから直接ファイルを送信するため、効率のよいファイル送信ができるようになる。
これと合わせて、 tcp_nopush
ディレクティブを有効にすることで、最も大きなパケットサイズでレスポンスヘッダとファイルの内容を送信でき、送信するパケット数を最小化できる。
location
ディレクティブについて
location
ディレクティブで使用できる修飾子の評価の優先度は、高いものから順に=(完全一致)
~(正規表現)
~*(正規表現・大文字小文字の区別なし)
(なし・前方一致)
^~(前方一致)
。- 前方一致ディレクティブが複数記述された場合の location ディレクティブは、記述した順番に関わらず、文字数が長い(より厳密な指定の)記述が優先される
^~
修飾子の設定にマッチした場合は、それ以降のlocation
ディレクティブが評価されず、^~
修飾子の設定が用いられるlocation
ディレクティブをネストすることで、優先順位をよりわかりやすくすることができる
HTTP/2 や SPDY の nginx での設定について
listen 443 ssl http2
listen 443 ssl spdy
とするだけでいい、ということを知った。もうちょっと面倒くさいものかと思ってた。。
リクエストボディに関する設定
POST メソッドなど大きなサイズのリクエストボディを扱うこともある場合、リクエストボディは nginx によってバッファリングされるため、いくつかのパラメータを調整しておく必要がある。
client_max_body_size
リクエストボディの最大サイズを指定。
client_body_buffer_size
nginx がリクエストボディの読み込みに利用するメモリバッファのサイズを指定する。実際のサイズがここで指定したバッファ容量を超えた場合、nginx は一時ファイルにバッファを出力するため、ディスク I/O が発生しリクエスト処理時間が長くなってしまうことがある。
client_body_temp_path
上記バッファの書き出し先を指定する。出力先デバイスを tmpfs にすることで、ディスクの I/O 待ちを発生させなくすることができる。
Unicorn と nginx との組み合わせについて
Unicorn は nginx とは異なり、イベント駆動型のアーキテクチャを持っていない。そのため、ワーカープロセスが一度に処理できるプロセス数も一つだけ。つまり、同時に処理可能なアクセス数=ワーカープロセス数、となってしまう。
そんな Unicorn と nginx を組み合わせる = Unicorn は nginx とだけやりとりをするだけでよくなった、となった場合、nginx はユーザーとのやりとりを全てバッファリングしてくれるため、Unicorn の各ワーカープロセスがブロックされる時間は、
- 純粋に処理中のとき
- nginx とのやりとりをしている時間
...だけになる。つまり、スループットが向上する。
また、アプリケーションでの処理が必要ない静的ファイルも nginx だけで直接配信できるようにすることでも、Unicorn のワーカープロセスを消費することを低減できる。
内部リダイレクトについて
try_files
ディレクティブと rewrite
ディレクティブは内部リダイレクトで処理される。内部リダイレクトでは、ロケーションを書き換えた上でもう一度そのリクエストの処理を行う、という挙動をするため、リクエストが処理された状態は引き継がれない。
proxy_cache_path
と proxy_cache_valid
について(疑問)
proxy_cache_path
ディレクティブで指定できる inactive=1d
などによる有効期限の指定だと、「指定した有効期限より長くリクエストされなかったらキャッシュ削除」となるんだけど、これを「1日経ったら問答無用で削除」みたいな挙動にしたい場合ってどうするんだろう。
少し調べてみた結果、本にも書いてある proxy_cache_valid
ディレクティブでやりたいことはできそうなかんじだろうか?
たとえば、
proxy_cache_path /var/hoge/fuga/cache inactive=1h; proxy_cache_valid 200 2h;
このように設定していた場合、早ければ(キャッシュへのアクセスが1時間、無ければ)1h でキャッシュが削除され、長くても 2h (正常アクセスの場合)で削除される。という理解でよいのだろうか。
これとは別に、キャッシュパージャー(purger)というキャッシュ削除の仕組みもあるようなのだけど、これは商用有料ライセンスのときのみ使えるもののようす?
nginx のキャッシュの仕組み
nginx のキャッシュの仕組みは、下記のようになっている。
- キャッシュするレスポンスをいったん一時ファイルに保存
- その後、指定したキャッシュ保存先にファイルを移動(rename)する
なので、「一時ファイルの保存先」と「キャッシュファイルの保存先」が違うデバイスだった場合、ファイルのコピーが必要となり、ディスク I/O が発生してしまう。これを回避するため、 proxy_temp_path
ディレクティブを使って一時ファイルの保存先も指定するようにする。
キャッシュをコントロールするレスポンスヘッダ
nginx のキャッシュ機能では、
X-Accel-Expires
Expires
Cache-Control
の優先順位でキャッシュの有効期限が評価される。
このうち、ブラウザのローカルキャッシュを左右するのは Expires
Cache-Control
のふたつ。またこれらは HTTP/1.1 で定義されていて広く使われている。
このふたつのヘッダを nginx で追加するには、 expires
ディレクティブを使う。 expires 60s;
としたら、expires ヘッダだけでなく Cache-Control: max-age=60
も追加される。仮にこの状態だと、nginx でキャッシュしているファイルの有効期限は60秒だし、ブラウザのローカルキャッシュの有効期限も60秒になる。
expires -1s;
などとしたら、Cache-Control は Cache-Control: no-cache
となる。この状態だと、ブラウザはキャッシュを用いず、毎回サーバに条件付きリクエストで問い合わせ・サーバはコンテンツの更新有無を判定した上でレスポンスを返す。となる。
つまり、 expires
Cache-Control
は、nginx のキャッシュとブラウザのローカルキャッシュの両方を制御することができるもの、になる。
一方で X-Accel-Expires
は nginx のキャッシュのみの制御になるので、「ブラウザのローカルキャッシュの有効期限」<「nginx のキャッシュ」にしたい場合は X-Accel-Expires
ヘッダを指定する必要がある。( expires
だけだと、どう頑張っても「ブラウザのローカルキャッシュの有効期限」=「nginx のキャッシュ」にしかできない。)
画像サムネイルの作成
こんなことも、nginx で行わせることができるのかー!(それだけ)
Lua による拡張
世間一般的には、この本にこの章が含まれていることが、その価値をよりいっそう高いものにしているようす。なんだけど、初心者な僕は「Lua」という単語すら初めて見た、というレベル。なので今回も、ひととおり読んだけれど「なるほど、設定ファイルの中にコードを書ける・拡張できる方法があるんだなー」くらいに、今回はとどめた。
でもこれほんと強力だなーと。ちょっとしたことなら、アプリケーションサーバにまで到達させずに、nginx だけで返したいレスポンスを返すことだってできちゃうよね。
などと感動していたら、OpenRestyェ...
OpenResty とは、 nginx と nginx_lua をはじめとするサードパーティモジュールをベースにした Web アプリケーションフレームワーク
。 OpenResty には memcached、Redis、MySQL といった各種サーバソフトウェアと通信するためのドライバや JSON パーサ、セッションの暗号化/復号を行うためのモジュールなど、アプリケーションを開発するうえでよく利用されるモジュールがバンドルされている
... とのことで、あぁ、未来に生きてんな、と。nginx をベースにしてるのだから、イベント駆動型の Web アプリケーションフレームワークでもあるわけか。しかもバックにいるのが nginx ということで心強いし。Test::Nginx というテストフレームワーク?もあるみたいだし。
でもどうなんだろうな、こういう流れ、今後大きなうねりとなっていくのかどうか。今々での使いドコロとしては、よほどの大規模なサービスにおいて、より素早くレスポンスできるようにするため・アプリケーションサーバをより "本業" に専念させるために、その手前にいる nginx が行える仕事の幅を広げよう、というポジションで使う、というのが現実的なのかなぁ。
感想
今年に入ってから、趣味アプリを自分で立てたサーバー(Compute Engine を使ってみている)で運用しはじめてみていて。いままで PaaS を使うことで考える機会のなかったあれやこれやを考える機会が増えてきていて、んで、これがまたそこそこおもしろくて。
nginx も、その「あれやこれや」に含まれていて、それまで "なんとなく" "手探りで" 触ってきた nginx について、一歩踏み込んだ理解をしてみようと思ったというのが、今回の購入に至ったワケ。
そういったこともあって、非常に面白かった。あっという間に読んでしまった。見よう見まねで書いた自分の conf と照らし合わせながらこの本を読むのもまた、非常によかったと思う。
あと、「あ、これは後で自分のにも設定しておきたいな」っていうことを、サーバーの chef リポジトリの issue にも随時書きながら読み進めたので、これからこの issue たちを潰しこむ作業を進めることにもワクワクしている。w