【Amazon Linux2】【Rails】Nginxをpumaのunixドメインソケットでリバースプロキシする設定

## 背景

ポートフォリオをEC2で公開するときにrails serverだとhttpで3000番ポートで通信をしなければならず、アクセスログの取得が不可で、かつアクセス負荷に対応ができません。
そういった場合、Nginxを利用してpumaをunixドメインソケットでリバースプロキシをする場合、接続方法でハマってしまったので、備忘録を兼ねてまとめます。

#### 環境

EC2(AmazonLinux2)
Nginx 1.20
puma 5.2.2
ruby 2.7.2
Rails 6.1
MySQL 5.7

## puma

WEBサーバーの一つ。Railsではアプリケーションサーバーとしてよく使われていると思います。
速度やメモリ効率に強みを持つらしい。同様のサーバーではunicornなどがある。
pumaとunicornの比較はこの以下のサイトがわかりやすかったです。

[Pumaの使い方 まとめ](https://nekorails.hatenablog.com/entry/2018/10/12/101011)

### puma設定ファイル

```:config/prd_puma.rb
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
threads min_threads_count, max_threads_count

worker_timeout 3600 if ENV.fetch("RAILS_ENV", "production") == "production"

bind "unix:///var/www/アプリ名/shared/sockets/puma.sock"

environment ENV.fetch("RAILS_ENV") { "production" }

pidfile ENV.fetch("PIDFILE") { "shared/pids/server.pid" }

plugin :tmp_restart
```

|ディレクティブ|説明|
|---|---|
|max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }|変数max_threads_countを定義|
|min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }|変数min_threads_countを定義|
|threads min_threads_count, max_threads_count|スレッドの最小数, 最大数を定義|
|worker_timeout 3600 if ENV.fetch("RAILS_ENV", "production") == "production"|pumaのworkerのタイムアウト時間を定義|
|bind "unix:///var/www/アプリ名/shared/sockets/puma.sock"|bindでどのように接続するかをURIで指定。今回はunixソケット経由で接続|
|environment ENV.fetch("RAILS_ENV") { "production" }|pumaをどの環境で動作させるか指定。今回はproduction|
|pidfile ENV.fetch("PIDFILE") { "shared/pids/server.pid" }|PIDファイルの設置場所|
|plugin :tmp_restart|pumaに機能追加するときに使う。デフォルトでtmp_restart。pumaを再起動することでできる|


## Nginx

NginxとはWEBサーバーの一つです。
ApacheというWEBサーバーの弱点であるC10k(Connect 10 × 1000 = 10,000の通信)問題を回避したWEBサーバーです。
Apacheは１アクセスに対して１対応する仕様なので、つ通信が10,000くると、負荷が大きくなってしまう弱点がありました。これに対して、Nginxは複数の通信に対して１対応なのでアクセス数の増加に比例して急激な負荷の増加を抑えられるそうです。

### Nginx設定ファイル


```terminal:EC2ターミナル
$ sudo amazon-linux-extras install nginx1
```
ちなみに`amazon-linux-extras`とは、AmazonLinux2に存在する<b>Extras Library</b>というパッケージ群を操作するコマンドです。
もしAmazonLinux1を使っている場合には、amazon-linux-extrasコマンドはないので<b>yum</b>というコマンドを使いましょう。

```terminal:EC2ターミナル
$ nginx -v
->nginx version: nginx/1.20.0
```
インストールされていれば、バージョンが表示されます。

次に自分のアプリ専用にNginx設定ファイルを作りましょう。

インストールすると、`/etc`配下に`nginx/nginx.conf`や`nginx/conf.d`というディレクトリが生成されているはずです。
Nginxには`nginx.conf`には、Nginx自体の設定(起動, アクセス負荷チューニング等)を書きます。このファイルにもバーチャルホストを書くことはできますが、わかりやすくするため<b>バーチャルホスト*</b>の設定は`nginx/conf.d`配下に`アプリ名.conf`を作りそこに書きましょう。

><b>*バーチャルホストとは</b>
デフォルトのバーチャルホストとは、複数のサイトが運用されているウェブサーバーに、IPアドレス（ドメイン名ではなく）でHTTPリクエストした場合に応答するホストのことです。
引用：[nginxでデフォルトのバーチャルホストを設定する方法](https://webkaru.net/linux/nginx-default-server/)


`アプリ名.conf`の書き方は以下のとおりです。

```terminal:EC2ターミナル
$ sudo vi /etc/nginx/conf.d/アプリ名.conf
```
viコマンドでファイルの編集+新規作成ができます。
viについてはコマンドモードと入力モードを抑えておけばとりあえず大丈夫だと思います。コマンドモード中にiを押すと入力モードへ、入力モード中にescを押すとコマンドモードになります。

1. iを押して入力モードへ
2. 内容を編集
3. escでコマンドモードへ
4. :wqで保存して終了

|コマンド|説明|
|---|---|
|i|insert(入力)モード|
|esc|コマンドモード|
|:wq|保存して終了|
|:q!|保存せずに終了|

*もちろんまだまだ重要なコマンドはあるので気になる方は調べてみましょう！


```nginx:/etc/nginx/conf.d/アプリ名.conf
upstream puma {
  server unix:/var/www/アプリ名/shared/sockets/puma.sock fail_timeout=0;
}

server {
  listen 80;
  server_name [ドメイン(ない場合はElastic IP];

  root /var/www/アプリ名/public;
  error_log  /var/log/nginx/error.log;
  access_log /var/log/nginx/access.log;
  client_max_body_size 2G;
  keepalive_timeout 5;

  # page cache loading
  try_files $uri/index.html $uri.html $uri @app;
  location / {

    # HTTP headers
    proxy_set_header X-Real-IP $remote_addr;
    index index.html index.htm;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://puma;

  }

}
```

設定ファイルの雛形はインストール後に自動生成される`nginx/nginx.conf`を見ると良いと思います。

設定ファイルの説明は以下のとおりです。

|ディレクティブ|説明|
|---|---|
|upstream {}||
|server unix:/var/www/アプリ名/shared/sockets/puma.sock fail_timeout=0;|UNIXドメインのpumaソケットファイルを指定|
|server {}|バーチャルホストを定義|
|listen 80;|バーチャルホストが使用するポートを指定|
|server_name [ドメイン(ない場合はElastic IP];|バーチャルサーバー内で使うホスト名としてドメイン名を指定|
|root /var/www/アプリ名/public;|公開するディレクトリを指定|
|error_log  /var/log/nginx/error.log;|エラーログの出力先を設定 (/var/log配下だとわかりやすい)|
|access_log /var/log/nginx/access.log;|アクセスログの出力先を設定 (/var/log配下だとわかりやすい)|
|client_max_body_size 2G;|Nginxが受信可能なリクエストボディの量を指定|
|keepalive_timeout 5;|HTTP通信をタイムアウトを待つ秒数を指定|
|try_files \$uri/index.html \$uri.html $uri @app;|指定したファイルパスがある場合はリダイレクト、ない場合はその次のファイルパスへと繰り返し、最後までない場合は最後に指定されたURIへとリダイレクト|
|location URI {}|リクエストされたURIにマッチししたときに使われる設定。設定は{}内に書く。|
|proxy_set_header XXXX|リクエストをプロキシする際にヘッダを付与する|
|X-Real-IP $remote_addr;|`X-Real-IP`で送信元を判別、`$remote_addr`で送信元IPを取得|
|index|インデックスページとして表示するファイルを指定|
|X-Forwarded-Proto $scheme;|ユーザーリクエストが使ったHTTPスキームを指定|
|X-Forwarded-For $proxy_add_x_forwarded_for;|リクエストが経由したアドレス全てを指定|
|Host $http_host;|ユーザーリクエストに含まれるホスト名を指定|
|proxy_redirect|レスポンスヘッダをどのように書き換えるかを指定(今回はoff)|
|proxy_pass 転送先URI|リクエストを転送先URIにプロキシ**する。今回はupstreamで`puma`として定義したpuma.sockに転送。|


<b>エラーが起きたとき</b>
`cat /var/log/nginx/error.log`を見てデバッグしましょう。(権限なければ sudoを頭につけてください。）

<b>アクセスログ確認方法</b>
`cat /var/log/nginx/access.log`


### 用語解説

`$`: 変数を展開している。Nginxではもともと定義されている予約語的な変数がある。今回はほとんど予約語を使っている。
`フォワードプロキシ`: 一般に「プロキシ」と呼ばれる。クライアントとサーバーの間に設置し、 内部ネットワークからインターネットへの通信を中継する。主にセキュリティ目的
`リバースプロキシ`: WEBサーバーの前に設置し、WEBサイトへのリクエストを代理受付する。主にセキュリティ目的

`Unixドメインソケット`:
>　ソケットは「受け口、接合部」の意味。「UNIXドメインソケット」とは、LinuxなどのUNIX系OS（オペレーティングシステム）で実行されるプロセス間のデータ通信の終点に使われるインターフェースのことをいう。
　LinuxなどのUNIX系OSは、各種デバイスの入出力にデバイスファイルというインターフェースを利用するため、一般のファイルを読み書きするのと同じ手順で周辺機器を操作できる。ところが、プロセス同士がネットワーク経由で通信するときには、ファイル・インターフェースを拡張した「ソケット」というインターフェースが必要になる。通常インターネットで利用しているソケット通信（INETドメインソケット通信）とは異なり、ネットワーク経由ではないローカルマシン上のプロセスが利用するソケットが、UNIXドメインソケットである。
引用：[UNIXドメインソケット](https://www.otsuka-shokai.co.jp/words/unix-domain-socket.html)

## まとめ

- プロキシとはクライアントとサーバー間に設置することで内部から外部への通信を中継してくれる。
- リバースプロキシはWEBサーバーの前に設置することでWEBサイトへのリクエストを代理受付してくれる。例えば会社であれば誰がどのサイトにアクセスしたか等を見ることができセキュリティ強化に繋がる。
- Nginxとpumaをリバースプロキシさせることで、アクセス負荷対策やrails serverでは取得できないアクセスログを取得できる

未経験の立場からすると、サーバーサイドエンジニア志望となるとサーバーサイド言語の勉強に偏りがちですが、Nginxやpuma等のサーバーそのものの勉強は必須だと感じました。
言語と並行してWEB周りの知識も付けていきたいと思います。

## 参考

<b>puma</b>
- [RackサーバーのPumaについて調べてみる](https://arakaji.hatenablog.com/entry/2015/08/03/200502)
- [Pumaの使い方 まとめ](https://nekorails.hatenablog.com/entry/2018/10/12/101011)
- [【Rails】puma.rbの気になるあれこれ](https://qiita.com/redrabbit1104/items/9292b5891dc46eb64f3a)

<b>Nginx</b>
- [ex1-lab](https://ex1.m-yabe.com/archives/4305)
- [Carpe Diem](https://christina04.hatenablog.com/entry/2016/10/25/190000)
- [8.1.11 proxy_redirect](http://www2.matsue-ct.ac.jp/home/kanayama/text/nginx/node68.html)
- [WEBプロキシとは？ 仕組みや機能・メリットについて解説](https://www.amiya.co.jp/column/web_proxy_20200911.html)