AWSで簡易なWebアプリをデプロイ ~ データベース編 ~

お疲れ様です．Tomです．

AWSで簡易なWebアプリをデプロイ【データベース編】です．

:::note info
全体概要編は[こちら](https://qiita.com/tomtech_2024/items/fc14e5fe845b7d7d5712)をご覧ください．
:::

スタックを示した構成図でいうところの赤で囲った枠が今回のトピックになります．

![databaseserver01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2918716/0def6071-c42f-efca-86c7-3f89bfddf5aa.png)

実ははじめ下記で構築したOracleにDB接続しようとしましたが，もともとある設定から変数を変えると工数が増えて納期に間に合わないことが予想されたので，今回は泣く泣くHackathonでも実績のあったMySQLで実装しました．JDBCの機能を用いてアプリとDB接続を指定します．

https://qiita.com/tomtech_2024/items/b4757914fc105a1895ac

# データベース編でやることの概要
1.**テスト用にローカル環境でのデータベース構築**
Linux環境でアプリの動作確認をするときはもちろん，IDEで動かす際にもDB接続は必要だった気がします．
SQL操作時にMySQL Workbenchがあれば便利です．
きれいなGUIで接続してくれていて，操作感がとても良いのでぜひ．
![Screenshot from 2024-05-03 16-34-15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2918716/08d09ea2-33a7-3f8f-4b4a-8476b8153168.png)

※ IDEでテストするとき，何かExtensionが必要かなと思ったのですが，特に必要ありませんでした．DB接続の際に設定ファイルへ正しく記載してください．

2.**RDSの構築**
DBインスタンスもそこそこ値段食うので，ひとしきり遊んだらすぐ消すことをおすすめします．またあとから見つけたわかりやすい記事も載せておきます．以下に設定した詳細を書いてみます．

:::note info
AWS無料期間でできることは下記の通りになっているらしいです．ちょっとしたボヤキですが，この条件下に収まっているはずの枠で課金をくらいました．目くじら立てるほどの金額ではなかったですが，もう一年以上立っていたんだなと思っていたところ，「もうすぐ無料期間が終了します」というメールが来て気づいた次第です．
詳細を見逃していたのかなということにしているので，みなさんもよくご確認頂いたほうが良いと思います．
:::

https://aws.amazon.com/jp/free/?gclid=CjwKCAjw6JS3BhBAEiwAO9waF92cCaQJ5Si-FX1euYyzMOcl445H6zKT7vy1uWIG1SYYJMApQrzTGBoClaYQAvD_BwE&trk=58ef6015-6519-450b-96ea-258378c321895&sc_channel=ps&ef_id=CjwKCAjw6JS3BhBAEiwAO9waF92cCaQJ5Si-FX1euYyzMOcl445H6zKT7vy1uWIG1SYYJMApQrzTGBoClaYQAvD_BwE:G:s&s_kwcid=AL!4422!3!618145042463!p!!g!!aws!18137558856!140477907517&all-free-tier.sort-by=item.additionalFields.SortRank&all-free-tier.sort-order=asc&awsf.Free%20Tier%20Types=*all&awsf.Free%20Tier%20Categories=*all


https://zenn.dev/sway/articles/aws_publish_create_rds

# 今回の動作環境とテーブルに関する概要
ここの設定ですが，RDSのインスタンスを消してしまったせいで，思い返して書けなかった箇所があります．

**Engine Type**: MySQL (MySQL Community Edition)
**Engine Version**: デフォルト値
**Availability and durability**: Multi-AZ DB instance (おそらく)

VPCに関する設定などは`全体概要編`に示したアーキテクチャの図で書いたとおりです．

テーブルに関しては下記のようなフォーマットでSQLを実施すれば，作成できます．

``` SQL: テーブル作成の一例
CREATE TABLE `<テーブル名>` (
    `<変数名>` <型> <制約>,
(中略)
    `<変数名>` <型> <制約>
    <表単位の制約>
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
```

:::note warn
このときアプリ側で指定しているテーブルの名前と変数の型を一致させるように心がけてください！
:::

``` SQL: テーブル確認
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| XXXXX              |
| XXXXX              |
| XXXXX              |
| XXXXX              |
| XXXXX              |
| XXXXX              |
+--------------------+
6 rows in set (0.00 sec)
```
アプリの動作に必要なテーブルが全てあるかを確認します．
もしローカルのCSVファイルなどをデータベースにロードしたい場合は下記を実行します．

``` SQL: CSVファイルのロード
LOAD DATA LOCAL infile '<ファイルのパス>/<ファイル名>.csv' INTO TABLE <格納したいテーブル名> fields terminated by ',' enclosed by '"';
Query OK, 3 rows affected (0.02 sec)
Records: 3  Deleted: 0  Skipped: 0  Warnings: 0
```
あとは`SELECT * FROM <テーブル名>`とかで確認します．

# 起きた不具合とその対処法
1.**ローカルのDBが起動しない**
現象としては下記の様相でした．

``` :様相①
$ systemctl status mysqld
× mysqld.service - MySQL Server
     Loaded: loaded (/usr/lib/systemd/system/mysqld.service; disabled; preset: disabled)
    Drop-In: /usr/lib/systemd/system/service.d
             └─10-timeout-abort.conf
     Active: failed (Result: exit-code) since Fri 2024-05-03 18:54:37 JST; 2s ago
   Duration: 2h 25min 9.880s
       Docs: man:mysqld(8)
             http://dev.mysql.com/doc/refman/en/using-systemd.html
    Process: 159422 ExecStartPre=/usr/bin/mysqld_pre_systemd (code=exited, status=0/SUCCESS)
    Process: 159471 ExecStart=/usr/sbin/mysqld $MYSQLD_OPTS (code=exited, status=1/FAILURE)
   Main PID: 159471 (code=exited, status=1/FAILURE)
     Status: "Server shutdown complete (with return value = 1)"
      Error: 22 (Invalid argument)
        CPU: 2.127s

May 03 18:54:33 fedora systemd[1]: Starting mysqld.service - MySQL Server...
May 03 18:54:34 fedora (mysqld)[159471]: mysqld.service: Referenced but unset environment variable evaluates to an empty string: MYSQLD_OPTS
May 03 18:54:37 fedora systemd[1]: mysqld.service: Main process exited, code=exited, status=1/FAILURE
```
``` :様相②
$ journalctl -xeu mysqld
░░ Defined-By: systemd
░░ Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
░░ 
░░ A start job for unit mysqld.service has begun execution.
░░ 
░░ The job identifier is 20990.
May 03 20:30:22 fedora (mysqld)[160406]: mysqld.service: Referenced but unset environment variable evaluates to an empty string: MYSQLD_OPTS
May 03 20:30:25 fedora systemd[1]: mysqld.service: Main process exited, code=exited, status=1/FAILURE
░░ Subject: Unit process exited
░░ Defined-By: systemd
░░ Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
░░ 
░░ An ExecStart= process belonging to unit mysqld.service has exited.
░░ 
░░ The process' exit code is 'exited' and its exit status is 1.
May 03 20:30:25 fedora systemd[1]: mysqld.service: Failed with result 'exit-code'.
░░ Subject: Unit failed
░░ Defined-By: systemd
░░ Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
░░ 
░░ The unit mysqld.service has entered the 'failed' state with result 'exit-code'.
May 03 20:30:25 fedora systemd[1]: Failed to start mysqld.service - MySQL Server.
░░ Subject: A start job for unit mysqld.service has failed
░░ Defined-By: systemd
░░ Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
░░ 
░░ A start job for unit mysqld.service has finished with a failure.
░░ 
░░ The job identifier is 20990 and the job result is failed.
May 03 20:30:25 fedora systemd[1]: mysqld.service: Consumed 2.956s CPU time.
░░ Subject: Resources consumed by unit runtime
░░ Defined-By: systemd
░░ Support: https://lists.freedesktop.org/mailman/listinfo/systemd-devel
░░ 
░░ The unit mysqld.service completed and consumed the indicated resources.
```
原因： 思い出せず，メモも見つかりませんでした…
対策： ``journald``コマンドや``systemctl status``コマンドの最後にかかれているログに吐かれているメッセージから，``MYSQLD_OPTS``の環境変数をエクスポートしたがあまり関係なく… ログをもう一度掘り返して見てみると，インストールし直して成功したように見えます．

```:実行していたコマンド
sudo dnf install mysql84-community-release-fc39-1.noarch.rpm
```
※必要に応じてyumやapt-getにかえたり，バージョン名を変えてみてください．
もし違っていたらすいません．`/etc/my.cnf`や`systemctl cat mysqld.service`などで各種ファイルを見たり，他の方法を調べて試してみてください．

2.**作成したユーザでログインを試みるものの，500番エラーが出る**（下記はイメージ）

![Screenshot from 2024-05-04 12-26-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2918716/7f4b8b02-28fe-f299-8cbd-c36d5755c1a0.png)

``` :Before
2024-05-06 14:20:08.202  WARN 138047 --- [nio-8080-exec-7] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1146, SQLState: 42S02
2024-05-06 14:20:08.203 ERROR 138047 --- [nio-8080-exec-7] o.h.engine.jdbc.spi.SqlExceptionHelper   : Table '<Tableの名前>' doesn't exist
2024-05-06 14:20:08.207 ERROR 138047 --- [nio-8080-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet] with root cause
```

原因： エラーを見てみるとテーブルが一部足りないとのこと．
対策： `CREATE TABLE`を実行して必要なすべてのテーブルを作成．データ型もアプリに合わせて定義．
気づき： 意識せずにコーディングしていたが，ログインが終わったあとすべてのDBを読み込む仕様になっていた！

# 実装を終えて（所感）
DBのテーブルを作成するSQLは，ローカルに構築するときも楽ちんになるのでファイルにしてGitにあげておくのが良いと思いました．また`アプリケーション編`でも書いたように，アプリとDBはDB接続やテーブル・変数定義で大きく影響し密接に繋がっているので，変数の型や特性をきちんと把握して整合性を担保することが重要なんだなと改めて実感しました（なにか粗結合にできるツールないのかな）

お読みいただき，ありがとうございました！