AWS/Rails デプロイ後の『502』エラー奮闘記

## 概要

先日、[「AWS - Capistranoを使ってデプロイした際のエラー奮闘記」](https://qiita.com/iloveomelette/items/3c0f2dd3faa2fcefc948)を書きました。
今回は、そのデプロイ後に起きた「**502**」エラーの対処法を述べていきます。

**デプロイには成功したのに、アプリが表示されない**、といった方の参考になれば幸いです。

前提として、デプロイにはCapistranoを利用しています。
構成は以下の通りです。

- macOS Monterey 12.3.1 (M1)
- Ruby 3.1.1
- Rails 6.1.5
- nginx
- puma
- MariaDB 10.6.7
- EC2(Amazon Linux 2 AMI / t2.micro)
- RDS

こんな感じです。

## 「502」エラーとは

そもそも「502」エラーとは何なのでしょうか。

>502エラーとは、Webサーバで起きるエラーの種類の一つで、目的地のWebサーバが機能していないことを、経路途上にある中継サーバ（プロキシなど）が報告するもの。
>引用先：[IT用語辞典 e-Words](https://e-words.jp/w/502%E3%82%A8%E3%83%A9%E3%83%BC.html)

ステータスコード500番台はサーバ側の問題であり、502は"Bad Gateway"というエラーメッセージを吐き出します。目的のサーバがレスポンスを正常に返してくれないよ、というサインです。
実際に、私がアプリケーションにアクセスした際の画面が以下です。

![nginx-error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2459618/ba7518d7-4220-9d3c-7748-f0c5414b22a6.png)

これだけだと内部がどうなっているかわからないので、エラーログを実際に見ていきます。

## エラーログを見る

以降では2つのターミナルが登場してきます。

＊<font color="red">ローカル</font>のターミナル => 通常のターミナル
＊<font color="red">サーバ</font>のターミナル => EC2にログインしている状態のターミナル

以下をローカルのターミナルで実行してみてください。

```zsh:ローカルのターミナル
$ curl -I <Elastic IP>
HTTP/1.1 502 Bad Gateway
          :
```

こうですね。「502」エラーが表示されています。
では、次にサーバ側のエラーログを見て具体的にどうなっているか確認をします。
**ログまでのパスは構成によって異なりますので、ご自身でご確認ください。**
筆者の場合、以下の通りです。

```bash:サーバ側のターミナル
$ cd /var/www/$APP_NAME/shared/log

# 移動ができたらディレクトリ内を確認
$ ls
=> nginx.access.log  nginx.error.log  production.log

# catコマンドでファイルの中身を確認
$ cat nginx.error.log
=> 2022/**/** **:**:** [crit] : *6 connect() to unix:/var/www/$APP_NAME/shared/tmp/sockets/puma.sock failed (2: No such file or directory) while connecting to upstream, client: ****, server: localhost, request: "HEAD / HTTP/1.1", upstream: "http://unix:/var/www/$APP_NAME/shared/tmp/sockets/puma.sock:/", host: "****"
```

すると、どうやら`2: No such file or directory`（ファイルもしくはディレクトリがないよ）と言ってますね。(＊「$APP_NAME」は事前にアプリ名を変数に格納しているのでアプリ名と思ってください)
では、実際にこのファイルもしくはディレクトリがないか確認をします。
以下のコマンドを実行します。

```bash:サーバ側のターミナル
# ルートディレクトリから
$ cd /var/www/$APP_NAME/shared/tmp/sockets
$ ls
>
```

特に何も表示されていないということは`puma.sock`ファイルが作成されていないということになります。

## `puma.sock`とは

`puma.sock`とは、ソケット通信を行なってWebサーバと連携をするために必要なソケットです。
つまり、pumaを起動するのに必要なファイルということです。
そのため、Pumaの設定を編集する必要があります。

```ruby:config/puma.rb
# 以下の部分をコメントアウト
# port ENV.fetch("PORT", 3000)

# socketの設定
bind "unix://#{Rails.root}/tmp/sockets/puma.sock"
```

`port ENV.fetch("PORT", 3000)`をコメントアウトせずデプロイを行うと、こちらを認識してしまい、うまく動作しないことがあるそうなので、コメントアウトをしておきましょう。

**で、す、が**
筆者の場合、これを修正してコミット、プッシュ、デプロイ後
確認をするも改善はしませんでした。

## `#{Rails.root}`の中身を確認しよう

そもそも`#{Rails.root}`の中身はどういったものなのでしょうか。
この確認を怠ると余計に時間を取られます。
筆者の場合、以下の通りでした。

＊筆者の場合、`current`配下に`app`ディレクトリなどが格納されています。ここは環境によって異なるのでご自身で確認してください。

```bash:サーバ側のターミナル
# サーバのルートディレクトリで実行
$ cd /var/www/$APP_NAME/current
$ yarn install --check-files
yarn install v1.22.18
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
Done in 8.01s.

# サーバ側で'rails c'を使用するためには以下のコマンド
$ bin/rails c -e production
Loading production environment (Rails 6.1.5)
irb(main):001:0> Rails.root
=> #<Pathname:/var/www/$APP_NAME/releases/****>
```

`yarn install --check-files`は`yarn.lock`と「インストールされている内容」に差異があった場合、バージョンを合わせるためのインストールをおこなってくれます。

>Verifies that already installed files in node_modules did not get removed.
>[Yarn公式](https://classic.yarnpkg.com/lang/en/docs/cli/install/)

少し話はそれましたが、`Rails.root`を確認すると、`/var/www/$APP_NAME/releases/****`と全く異なるパスを指定してますね。そりゃ、起動できないわって感じですね。
というわけで修正を行います。

```ruby:config/puma.rb
bind "unix:///var/www/$APP_NAME/shared/tmp/sockets/puma.sock"
```

これでようやくアプリが表示されるかなとアクセスしてみると...
**なぜか表示されません！！！**
正直、発狂しましたよ。

色々調べた結果、ある仮説に辿り着きます。
そういえばpumaの設定ファイルもう一つあったな、と。
早速見に行くことにしましょう。

```bash:サーバ側のターミナル
$ cat /etc/systemd/system/puma_$APP_NAME_production.service
[Unit]
Description=Puma HTTP Server for $APP_NAME (production)
After=network.target


[Service]
Type=simple
User=****
WorkingDirectory=/var/www/$APP_NAME/current
# Support older bundler versions where file descriptors weren't kept
# See https://github.com/rubygems/rubygems/issues/3254
ExecStart=/home/user
ExecReload=/bin/kill -USR1 $MAINPID
StandardOutput=append:/var/www/$APP_NAME/shared/log/puma_access.log
StandardError=append:/var/www/$APP_NAME/shared/log/puma_error.log




Restart=always
RestartSec=1

SyslogIdentifier=puma

[Install]
WantedBy=multi-user.target
```

特に起動コマンド`ExecStart`に注目してほしい。
pumaが起動するのは`puma.sock`を使って起動するはず。
パスが間違っている！

なので、パスを編集しました。

```bash:サーバ側のターミナル
# サーバのルートディレクトリから
$ cd /etc/systemd/system/
# ${APP_NAME}の箇所は自分のアプリ名に変えてVimを開く
$ vi puma_${APP_NAME}_production.service
# 設定ファイルの再読込
$ sudo systemctl daemon-reload
# サービス自動起動有効
$ sudo systemctl enable puma_${APP_NAME}_production.service
```

Vimが開けたら修正します。

```vim:puma_${APP_NAME}_production.service
ExecStart=/home/ユーザ名/.rbenv/bin/rbenv exec bundle exec --keep-file-descriptors puma -C /var/www/$APP_NAME/shared/puma.rb
```

修正ができたら以下を実行します。

```bash:サーバ側のターミナル
# サーバのルートディレクトリから
$ cd /etc/nginx
# nginxの再起動
$ sudo service nginx restart
Redirecting to /bin/systemctl restart nginx.service
```

これにてようやくアプリを表示することができました。

お疲れ様です。
最後までお読みいただきありがとうございます。

## 参考

- [puma/puma](https://github.com/puma/puma)
- [bundle install](https://bundler.io/man/bundle-exec.1.html)
- [NginxとRails（Puma）をソケット通信で連携させる方法！](https://kitsune.blog/nginx-rails)
- [nginxとPumaを連携し、nginx + Puma + Rails6の開発環境を構築する手順](https://nishinatoshiharu.com/nginx-puma-setting/)
- [【Puma】Rails 5.2 + Puma + Nginx のデプロイ設定](https://serip39.hatenablog.com/entry/2020/12/23/235700)