nginx実践ガイド:IT技術者のための現場ノウハウ
はじめに
nginx
webサーバソフトウェア
静的ファイルを配信するだけでなく、ロードバランサやリバースプロキシとしての使い方もされる。
第1章
nginxについて
2016年時点でアクセスの多いトップ100万のサイトのうち、3割程度のシェア率を誇る
この数字は2010年時点では5%だった。人気が上がっていることがわかる
Apacheは標準的なwebサーバとして特に理由がない限り採用されることが多いはず。
nginxはあえて選ばれているケースが多い。なぜ?
nginxの機能と特長
nginxの使い方
静的なコンテンツのwebサーバー (4章で)
動的なコンテンツのwebサーバー
webアプリケーションと呼ばれる種類のソフトが動作
アクセスを処理してデータを生成
接続方法はFastCGIやWSGI, TCPソケットなど色々 (5章で)
ロードバランサ , リバースプロキシ
クライアントからのwebアクセスを別のwebサーバに転送するサーバのことをリバースプロキシと呼ぶ。
また複数の転送先に負荷を分散するリバースプロキシを、特にロードバランサと呼ぶ
nginxは設定が容易で性能が高いため、この用途のためだけにnginxが選定されることがある
nginxにHTTPSの処理を担当させ、暗号化を解いた状態で別のwebサーバにアクセスを転送する使い道もある
(7章で)
nginxの特徴について
設定ファイルの構造がシンプルである
confにファイルを記述していくが、apacheと比較するとシンプル
基本機能がしっかり作られていて、安定動作する
動作に必要なメモリやCPUのリソースが少なく、効率良く動作する。
サービスを止めない再起動やバージョンアップの機能などが備えられているので、安定動作する
性能が高い
アクセス性能が優秀で、同時接続数が増えて高負荷となっても性能が落ちにくい
キャッシュ機能があり、ファイルI/O, ネットワークI/Oの節約ができる
新機能への対応が早い
HTTP/2, websockerのリバースプロキシなど、新機能への対応が早期に実現されている。
webシステムのアーキテクチャについて
一般的な流れについて
インターネットからのアクセス
ロードバランサが受け取り、それぞれwebサーバへ負荷分散(nginxの適用範囲)
webサーバが受け取る(nginxの適用範囲)
webアプリケーションがあれば、そちらが動作
DBや各ファイルを参照...
小規模な場合はロードバランサを使用してないケースなどもあるが、大規模になればロードバランサ等も複数のノードで管理することもある。
nginxの内部構造について
2種類のプロセスが起動している
code:psコマンド
nginx: master process
nginx: worker process
masterはrootユーザーで起動し、workerはnginxやwwwというような一般ユーザーで起動している
master
設定ファイルの読み込み
ネットワーク通信に使うソケットの待受を設定
workerを起動し、監視する
worker
ネットワーク処理のイベントループを処理
masterが待受を設定したソケットを使っての接続の受付
ネットワーク、ファイルI/Oの実施
HTTP, SSL/TLSのプロトコル処理
masterとworkerの関係
1つのmasterが複数のworkerを動作させることができる
ただし、単一workerの中でもI/Oを多重化させ、処理を全て待たずにできるところから進めていけるようになっている
nginxのモジュール構造
内部で機能ごとのモジュールに分かれている
コンパイル時のフラグによって組み込むかを指定できる
ただ配布しているパッケージはほぼ全てのモジュールが組み込み済みなので、特に意識しなくて良い
第2章
nginxパッケージの種類
stable
APIの変更を伴わないバグ修正が取り込まれる、安定バージョン
mainline
開発が活発に行われる最新バージョン
毎年4月, 1年おきにメジャーバージョンが上がる。
メジャーバージョンが変わる時に古いstable版のサポートが終了する
特別な理由がない限りはmainlineで問題ない
nginxを利用する上で必要なパッケージ色々
nginx以外に必要なソフトウェア
HTTPに対応させるために必要な、openssl
ファイアウォールの設定を行う、firewalld
サーバ起動の管理に、systemd
ログファイルの入れ替えなど(ローテート)に使う、logrorateなどなど
CentOS7などは、標準パッケージに大体入れてくれている。
その中で別途必要になった際にyumで入れるという感じ。
nginxのインストールの流れ
多様なOSで動く。今回はCentOS7で。
SELinuxが有効な状態でいると待ち受けポートの変更やアプリケーションサーバと接続する場合に起動しなくなるなどの不具合が起こる場合があるので、無効にしておくとよい。
リポジトリの追加
centOS7にリポジトリを追加
バージョンが古い場合があるので、最新のものが欲しいならやっておく
yumでインストール
yumは自動的に最新のものを選択し、インストールする
自動アップデート設定
毎日深夜に更新確認を行い、常に新しいバージョンが使われるようになる
新しいものが動作しない場合は古いバイナリのまま動作する仕組みになっている
yum-cronをインストールして設定する必要がある
第3章
nginxパッケージの全体構造
バージョン確認: rpm -qi nginx
パッケージ内のファイル確認: rpm -ql nginx
詳細を知りたい場合はP29を確認する。
/etc/nginxディレクトリについて
nginx.conf
最初に読まれる。他の設定ファイルはこのファイルから読まれる
mime.types
ファイル拡張子、Content-Typeのマッピングテーブル
conf.d, default.conf
ポート番号、ドキュメントルートの設定
基本的なwebサーバとしての設定が記述される
fastcgi_params, scgi_params, uwsgi_params
各種パラメータと、nginxの変数やテキストのマッピングテーブル
win-utfなど
各文字の変換テーブル。通常使わない
色々あるが、大元はnginx.conf。
デフォルト設定ではmyme.typesとconf.d/*.conf*のファイルだけしか読まれない。
初期からあるconf.dはdafault.confのみ。
ファイアウォールの設定
webサーバを起動しただけでは、クライアントからのリクエストを受け付けることはできない
なのでHTTPポートを開ける設定をファイアウォール側でしてやる。
今回は省略
nginxの各コマンド
CentOSに限らず、Linuxディストリビューションはsystemdを使ってサービスの設定をする。
start, stop, restartなど。
コマンドの挙動について補足
restart
nginxを再起動する。停止>起動の流れになるのでリクエスト受付できない時間が発生する。
reload
設定ファイルを再度読み込む。
新たなworkerプロセスを新たな設定で立ち上げる
古い設定のworkerプロセスでリクエスト処理中でないものを順次終了させる
この流れなので、リクエストを受け付けできない時間が発生しない。
停止について
一般に、処理中のリクエストを待ってから作業することgraceful ...と呼ぶ
停止ならgraceful shutdown, 再起動ならgraceful restart
逆に、処理が終わるのを待たずに終了することをfast shutdownなどと呼ぶ。
stopやrestartなどは処理中のリクエストを打ち切るのでfast shutdownに分類できる。
gracefulに行いたい場合は別のコマンドを稼働させる必要がある。
nginxデーモンの実行ファイルオプション
オプションを与えることで、実行中のデーモンの制御や設定ファイルのテストを行うことができる
オプション
-h: ヘルプ
-v: バージョン
-V
バージョン情報やコンパイラの情報を確認できる
組み込まれたモジュールの確認なども
-t: 設定ファイルをテストし、エラー内容を表示
-T
設定ファイルをテストし、設定ファイルの内容を表示
includeされたconfファイルなどは展開され、1つの設定ファイルとして表示される
オプションを付与した時の動作例
nginx -s stop
実行中のnginxデーモンの停止。fast shutdownで行われる
nginx -s quit
実行中のnginxデーモンの停止。graceful shutdownで行われる
nginx -s reopen
実行中のnginxデーモンにログファイルを開き直させる
nginx -s reload
実行中のnginxデーモンに設定ファイルを再度読み込ませる
デーモンの制御
Unixデーモンについて
ほとんどは、実行中のデーモンプロセスにシグナルを送信することで制御している
nginxも同様にシグナルを使って制御できる
nginxのプロセスへのシグナル送信
systemctlとかnginxコマンドは、実際はシグナルを送信している
killコマンドでシグナルを送ってデーモンを制御することもできる
masterプロセスとworkerプロセスで処理できるシグナルに違いがある。
masterプロセスで処理できるもの
TERM/INT: master,workerの終了。fast shutdown
QUIT: master,workerの終了。graceful shutdown
HUP
設定ファイルの再読み込み。
新たなworkerを新たな設定で起動させ、古いworkerをgracefulに終わらせる。
USR1: ログファイルの開き直し
USR2: 新たな実行ファイルで再起動する
WINCH: workerをgraceful shutdownする
workerプロセスで処理できるもの
TERM/INT: 自身の終了。fast shutdown.
QUIT: 自身の終了。gracefulで
USR1: ログファイルの開き直し
WINCH: 異常終了。デバッグ用の機能
シグナルとコマンドの関係
systemctl stop nginx
masterにTERM, QUITのシグナルを送っている
nginx -s open
masterにUSR1シグナルを送っている
シグナルが直感的ではないので、コマンド化しているだけという感じ。
code:terminal
ps axw | grep nginx
19512 ? Ss 0:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
19513 ? S 0:00 nginx: worker process
19514 ? S 0:01 nginx: worker process
# killコマンドでシグナルを送ると、消せる
kill -QUIT 19512 # nginx masterプロセスに対してQUITシグナルを投げる
nginxのログファイルについて
webブラウザからのアクセスを記録するアクセスログと、エラーログ
デフォルトはaccess.logとerror.log
ローテートについて
logrotateプログラムによって古いログは圧縮されていく。
古いファイルはgzipで保存され、一般に52日分ほど貯まるとローテートされていく。
挙動の管理は/etc/logrorate.d/nginxという設定ファイルで調整ができる。
第4章 Webサーバの構築
HTTPの基礎知識
HTTP
webで使われるプロトコル
TCP/IP上で動作し、HTMLや画像を取得できるプロトコルとして広まった
バージョンは0.9, 1.0, 1.1, 2がある
SSL/TLSといった暗号化技術と組み合わせてセキュリティを高めたHTTPをHTTPSと呼ぶ。
デフォルトのポート番号はHTTPは80,HTTPSは 443
古くのHTTPについて
HTTP/1.0までは、1回の接続で1つのリクエストを送信、1つのレスポンスを受信して接続を閉じていた
リクエストと別のリクエストとの間には関連性がない
連続するリクエストの処理であっても、状態を持たずに1つ1つのリクエストを独立して処理できる仕組みだった
いわゆるステートレスな通信だった。
色んなユーザーからリクエストがあっても、1つ1つ独立して返す感じか
HTTPリクエスト
URLやURIはHTTPのリクエストに使う。一般に以下のプロトコルを示す文字列のことを指す。
http://user:password@www.example.com:8080/path/to/file?a=1&b=2#1
table:URI/URLの各役割
名前 例 機能
スキーム http プロトコルの指定を行う。http, https, ftpなど
ユーザー名 user 認証に使うユーザー名の指定。次のパスワードとまとめて省略可能
パスワード :password 認証に使うパスワードを指定する。省略できる
host名 www.example.com サーバのホスト名やIPを指定する。IPv6の場合は[]で括る。
パス /path/to/file サーバ内のパス指定。省略する場合ドキュメントルートが使われる
クエリ文字列 ?a=1&b=2 HTMLフォームなどに入力された内容、省略できる
フラグメント #1 ドキュメント内の位置の指定。省略すると先頭が使われる HTTPはURLが複数の行に分解されて読み解かれる
テキストデータでやり取りされる、テキストプロトコルとなっている
code:terminal
# curlで覗ける
HTTP/2 200
content-type: text/html; charset=ISO-8859-1
...
code:httpテキストプロトコル
GET /path/to/file?a=1&b=2 HTTP/1.1
Host: www.example.com
User-Agent: ブラウザ名
Cookie: c=3
...
各行について
1行目
リクエスト行。メソッド名やパス, プロトコルバージョン(HTTP/1.1とか)が記述される。
2-4行目
HTTPのリクエストヘッダ
クライアントの情報やURLのホストなどの情報が入る。
コロン:で各要素が区切られている 。:path:とか:method:とかかな
Host:ヘッダについて
必須のヘッダ情報。
URLのホスト名とポート名を表現しているので、サーバはこちらと1行目(リクエスト行)を見て、どのURLにアクセスされたのかを判断する。
Host:ヘッダの中身によって応答するコンテンツを変えることで、1つのwebサーバが複数のホスト名のwebサーバを兼ねることができるようになっている
それぞれのホスト名のwebサーバのことをバーチャルホストと呼ぶ。
nginxやapacheにこれをよく定義する理由は、
code:txt
AAA.comというヘッダならサーバ内のAというファイルを表示
BBB.comというヘッダならサーバ内のBというファイルを表示
というように、アクセスされたドメイン名に応じてページを出し分けることができるので。
空行以降
リクエストボディ
GETの場合は存在しない。POSTやPUTだとアップロードされるデータが入る。
HTTPレスポンス
code:http
HTTP/1.1 200 OK
Server: nginx
Date: Thu, 15 Dec 2016 15:00:00 GMT
Content-Type: text/html; charset=UTF-8
COntent-Length: 335
Last-Modified: Wed, 07 Oct 2015 12:55:32 GMT
<!DOCTYPE html>
<html>
...
1行目: ステータス行
プロトコルバージョン、ステータスコード、原因(今回はOK)が表示される
2行目~空行: レスポンスヘッダ
リクエストヘッダと同じ記述方法で、:で同様に区切られている
空行以降: レスポンスボディのデータ
Keep-Aliveとパイプライン
HTTP/1.0まで
1つのリクエストに1つのレスポンスを返すと、毎回tcpの接続を閉じていた
HTTP/1.1以降
1回の接続で複数のリクエストとレスポンスを処理することができるようになった
Keep-Aliveと呼ばれる仕組み
Connection:というヘッダをレスポンス/リクエストの際に付与する
複数接続があるため、切れ目を指定する必要がある
チャンクを使うか、Content-Length:ヘッダで指定する必要がある
後続のリクエストをレスポンスを受信する前に送信することもできる
パイプラインという仕組みで、性能向上に役立てられている
パイプラインを使った場合でもwebサーバのリクエスト処理順序は決まっていて、必ずリクエストを送信した順番にレスポンスを返す。
チャンク分割
HTTP/1.1などで使われる、データを小さく分割して送受信するやり方のこと
Transfer-Encoding:ヘッダにchunkedと指定することで実装可能
データ送信の効率化や、ストリーミング用途でも使われる
WebSocket
HTTP/1.1などの、双方向通信の規格
最初にUpgrade:ヘッダでネゴシエーションしたのち、自由に双方向で通信できるようになる
nginxはこちらに対応させることができるが、設定の調整が必要。
HTTP/2
2015年に仕様化された
SPDYと呼ばれていたGoogleの独自拡張がベース
ヘッダ圧縮、バイナリプロトコル、多重化と優先度制御、サーバーからのプッシュなどの高速化・効率化技術が組み込まれたプロトコル
規格上はHTTPでも使えるが、ブラウザやプロキシサーバーではHTTPSのみの2.0対応とさせていることが多い
WebSocketの規格が削除されていて、使えなくなった
プロキシサーバーのHTTP
会社組織などでは、プロキシサーバーを通してwebに繋ぐケースが多い
HTTP/1.1の規格ではプロキシサーバーに関しても規定されている
プロキシサーバへのリクエストも、HTTPリクエストと似たものになっている
こちら経由でHTTPSのwebサイトへ繋ぐ場合は、プロキシサーバーから通信の中身は知ることができない
nginxの話にようやく戻ってきた
nginx設定ファイルの構造について
/etc/nginx/nginx.conf
ディレクティブを書いて定義していく
;で終わったり、{, }で定義できる設定項目のこと
カッコで括ったブロックはコンテキストとも呼ぶ。
どのブロックにも含まれない、外側のコンテキストをmainコンテキスト
ディレクティブによって作成されたコンテキストは、コンテキストを作成したディレクトリの名前をつけて{ディレクティブ名}コンテキストと呼ぶ。
code:nginx.conf
# シャープでコメント
# この辺にnginx本体の設定(mainコンテキスト)を書く
events {
# イベント待ち関連のパラメーターを記述する
}
http {
# webサーバー全体の設定
server {
# バーチャルホスト1の設定
location xxx {
# URLのパス名ごとの設定
}
location AAA {
...
}
}
server {
# バーチャルホスト2の設定
location xxx {
...
}
}
}
ディレクティブにはそれぞれ記述できるコンテキストが決まっている
httpディレクティブはmainコンテキストの中にしか、
serverディレクティブはhttpコンテキストの中にしか書けない
同様にlocationディレクティブはserver, locationコンテキストの中にしか書けない
複数の種類のコンテキストに記述できるものもある
access_logディレクティブとか
記述するコンテキストによって、別の意味に変わるディレクティブもある
server
httpコンテキストの中なら、バーチャルホストの設定をするコンテキストを作るディレクティブ
upstreamコンテキストの中なら、ロードバランサの振り分け先を記述するディレクティブになる
そのほか色々あるが、nginx公式のドキュメントを見よう
Syntax
設定ファイルの書式。
コンテキストを生成するディレクティブなら{などが付き、そうでなければ;で終わる
Default
デフォルト値があれば記述される。無ければ-と記載。
Context
ディレクティブを記述できるコンテキストの一覧。
anyならどこでも書ける。
そのほか、注意点の記述など。
includeディレクティブ
別の設定ファイルを読み込むことができる
conf.dディレクトリにserverディレクティブとその中身を記述したファイルを置いておく
httpコンテキストでincludeすると、設定がひとまとめにする手法を使うと管理がしやすい
引数は絶対パスもしくは、/etc/nginxからの相対パスで指定する
Unixのglobパターンを使って指定することもできる
include conf.d/*.conf # .confの全てのファイルを読む
nginxの起動時など、設定ファイルを読み込む時に適用される
動作中に書き換えただけでは新しい設定が有効になることはない
mainコンテキストの基本設定について
code:nginx.conf(デフォルト)
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
ユーザー名
nginxのworkerが動作するユーザー名。通常は変更の必要なし
アクセスするファイルのパーミッションを、このユーザーからのアクセスができるようにしておく必要がある
masterはrootで動作する
workerプロセス数
I/Oの多重化処理により同時に複数のリクエストを処理できる
ただし、シングルスレッドで動作するのでCPUはプロセスにつき1つしか使われない
性能を出したい場合はCPUのコアと同程度に指定しておくと良い
worker_processes auto;としておくと、コア数と同等のworkerを起動する。
エラーログの設定
ログの出力先とログレベルを記述する
ログレベルは8段階くらいある
pid(プロセスID)
プロセスIDを記述したファイル小野配置先
シグナルを送る時に使う
そのほか
worker_rlimit_nofile
同時に開けるファイル、接続最大数を指定する
worker_cpu_affinity
使用CPUを制限する
load_moduke
動的なモジュールを読み込む。10章で
eventsコンテキスト
実行環境ごとに効率の良いイベントループを使って動作させる
code:nginx.conf
events {
worker_connections 1024;
}
worker_connections
1つのworkerプロセスが同時に受け付けられる接続数
クライアントの数だけでなく、全ての接続
リバースプロキシとして動作させる場合は、アクセスがあるとバックエンドのサーバとも接続が起こる
なので、1回のアクセスで2つの接続を使用する
クライアント - nginx(リバースプロキシ) - webサーバみたいな感じ
そのほか
use
接続を処理する方法を指定する
記述がない場合は、自動的に効率の最も良いものが選択される動作となる
worker_aio_requests
非同期ファイルI/Oの同時発行リクエスト数を指定する
ファイルI/Oの非同期かをする場合に使う。
使用するストレージやworkerの数にもよるが、デフォルトの32から変更することで性能差異が生まれる
httpコンテキスト
webサーバ全体の設定を記述する
この中で書けるディレクティブは、serverコンテキストやlocationコンテキストにも書けることが多い
ここにはさまざまなバーチャルホストに共通する設定を書く
複数のバーチャルホストを構築するときでも共有できるようにしておく
serverコンテキストで上書きすれば、バーチャルホストごとに設定の変更もできる
code:nginx.conf
...
http {
include /etc/nginx.mime.types;
default_type application.octer-stream;
log_format main '$remote_addr - $remote_user $time_local "$request" ' '$status $body_bytes_sent "$http_referer" '
'$"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
}
mime.types
拡張子とContent-Typeの対応付けをするディレクティブが書かれたmime.typesを読み込む
定義されていない場合はocter-streamという任意のバイナルファイルを示すContent-Typeに設定する
この記述のおかげでHTTPのContent-Typeヘッダが正しく設定されるようになる
log_format
Apacheと似たログのフォーマットをmainという名前で定義している
ターミナルのzshを弄る感じ。理解があれば違った形のログフォーマットを自分で書ける
access_log
アクセスログの出力先ファイル名
また、今回は出力形式として直前のlog_formatで定義したmainを指定している
mainの指定を行わない場合は、こちらもApacheとよく似たcombinedという形式が使われる
sendfile
ファイルの送信方式を表すディレクティブ。性能向上に貢献する
keepalive_timeout
Keep-Aliveのサーバー側タイムアウトを今回は65秒で設定
サーバ側のタイムアウト時間の他、第2引数でクライアント側のタイムアウト時間も定義できる
第2引数を定義した場合、設定時間が経過するとクライアント側から接続が切断される
include
http内で書かれる場合は、serverコンテキストが書かれたファイルであることが多い
バーチャルホストの設定が記述される。
ポート番号
インターフェイス
ホスト名など
設定ファイルで使える変数について
条件判定やlog_formatのログ文字列で参照できる内部変数を持っている
ログフォーマット用は%で始まる書式だが、それ以外に通常のものでも可能
変数の一部
もっと知りたくなったらP53とかを見る。
$request_method
HTTPメソッドを取得できる。getならGETが格納されている
$args, $query_string
クエリストリングの取得。a=1&b=2など。
さらに上の例ならば、$arg_aと書けば1, $arg_bと書けば2を取れる
$host, $http_host
ドメインを取得。www.example.comとか
$uri, $document_uri
アクセスされたパスの取得。xxx.com/docs/index.htmlなら/docs/index.html
他にもcookieを取得する$cookie_名前)とかブラウザ情報を取得する$http_user_agentとかめっちゃいっぱいある
ディレクティブの引数に変数を使う
アクセス元ごとにログファイルの出力先を変えたいなら、下記
access_log /var/log/nginx/access/$remote_addr.log
リモートIPアドレスを取得する変数
変数を使えないディレクティブも存在する。
error_log /var/log/nginx/error/$remote_addr.log
エラーログは引数には変数が使えないので、$remote_addr.logというそのままの名前で出力される
変数を定義する
setディレクティブで定義できる。
set $jibun_no_hensu 1000;
httpコンテキストやmainコンテキストでは使えないので注意
静的なウェブサイトの構築
ここでは状況やユーザーの入力によって内容が変わらず、URLにアクセスするとナジデータが取得できるサイトを指す
静的サイトの構築は性能が出やすい。
serverコンテキストの設定
code:xxx.com.conf
server {
listen 80;
server_name static.example.com;
access_log /var/log/nginx/static-access.log;
error_log /var/log/nginx/static-error.log;
location / {
root /www/dir;
index index.html index.htm;
}
}
listenディレクティブ
待ち受けるポート番号を示す
80番はhttpのデフォルトポート番号だが、listenの記述を省略した場合も80番ポートが使われる
ただしmasterをroot以外のユーザーで起動した場合は8000番がデフォルトになる。
listenというのは、通信プログラムで接続待ちをするunixシステムコールの名称のこと。
サーバーに複数のIPを割り当てており、設定したいバーチャルホストが特定のIPでのみリクエストを受け付けたい場合はlisten 192.0.2.1:80;というように記述する
IPv6の場合はlisten [2001:db8::1]:80;というようにカッコで括る
同一ホスト内でのプロセス間通信に使われるUnixドメインソケットを使う場合は、listen unix:/var/run/nginx.sock;と記述する
複数のserverディレクティブがある場合も、同じポートを指定したlistenを書くことができる
その場合、server_nameディレクティブによるホスト名の指定を使ってバーチャルホストを識別する
1つのserverディレクティブの中に複数のlistenディレクティブを書くこともできる
その場合、同じバーチャルホストが複数ポートでリクエストを受け付けるようになる
server_nameディレクティブ
バーチャルホストのサーバーのホスト名を指定する
並べて記述することで複数の指定ができる
ワイルドカードによる指定も対応。
server_name *.example.com;とか
server_name .example.com;と書くと以下の両方を指定した場合と同じ挙動となる
*.example.com
example.com
ホスト名がどのserver_nameにもマッチしなかった場合
listenにデフォルトバーチャルホストを設定しておくと、そちらが使われる
listen 80 default_server;
優先順位について
複数指定して複数マッチした場合は、どのバーチャルホストが使われるか
1. 完全一致
全く同じものがあった場合は、後に書かれた記述は無視される
2. *で始まるワイルドカードで最長のもの
3. *で終わるワイルドカードで最長のもの
4. 設定ファイル中で最初にマッチする正規表現
つまり正規表現やワイルドカードで定義したものより、ホスト名全体を書いたものが優先度が高い。
locationディレクティブ
引数に指定されたパス名に対応するコンテキストを作る({とかのこと)
複数定義されている場合、優先順位はこちらも完全一致が優先される流れになる
優先順位がつけられないlocationが存在するとシンタックスエラーとなる
ネストすることができるが、パス名は先頭から比較される
code:.conf
location /about/ {
# /about/ にアクセスした場合の処理...
location /etc/ {
# /etc/ が参照される.実質走らない
}
location /about/etc/ {
# /about/etc/ が参照される.
}
}
ネストは優先順位の制御に使うと便利。
rootディレクティブ
ドキュメントルートを設定する
ただしrootディレクティブ自体、http, server, locationコンテキストにかける
特定の条件に一致する場合にのみ使われるドキュメントルートの設定
indexディレクティブ
ディレクトリにアクセスした時にレスポンスに使用されるファイル名
indexに複数指定をすると、前から順番にファイルの存在チェックを行い, 見つかったファイルを使用する
そのほか
aliasディレクティブ
rootと役割が似ている
code:.conf
location /files/ {
root /data/;
}
この場合、/files/a.htmlというファイルがあった場合
/data/files/a.htmlが該当する
code:.conf
location /files/ {
alias /data/;
}
この場合、files/a.htmlというファイルがあった場合
/data/a.htmlが該当する
filesとdataがエイリアスだということを示す。
アクセス制限
nginxでアクセス制限をかけたwebサイトを構築することができる
IPアドレスによる制限
allow, denyディレクティブを使う
特定範囲のIPアドレスからのアクセスを許容、拒否することができる
code:.conf
location /private {
deny 192.168.1.1;
allow 192.168.1.0/24;
deny all;
}
こちらの意味合いについて
192.168.1.1からのアクセスは拒否
192.168.1.1以外の192.168.1.0/24の範囲からのアクセスを許可
それ以外を拒否
→192.168.1.2 ~ 192.168.1.255までを許可したと言うこと
パスワード認証(Basic認証)によるアクセス制限
パスワードを平文で送信するので、プロキシサーバーや途中経路のネットワークでパスワードを盗まれるリスクがある
SSL(HTTPS)と組み合わせて使うことを推奨.
code:.conf
location /private {
auth_basic "password";
auth_basic_user_file /etc/nginx/htpasswd;
}
auth_basicディレクティブ
realm(認証領域)として使われる文字列
Windowsとかで認証をするときに出したい文字を指定できる
引数にoffを指定するとBasic認証が無効になる
これは、全体にかけておいて特定のlocationだけ掛けないようにする時などに使う。
auth_bacic_user_file
パスワードファイルを指定する
Apacheと同じものが使える
sudo yum install httpd-tools
IPとBasic認証両方書いた場合
デフォルトでは両方で許可されたアクセスのみが許可されると言う動作になる
satisfy any;と言うディレクティブを記載しておくと、どちらかの条件を満たすアクセスを許可するという動作に変わる
第5章の前に
実際に触って確認したいので、コンテナなどで用意できないか試す
リポジトリ作った
コンテナ操作
この辺参考
コンテナ起動する
docker run --name 4_chapter_nginx -d -p 8100:80 nginx
コンテナ削除
docker rm -f 4_chapter_nginxもしくは、psで見れるIDを指定する
停止と再開(今回のコンテナIDも記載)
docker stop f9882, docker start f9882
バインドする
一旦上で作ったやつ停止させてから、
バインドする対象のディレクトリを用意する
/Users/skoni/Desktop/study/nginx_guide/4_static_server
この中にindex.htmlを作っておく
runする際にバインド先を指定する
/usr/share/nginx/htmlは変えない(変えたければドキュメントルートを変える)
code:terminal
docker run --name 4_chapter_nginx -v /Users/skoni/Desktop/study/nginx_guide/4_static_server:/usr/share/nginx/html:ro -d -p 8100:80 nginx
コンテナの中に入る
docker exec -it 4_chapter_nginx bash
とりあえずvim入れる
code:txt
# nginxコンテナはdebian系なので、apt(apt-get)で入れる。
apt update
apt install -y vim
色々設定を試そう
basic認証
.htpasswdを作るためにパッケージ導入
apt-get install apache2-utils
パスワード作成
code:txt
root@f9882746fdc4:/etc/nginx# htpasswd -c /etc/nginx/.htpasswd skoni
New password:test1234
Re-type new password:test1234
Adding password for user skoni
code:/etc/nginx/.htpasswd
# ID : bcryptで暗号化されたパスワード という形で格納される。
skoni:$apr1$sTrkSw3f$hud/TmmKhqRSSTXGnuNb./
nginx記述
code:conf.d/default.conf
location / {
auth_basic "password";
auth_basic_user_file /etc/nginx/.htpasswd;
root /usr/share/nginx/html;
index index.html index.htm;
}
再起動 / リロード. nginx -s reload
skoni:test1234で定義できた。
https://scrapbox.io/files/657070ea0c564c0024c45757.png
locationを書くとページが動作しなくなる現象について
ログの設定テスト
デフォルトのログについて
code:txt
root@f9882746fdc4:/var/log/nginx# ls -la
total 16
drwxr-xr-x 1 root root 4096 Dec 6 13:27 .
drwxr-xr-x 1 root root 4096 Dec 5 13:26 ..
lrwxrwxrwx 1 root root 11 Nov 21 09:05 access.log -> /dev/stdout
lrwxrwxrwx 1 root root 11 Nov 21 09:05 error.log -> /dev/stderr
これはdocker特有の設計。docker側のログに出るように標準出力に飛ばしている
code:default.conf
location = /redirect/ {
access_log /var/log/nginx/redirect_access.log;
}
こんな感じで描くとログは出るようになるが、index.htmlが404になる挙動になった
location = /redirect { ...と書くと解消はされるが、ログが出ない
ログ
code:docker側
172.17.0.1 - skoni 07/Dec/2023:13:51:55 +0000 "GET /redirect/ HTTP/1.1" 200 60 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36" "-" 2023/12/07 13:51:12 error 460#460: *109 "/etc/nginx/html/redirect/index.html" is not found (2: No such file or directory), client: 172.17.0.1, server: localhost, request: "GET /redirect/ HTTP/1.1", host: "localhost:8100" パスが違う?
/etc/nginx/html/redirect/index.htmlなんてものは存在しない。
locationがヒットした瞬間に今まで見ていた場所と別の場所を見ている
後で調べる。
第5章 webアプリケーションの構築
webアプリケーションとwebサーバーの接続
webアプリ
ブラウザなどのクライアントからのリクエストをwebサーバを通じて受信
処理した結果をクライアントに応答させて動作させる
接続について
webサーバ(拡張モジュール付与) > CGI, FastCGIなど > webアプリ
CGI(Common Gateway Interface)
最も古くからある接続方法
アクセスされるたびにプロセスを起動し、パラメーターを標準入出力でやり取りする
プロセス起動が手間なので、現在はあまり使われない
FastCGI
CGIの処理速度の遅さを克服するために作られた接続方法
webアプリのプロセスがデーモンとして常駐
パラメーターはソケットを通じてバイナリプロトコルでやり取りされる。
性能が出しやすいので、現在も使われる
SCGI(Simple...)
同じくwebアプリのプロセスがデーモンとして常駐
FastCGIよりも単純
webサーバの拡張モジュール
wenサーバの内部で、webアプリが使うプログラミング言語の処理を動作させる接続方法
性能が出るが、webアプリがwebサーバーの権限で動作する必要がある
webサーバによって対応に差がある
apacheには多くのモジュールが用意されているが、nginxはまだ少ない
WSGI(Web Server Gateway Interface)
Pythonとやり取りするプログラミングインターフェイス
Pythonのwebアプリケーションにとっては非常に便利なのでよく採用される
RubyやPerlにも似た仕組みのものが多い
TCPソケットのHTTPサーバー
webサーバーのバックエンドとして、webアプリを含んだwebサーバを動作
前段のwebサーバーでリバースプロキシを設定する
最近は色々な言語で使われる
UnixドメインソケットのHTTPサーバー
webサーバーのバックエンドとして、TCPソケットの代わりにUnixドメインソケットを使用する
webアプリとwebサーバを同一ホストで動作させ、前段のwebサーバ側でリバースプロキシ設定をする
TCPよりも性能が出やすい。
Webアプリケーション設定に使われる設定
メソッドごとのアクセス制限
code:conf
location / {
limit_except GET POST {
deny all;
}
}
GETとHEAD, POSTは許可。それ以外は拒否という記述。
URLの書き換え
実例がないのでちょっと理解しづらいので、掻い摘んで書く。
returnを使ったリダイレクト
移転でURLが変わった場合など
return 301 www.example.com;
レスポンスコードの指定と、リダイレクト先のURLを指定する
301,302,303,307以外のレスポンスコードの場合は、レスポンスボディのテキストになる
rewriteディレクティブにより書き換え
クライアントにもセルURLはそのままで、別のURLにアクセスしたかのような内容を返す
PHPでwordpressが動く時の挙動を見る
apacheにはphpの拡張モジュールがあるが、nginxにはないのでFastCGI経由で行う。
FastCGIのインターフェイスを使ってPHPのプログラムを起動させる
PHP-FPM(FastCGI Process Manager)を使う。
php-fpmのデーモンを設定して起動し、nginxにはFastCGIで接続するよう設定を行う
リクエスト > nginx(HTTPサーバ), FastCGIクライアント > FastCGIプロトコル > PHP-FPMの載ったwebアプリ
PHP-FPMについて
PHPの開発元が配布しているデーモン
構造
プールと呼ばれる、webサーバでいうバーチャルホストのような塊を複数作れる
プールごとに同時接続数やプロセスの数を指定できる
使用するPHPアプリごとにPHP-FPMのプールを作り、使用する資源を分ける
nginxと同様にmaster / workerプロセスに分かれる。
masterがworkerの起動や監視を行い、workerがリクエスト処理を行う。
設定ファイル
/etc/php-fpm.iniと、.etc/php-fpm.d/*.conf
*.confはプールごとの動作設定
デフォルトはwww.conf。
wwwプールの設定
code:www.conf
# TCPソケットの代わりに、Unixドメインソケットを利用するように設定
listen = /var/run/php-fpm.sock
# user, groupをnginxにすることでPHPプログラムとnginxの違いによる権限の不具合を予防する
user = nginx
group = nginx
wordpressを実際にnginxコンテナに入れてみる
code:terminal
# 稼働
docker run --name 5_webapp -v /Users/skoni/Desktop/study/nginx_guide/5_webapp:/usr/share/nginx/html:ro -d -p 8101:80 nginx
# 止めたり消したり
docker stop 1bbea6d
docker rm -f 1bbea6d
# 入る
docker exec -it 5_webapp bash
php-fpmの導入
code:txt
# phpなど導入
apt install php-fpm php-mysql
# 多分www.confの場所が変わってここになった
vim /etc/php/8.2/fpm/pool.d/www.conf
code:www.conf
# unixソケットに指定
listen = /var/run/php/php8.2-fpm.sock
# ユーザーとグループの変更
wordpressを落とす
code:txt
apt install sudo
apt install wget
apt install tar
tar xfz latest-ja.tar.gz
chown -R nginx wordpress
nginxの設定を調整する
記載の通りにやる。
これだけだと動かない
nginxコンテナではなく、centosとかのコンテナでnginxを入れてやったほうがいい気がしてきた。
データをボリュームとして残す
nginxの導入後のドキュメントルートの指定なども必要になる
第6章 HTTPSへの対応
SSL/TLS Secure Socker Layer / Transport Layer Security
HTTPS接続の際に使用するプロトコル
SSL/TLSによる接続の上で、HTTPプロトコルで通信を行うこと。
webだけでなくメール等でも使われる
証明証を使った認証、公開鍵/共通鍵暗号などと組み合わせて使う。
公開鍵暗号
秘密鍵、公開鍵のペアを使った暗号化手法
通信相手の公開鍵を使って暗号化したメッセージを送信
受信したメッセージは自身の秘密鍵で複合することで、秘密を守る
一般的には鍵のビット長が長い方が強度が高く、RSA暗号よりも楕円曲線暗号の方が強度が高い。
SSLについて
TLSの下になったプロトコルで、v1.0~3.0までが存在する。
いずれも脆弱性があり非推奨なため無効化が進められている。
古い携帯電話やIEをアップグレードしていないとSSL3.0などが残っているためHTTPS非対応の場合がある。
TLSについて
SSLの後継プロトコルで、v1.0 ~ v1.3(2022年現在)が存在する。
主にこちらが通信時に使われるが、慣例的にTLSのこともまとめてSSLと呼ぶ。
そのため現行ライブラリの名前もOpenSSLとかそういう呼び名がついている
認証・暗号化のプロセス
安全な通信を開始する前にクライアントとサーバーで証明書・公開鍵のやりとりを行う
この通信をハンドシェイクと呼ぶ
HTTPヘッダのやり取りをする前にハンドシェイクを行い、HTTPヘッダも含めて暗号化されるようになっている
TCPハンドシェイク
C: SYN > S: SYN ACK > C: ACK ※C: Client S: Server
SYN, SYN ACK, ACKと3つのメッセージを交換する(スリーウェイハンドシェイク)。
その後SSL/TLSのハンドシェイクが始まる
SSL/TLSハンドシェイク
C: Client Hello > S: Server Hello(サーバー証明書) > C: 鍵交換 > S: 鍵計算 > CとSでデータ通信
クライアントからのClient Hello
SSL/TLSプロトコルのバージョン情報、対応する暗号化アルゴリズムのリストを含めたメッセージを送る。
サーバーからのServer Hello
SSL/TLSプロトコルのバージョン情報、対応する暗号化アルゴリズムのリスト、またサーバ証明書から始まる証明書チェインの情報を含めたメッセージを送る。
クライアントによる証明書の検証
証明書チェインの検証を行う。
アクセス先サーバーのドメイン、証明書のドメインの比較
クライアントがあらかじめ持っている証明書ストアにあるルートCA証明書から正しくチェインが辿れるかどうか
有効期限が切れていないか、失効していないかなど
クライアントからのプリマスタシークレットの送信
共通鍵の元となる乱数データのこと。
このデータと暗号化アルゴリズムの組み合わせで、暗号化に使う共通鍵を計算できる
プリマスタシークレットはサーバー証明書に含まれる公開鍵で暗号化されて送付される
クライアント/サーバー双方で鍵を生成する
プリマスタシークレット、暗号化アルゴリズムを元として共通鍵を作成する
鍵交換の部分に公開鍵番号を使い、ハンドシェイクが終わったら共通鍵による暗号化通信に切り替わる。
共通鍵よりも公開鍵の方が計算量が多く時間がかかるので、大きなデータの場合はこちらの方が楽なため。
中間者攻撃と証明書
プロキシ(代理という意味)を通してHTTPSページにつなぐ場合、プロキシサーバーにCONNECTというメソッドを使う
与えられたホストとポートに対して通信をするメソッド
ポートに接続後、HTTPSのハンドシェイクをして通信を始める
そのためプロキシサーバーはアクセス先のホスト名とIPアドレス、ポート番号は把握できるがデータの中身は知ることができない。
プロキシサーバーがハンドシェイクに介入し、通信の中身を見たり改竄することを中間者攻撃Man in the Middle Attackと呼ぶ。
もう少し詳細に
クライアントからClient Helloを受け取ったプロキシサーバーは、Server Helloを送らないといけない
Server Helloにはサーバー証明書が含まれる
サーバー証明書は暗号化前のハンドシェイク中のやりとりに含まれていて、これは頻繁に平文で通信される。そのため証明書データの中身をプロキシサーバーが知ることは容易である
それは良くないので、サーバー証明書は公開鍵に付加情報をつけてCAが署名し、プロキシサーバーが知ることのできない秘密鍵とペアになっている
証明書(公開鍵)を使って暗号化したデータは秘密鍵がないと復号できないので、中間者攻撃で本物の公開鍵を使ってもプリマスタシークレットを知ることができなくなる
プロキシサーバーが勝手に秘密鍵と証明書を作って、Server Helloを返した場合
クライアント側がサーバーから送られた証明書を検証することで、正規のCAが署名したものではないと警告が出る
以下の通信の形になっているが、プロキシサーバーが読めない(中間者攻撃を防ぐ)ような工夫がされている
ブラウザ ->HTTPS通信-> プロキシサーバー ->HTTPS通信-> webサーバー
サーバー証明書の取得
CA認証局: Certification Authorityが発行する。3種類ある。
DVDomain Validation証明書
ドメインが所有されていることを示す証明書
対象ドメインを所有していないユーザーが、証明書を購入できないような確認をした上で発行される
この辺だろうか
https://scrapbox.io/files/658ab6599593c80024f7240c.png
OVOrganization Validation証明書
ドメイン所有に加えて、発行先の組織が存在することを確認した上での発行
登記簿や電話で確認されるので個人では取得できない。
DV証明書と見分けがつきにくいが、証明書の詳細情報を参照すると法人名が確認できるのでそこで見分けがつく。
この辺だろうか?
https://scrapbox.io/files/658ab617683b850024675349.png
EVExtended Validation証明書
ドメイン、組織の確認を厳格に行って発行される証明書
ブラウザのアドレスバーの色が変わり、ユーザー目線でもわかるようになっている
証明書発行の手順
秘密鍵の作成
CSR(Certificate Signing Request)の作成
CAにCSRを提出
審査を行い、webサーバーの設定に必要となる証明書を発行
webサーバーに証明書を設置
秘密鍵とCSR作成の手順
必要になったときに読む。
Let's Encryptについて
無料でサーバー証明書を発行するCA
ACMEというプロトコルを使って証明書を発行する
クライアントのスクリプトも無料公開されていて、実行することで3ヶ月有効なDV証明書を取得できる
これがcertbot。nginx用のプラグインは開発中。
HTTPSのセキュリティ設定
SSL/TLSへの攻撃手法
POODLE
SSL3.0への攻撃。この影響でTLSへ移行した。なおTLS1.0及び1.1にも影響があった
Heartbleed
OpenSSLの実装問題を突いた攻撃で、アップデートや秘密鍵、証明書の更新が必要になった
Mozillaのガイドライン(もじらと読む)
HTTPS自体も適切に設定しなければセキュリティを保つのが難しい
webサーバーチェックツールなどがある
セキュリティ設定の注意点
互換性があると、古いクライアントが接続できなくなる
そのため設定を行う際は、古いクライアントから接続するユーザーがいるかを考慮する。
第7章 リバースプロキシ
リバースプロキシについて
一般的なwebサイトは3つの階層を成している
インターネット、webサーバー(リバースプロキシ)、アプリケーションサーバー
プロキシ
代理人という意味
クライアントの代わりに、サーバーにアクセスするサーバのことをプロキシサーバーと呼ぶ
フォワードプロキシ
クライアントの近くに用意して、外部ネットワークにアクセスするプロキシサーバー
内部のLANからインターネットにアクセスするために用意することが多い
リバースプロキシ
webサーバーの近くに用意して、外と中のネットワークの中継をするプロキシサーバー
入り口として負荷分散、キャッシュ、暗号化(https)を目的として使う
nginxはこちらの機能がある
ロードバランサ
負荷がかかった時の対処の2通りあるが、スケールアウトの際に適した機能。
webサイトへのアクセスを受け付けた後、複数のバックエンドのアプリケーションサーバーにアクセスを転送する
スケールアップ
CPUなどのスペックを高めること
データベースサーバなどはこちらが採用される
スケールアウト
サーバーの台数を増やして複数のサーバーに負荷分散をすること
アプリケーションサーバならこちらが採用されることが多い
リバースプロキシの便利な使い方
負荷分散
可用性の向上
バックエンドの1台がダウンした時、リバースプロキシが検出
その他のサーバーに処理を割り振ることで可用性を上げる
全て落ちた場合でもリバースプロキシの内部で見栄えの良いエラーページを用意するなども可能
メンテナンス補助
リバースプロキシだけでメンテナンスページを出しておき、その間にバックエンドのメンテナンスをする
キャッシュによる性能向上
リバースプロキシでコンテンツをキャッシュする
HTTPSへの対応
リバースプロキシにSSLの処理をさせ、バックエンドは通常のHTTPを使う
これでHTTP前提で作られたサイトを用意にHTTPS対応ができる
第8章 性能向上
性能の良いwebシステムとは何か
スループット
単位時間あたりの処理量
毎秒何バイト転送したのか、何回のアクセスを処理したのかという値のこと
単位はbytes/sec, requests/secなど
大きければ大きいほど性能が高い。通信に関するスループットは帯域と呼ぶこともある
レスポンス時間
1つのアクセスの応答にかかった時間
レイテンシ, 応答時間とも呼ぶ
1つのアクセスには、プロトコル処理、I/O処理が全て含まれている
レスポンス時間の値は、小さければ小さいほど性能が高い
https://scrapbox.io/files/659015eee6e7d20023a61cb3.png
多重度
スループットやレスポンスなどは、複数のアクセスを同時に処理するケースがほとんど
同時に行うアクセスの数を表す言葉。同時接続数, 並列度などとも呼ぶ
ベンチマーク
性能測定用のソフトウェア
システム全体を測定するシステムベンチワーク
個別の要素を測定するマイクロベンチマークに分類ができる
webサーバーのベンチマークにはabなどがあり、個々に検証して行ってボトルネックの箇所を探るのが基本。
ホットスタートとコールドスタート
アクセスがあり安定した状態で行うのがホットスタート
起動直後の状態で行うのがコールドスタート
ホットスタートでベンチマークは測定されることが多い
性能を制限する
実環境が複雑な場合、帯域を制限するtcコマンドなどで意図的に制限する
こちらで実環境に近づけて負荷検証を行ったりする
モニタリングしよう
vmstat, iostat
一定時間おきにリソースの使用状況を出力する
dstat
vmstatなどと同じ
プラグインで色付けしたりと拡張ができる
sar
分おきにデータを取得して保存でき、参照できる
munin
デーモンを通じてデータの取得をする
複数サーバーのデータを集計し、webブラウザで確認できる
cacti
データを集計しグラフ化できるなどなど
第9章 セキュリティ
Linuxで考慮すべきセキュリティ設定について
ファイアウォール設定
ネットワークのポート番号、アクセス元アドレスの情報からアクセスを制限する機能の総称。
サービスに必要な接続以外を設定しておけば良い。
listenアドレスの設定
listen
Unixシステムコールの1つで、ソケットでアクセスを待ち受けるシステムのこと
アクセスを受け付けるIPアドレスを指定するものが、listenアドレス
nginxでlistenディレクティブで指定できる
意図しないネットワークからの接続を受け付けないようにできる
ログインの設定
sshでログインできるユーザーやアクセス元の制限、公開鍵認証のみに制限したり
セキュアOSの使用
SELinuxなど、それぞれのプログラムが操作可能な範囲のルールを限定する
仮に侵入された時の被害の拡大を防ぐことができる
仮想マシンやコンテナによる機能の分離
侵入されたとしても、影響を限定させられる
ログ、負荷の監視
異常検知や攻撃に気付きやすくなる
セキュリティアップデートの適用
セキュリティアップデートの公開は、脆弱性の公開と言っても良い
IPS, ISDの利用
10章移行の要点についてまとめ
クラスタ構成
同じように動作する複数のサーバを使い、サービスを提供する
負荷を複数のサーバーに分散させる負荷分散クラスタ
2台あれば、2台のサーバーに均等の負荷を分散することで1台の場合の2倍の負荷を処理できる
複数のサーバーで冗長性を持たせ、可用性を上げるHAクラスタに分類できる
High Availability
稼働系と待機系に分ける
稼働系がダウンした時、意図的に待機系に切り替えてダウンタイムを減らす
メンテナンスの考え方
私事だが、aws側のロードバランサでできないなら、nginxでやればいい