AWS - Capistranoを使ってデプロイした際のエラー奮闘記

こんにちは。
早く駆け出しエンジニアになりたい@iloveomeletteです。

標題にある通り、Capistranoを使ってデプロイした際に少々苦戦したので、
共有していこうと思います。
ポケモンのジム戦のようにエラーを解決するたび、またエラーに遭遇するということを繰り返してました。しょうもないエラーも紹介しつつ解決までの過程を書いていきます。
誰かの参考になれば幸いです。

尚、ご指摘箇所がございましたら
ご教授いただけますと幸いです。

それでは以下の目次でお送り致します。

| ラウンド | エラー項目 |
|:-:|:-:|
| 1  | VS 『supports platforms』 問題  |
| 2 | VS 『An error occurred while installing mysql2 (0.5.3)』 問題  |
| 3  | VS 『Mysql2::Error: Unknown database 'アプリ名_production'』 問題  |

## 前提

それでは、まず環境などの前提部分からですね。

- macOS Monterey 12.3.1 (M1)
- Ruby 3.1.1
- Rails 6.1.5
- nginx
- puma
- MariaDB 10.6.7

利用しているAWSサービスはEC2とRDSを利用していて「Amazon Linux 2 AMI」の「t2.micro」を選択しています。こんなところでしょうか。追加があれば追加していきます。

＊ **サーバー環境**のターミナル => EC2にログインした状態
＊ **ローカル環境**のターミナル => 通常のターミナル

以降の話はこちらのコマンド実行後の話です。

```zsh:ローカル環境のターミナル
$ bundle exec cap production deploy BRANCH=HEAD
```

## ラウンド1 『supports platforms』 問題

それでは初戦ですね。

### 結論

先に結論を述べると以下のコマンドを実行してこの問題を倒すことができました。

```bash:ローカル環境のターミナル
$ bundle lock --add-platform x86_64-linux
Fetching gem metadata from https://rubygems.org/.........
Resolving dependencies...
Writing lockfile to ~~~/アプリ名/Gemfile.lock
```

### エラー箇所と謎解き

途中までは上手くいっていたのですが、以下のエラーが発生しました（以降、省略するときは「###」を挟みます）

```bash:ローカル環境のターミナル
###
00:35 bundler:install
      01 Your bundle only supports platforms ["x86_64-darwin-21"] but your local platform
      01 is x86_64-linux. Add the current platform to the lockfile with
      01 `bundle lock --add-platform x86_64-linux` and try again.
###
status: 16 (SSHKit::Runner::ExecuteError)
bundle stdout: Nothing written
bundle stderr: Your bundle only supports platforms ["x86_64-darwin-21"] but your local platform
is x86_64-linux. Add the current platform to the lockfile with
`bundle lock --add-platform x86_64-linux` and try again.
###

** DEPLOY FAILED
** Refer to log/capistrano.log for details. Here are the last 20 lines:
###
 DEBUG [5d094cc5] 	Your bundle only supports platforms ["x86_64-darwin-21"] but your local platform

is x86_64-linux. Add the current platform to the lockfile with

`bundle lock --add-platform x86_64-linux` and try again.

```

これはつまり「お使いのbundleは"x86_64-darwin-21"のみサポートしてるけど、あなたのローカル環境は"x86_64-linux"だからこれを追加してね」ということですね。

親切なことに`bundle lock --add-platform x86_64-linux`をしてねと教えてくれています。

では、この`bundle lock`とは何なのでしょうか。
[公式リファレンス](https://bundler.io/v2.3/bundle_lock.html)によると`bundle lock`は

> Creates / Updates a lockfile without installing

であると、つまり「インストールなしでlockfileを生成したり更新したりする」コマンドですね。
では`--add-platform`は何をするのでしょうか。察しがつくと思いますが、

> Add a new platform to the lockfile, re-resolving for the addition of that platform.

であると、つまり「lockfileに新しいプラットフォームを追加して再解決する」コマンドですね。

このコマンドを実行後、再度コミット、プッシュしてデプロイを実行するとこの問題を倒すことができました。

## ラウンド2 『An error occurred while installing mysql2 (0.5.3)』 問題

先ほどのエラーを倒してやっとデプロイができると思っていた矢先、すぐにバトルが始まります。

### 結論

結論から述べると以下のコマンドを実行してMariaDB関連のインストールを行えば解決します。

```zsh:サーバ環境のターミナル
$ sudo yum install -y MariaDB-devel MariaDB-shared
```

**ここはかなりご自身の環境によって大きく問題が異なる可能性が高いのでエラー文をよく読むことをお勧めします。**

### エラー箇所と謎解き

エラー内容は以下の通り

```bash:ローカル環境のターミナル
00:31 bundler:install
      01 Gem::Ext::BuildError: ERROR: Failed to build gem native extension.
###
      01 Could not create Makefile due to some reason, probably lack of necessary
      01 libraries and/or headers.  Check the mkmf.log file for more details.  You may
      01 need configuration options.
###
      01 /home/****/.rbenv/versions/3.1.1/lib/ruby/3.1.0/mkmf.rb:1086:in `block
      01 in find_library': undefined method `split' for nil:NilClass (NoMethodError)
      01
      01     paths = paths.flat_map {|path| path.split(File::PATH_SEPARATOR)}
      01                                        ^^^^^^
      01 from 
###
Caused by:
SSHKit::Command::Failed: bundle exit status: 5
bundle stdout: Nothing written
bundle stderr: Gem::Ext::BuildError: ERROR: Failed to build gem native extension.
###
** DEPLOY FAILED
** Refer to log/capistrano.log for details. Here are the last 20 lines:
###
An error occurred while installing mysql2 (0.5.3), and Bundler cannot continue.

In Gemfile:

  mysql2
```

エラー文を読んでみると「mysql2 (0.5.3)をインストールしている間にエラーが発生して続けることができませんでした」というような内容ですね。

そもそも`mysql2`とは何なのでしょうか。
mysql2の[README](https://github.com/brianmario/mysql2)によると

>Mysql2 - A modern, simple and very fast MySQL library for Ruby - binding to libmysql

「Ruby用のモダンでシンプルかつ非常に高速なMySQLのライブラリである」と言っています。
つまり、RailsでMySQLを利用するために必要なものであると言えます。

少し話が逸れましたが、4行目付近でこのようなことを言っています。

```zsh:ローカル環境のターミナル
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.
You may need configuration options.
```

つまり、「詳細を知りたいなら`mkmf.log`をチェックしなさい」と言っていますので、このログを見てみましょう。
途中でこのようなメッセージがあると思うので、そのパスを使用します。

```zsh:ローカル環境のターミナル
To see why this extension failed to compile, please check the mkmf.log which can
be found here:
# 下記のパスを使用します
~~~/shared/bundle/ruby/3.1.0/extensions/x86_64-linux/3.1.0/mysql2-0.5.3/mkmf.log
```

`cat`コマンドを利用してログの中身を見てみましょう、と言いたいところなのですが勉強不足のため、このログを見てもいまいちピンと来ませんでした。念の為、見てみることをお勧めします。

```bash:サーバ環境のターミナル
$ cat ~~~/shared/bundle/ruby/3.1.0/extensions/x86_64-linux/3.1.0/mysql2-0.5.3/mkmf.log
```

では、どうしたかというと先ほどのメッセージの1文前のメッセージを見てみると

```zsh:ローカル環境のターミナル
Could not create Makefile due to some reason, probably lack of necessary libraries and/or headers.
```

これは「Makefileをいろんな理由により作れなかったよ。多分、必要なライブラリやヘッダーが足りないからかも」と言っています。エラー文で様々調査を行なっているとある[GitHub Issues](https://github.com/brianmario/mysql2/issues/877)に辿り着きます。ここでは`yum install -y Mariadb-devel Mariadb-shared`をTryしてみてと言っています。他の記事でもこのコマンドを見かけていたので、そもそもこの2つは何なんだと調べてみました。
[公式MariaDB](https://mariadb.com/kb/en/about-the-mariadb-rpm-files/)(少しバージョンは古いですが)によると

>MariaDB-devel - Development headers and static libraries.
>MariaDB-shared - Dynamic client libraries.

な、なんかこれっぽい...
そうです。MariaDBをインストールする際は、[公式AWS](https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/USER_ConnectToMariaDBInstance.html)を参考にして導入していたので、Rails用に必要なものをインストールできていなかったということですね。
後付けではありますが、mysql2のREADMEでも以下のように記されています。

>You may need to install a package such as libmariadb-dev, libmysqlclient-dev, mysql-devel, or other appropriate package for your system. 

RailsからMariaDBを操作するので、少なくとも`Mariadb-devel`は必要みたいです。
ということで、MariaDB関連のインストールを行なって、このエラーを倒すことができました。

```bash:サーバ環境のターミナル
$ sudo yum install -y MariaDB-devel MariaDB-shared

# 以下を実行してRDSのMariaDBにログイン
$ mysql -h エンドポイント -u root -p

# 以下を実行してデータベースを確認
MariaDB [(none)]> show databases;
# 表示されたデータベース一覧に「アプリ名_production」があるか確認
```

## ラウンド3 『Mysql2::Error: Unknown database 'アプリ名_production'』 問題

さあ、これでやっとデプロイできるかなと思った矢先、またまたエラーと遭遇します。
ここはサクッと終わらせます。

### 結論

とてもつまらないミスでした。

```diff_yaml:config/database.yml
production:
  <<: *default
- database: AB_app_production
+ database: A_B_app_production
  username: -
  password: <%= Rails.application.credentials.db[:password] %>
  host: <%= Rails.application.credentials.db[:hostname] %>
```

### エラー箇所と謎解き

では、どのようなエラーか見ていきます。

```zsh:ローカル環境のターミナル
01:52 deploy:migrating
      01 $HOME/.rbenv/bin/rbenv exec bundle exec rake db:migrate
      01 rake aborted!
      01 ActiveRecord::NoDatabaseError: Unknown database 'AB_app_production'
###
Caused by:
Mysql2::Error: Unknown database 'AB_app_production'
```

今度は「よく分からんデータベースがあるよ」と言われています。
この時、疲れていた私はMariaDBの再起動などを試したりしていました。

ここで言いたいことはちゃんとエラー文をよく見ろということですね。
例えば、私のアプリは「`A_B_app_production`」なのですが、ここで示されているものは
「`AB_app_production`」ということですね。これを見分けるのは難しいですね笑
まずはタイポがあるか確認することが大事だと痛感しました。

余談ですが、恐らく最初のアプリ立ち上げで命名する際に「 _(アンダースコア) 」を入れ忘れたんですね。

では、以下のように修正してコミット、プッシュ後に再度デプロイをします。

```diff_yaml:config/database.yml
production:
  <<: *default
- database: AB_app_production
+ database: A_B_app_production
  username: -
  password: <%= Rails.application.credentials.db[:password] %>
  host: <%= Rails.application.credentials.db[:hostname] %>
```

これにてやっとデプロイに成功しました！！
**で、す、が！** 本当の地獄はここからでした。
次回は、実際にElastic IPにアクセスしてアプリが表示されるか確認をした際の奮闘記をお届けします。

## 参考文献

- [Unable to install mysql2 gem with MariaDB 10.2.7 on CentOS 7: "cannot find -lmariadb"](https://github.com/brianmario/mysql2/issues/877)
- [About the MariaDB RPM Files](https://mariadb.com/kb/en/about-the-mariadb-rpm-files/)
- [Bundler公式リファレンス](https://bundler.io/v2.3/bundle_lock.html)
- [mysql2のREADME](https://github.com/brianmario/mysql2)