light log

学んだこととか

Unicorn用のNginxの設定をする

nginx.confを昨日いい加減に書いたので、

yamacent.hatenablog.com

設定を見直す。

昨日の書いたのは以下。

nginx.conf

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}

http {
  include mime.types;
  default_type application/octet-stream;

  sendfile on;
  keepalive_timeout 65;

  upstream unicorn_app {
    server unix:/tmp/unicorn.sock;
  }

  server {
    listen 80;
    server_name _;
    location / {
      proxy_pass http://unicorn_app;
    }
  }
}

以下を参考にした。

serverブロックを別ファイルにする

まず、初期状態のnginx.confのhttpブロックはこうなってる。

http {
  ...

  include /etc/nginx/conf.d/*.conf;
}

各サーバの設定はconf.d以下にサーバごとにファイルを分けて格納するのがより良い方法なのだろう。

ということで、新たにconf.d/rails_app.confを作って、upstreamブロックとserverブロックをそっちに移動する。

rails_app.conf

upstream unicorn_app {
  server unix:/tmp/unicorn.sock;
}

server {
  listen 80;
  server_name _;
  location / {
    proxy_pass http://unicorn_app;
  }
}

nginx.confは初期状態の、conf.d以下をincludeする形に戻す。

ここまでで一旦動作確認する。本質的には設定内容は変わってないはずなので、問題なく動くはず。

$ unicorn_rails -c config/unicorn.rb 

conf.d以下に最初からあるファイル(default.conf、example_ssl.conf)はファイル名末尾に.bkとかを付けてincludeしないようにしておいた。

設定内容の意味を知る

だいたい見た目から予想はつくものの、昨日の時点ではちゃんと調べずに書いてたので、ちょっと調べた。

upstream

Wikiにはこう書いてある。

Defines a group of servers. Servers can listen on different ports.

Module ngx_http_upstream_module

サーバグループを定義するやつみたい。

今回のように一つだけ書く場合には、ただ単に後ろで参照するための名前を付けているのと同じだと思う。

試しにupstreamの内容を後方のproxy_passに直接書いてみる。

rails_app.conf

#upstream unicorn_app {
#  server unix:/tmp/unicorn.sock;
#}

server {
  listen 80;
  server_name _;
  location / {
    #proxy_pass http://unicorn_app;
    proxy_pass http://unix:/tmp/unicorn.sock;
  }
}

リロード。

$ sudo nginx -s reload
$ unicorn_rails -c config/unicorn.rb 

問題なく表示された。元に戻しておく。

listen

これは見た目通りリッスンするポート番号だろう。

Sets the address and port for IP, or the path for a UNIX-domain socket on which the server will accept requests.

Module ngx_http_core_module

サーバが受け入れるリクエストのアドレスとポート、またはUNIXドメインソケットのパス。

それはまあそうなんだけど、気になるのはデフォルト。

If the directive is not present then either :80 is used if nginx runs with the superuser privileges, or :8000 otherwise.

とのことなので、superuser権限で実行してたらデフォルトで*:80が使われる。

ということはlisten 80;の行は要らないから消しちゃおう。

リロード。問題なし。

server_name

これも見た目通り。

Server names are defined using the server_name directive and determine which server block is used for a given request.

Server names

nginxはこのリクエストを受け取ると、このホスト名がserver_nameディレクティブに設定したサーバ名に一致するserverディレクティブのバーチャルサーバを選び、そのserverディレクティブ内の設定が適応されます。

nginx連載4回目: nginxの設定、その2 - バーチャルサーバの設定 - インフラエンジニアway - Powered by HEARTBEATS

指定してあるアンダースコア(_)は全部に一致するやつ。

In catch-all server examples the strange name “_” can be seen:

Server names

最初はこれもアンダースコアがデフォルトかと思ったけど、デフォルトは""だった。

Default: server_name "";

It allows this server to process requests without the “Host” header field

Module ngx_http_core_module

空を意味する""を設定すると、Host:ヘッダ フィールドがない場合に一致します。

nginx連載4回目: nginxの設定、その2 - バーチャルサーバの設定 - インフラエンジニアway - Powered by HEARTBEATS

""の場合にはHostヘッダフィールドがない場合に一致となっているので、server_name _;の行を削除すると動かないかと思ったけど、動いた。server_name hoge;とかにしてみても動いた。

なんでだろうと思ったけど、

複数のバーチャルサーバがあるときには、リクエストのHost:ヘッダ フィールドのホスト名がserver_nameディレクティブのサーバ名に一致するserverディレクティブのバーチャルサーバが適応されます。しかし、一致しない場合にはデフォルトサーバとして選ばれたバーチャルサーバが適応されます。

デフォルトサーバはIPアドレスとポート番号の条件が一致したlistenディレクティブを持つserverディレクティブの中で次のものが選ばれます。

  • 一致したlistenディレクティブの中でdefault_serverオプションを持つ
  • 設定ファイル内で最初に一致したlistenディレクティブを持つ

listenディレクティブにdefault_serverパラメータを付けると、そのserverディレクティブがデフォルトサーバになります。

nginx連載4回目: nginxの設定、その2 - バーチャルサーバの設定 - インフラエンジニアway - Powered by HEARTBEATS

これの二個目に合致して、デフォルトサーバになってるっぽい。

結果的に動くけど、意図した動作じゃないので、やっぱりアンダースコアは指定した状態にしておく。

location

これも直感的。 指定したパスに一致したリクエストを受けたときの動作をブロック内で設定する。

Module ngx_http_core_module

proxy_pass

プロキシ先のURIやパスを指定する。

Sets the protocol and address of a proxied server and an optional URI to which a location should be mapped.

Module ngx_http_proxy_module

できあがり

以上を踏まえると以下のような感じになった。

nginx.conf(初期設定のまま)

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-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;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

conf.d/rails_app.conf

upstream unicorn_app {
  server unix:/tmp/unicorn.sock;
}

server {
  server_name _;
  location / {
    proxy_pass http://unicorn_app;
  }
}

まとめ

超シンプルな設定でNginxのパワーを全然活かしてないんだろうけど、今のところこれで全然問題なさそう。

2015/5/28 追記

上記の設定だと、アプリでリダイレクトしたとき(scaffoldでできるcreateとかupdateとか)のときに、ポート番号が引き継がれなくてページが読み込めなかった。

なので、プロキシ時にヘッダ情報を引き継ぐ処理を追加する。

参考:Rails 4.2 + Unicorn + Nginx でアプリケーションサーバの構築 - Qiita

upstream unicorn_app {
  server unix:/tmp/unicorn.sock;
}

server {
  server_name _;

  location / {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_pass http://unicorn_app;
  }
}

proxy_set_header Host $http_host;だけで一応上記の問題は解消したんだけど、参考にしたページに従って他の二行も追加しておいた。

続き書いた

yamacent.hatenablog.com