AWS上でRedmineを構築して育てていく話 Part1

前回がCloudwatch AgentをPCに入れるという変化球だったのですが、
地に足を着ける時間として基本的なことをアウトプットしたくなったので書いてます。

# 今回やること
内容はRedmineを一旦AWSで構築した後で、
きちんとhttpsでのアクセスだったりRDSへ移行していく話です。
IaC化などはまた別途、勉強がてら育てます。

# 実施作業
ここからは実施作業について書いていきます。

## IAMロールの作成
EC2に割り当てる用のIAMロールを作成します。
ポリシー`AmazonSSMManagedInstanceCore`が割り当てられたロールを作成します。
理由は口述しますが、SSM Session Managerでの接続がしたいからです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/ba730a0b-041e-e634-fbce-c6f0af2da11c.png)

## EC2デプロイ
RedmineのEC2をデプロイします。

EC2の起動画面からmarketplaceでRedmineを選択します。
バージョン 5.0.5-19-r30 on Debian 11
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/c4f8283b-608e-eec7-53e2-c993d60764a0.png)

IAMロールは先ほど作成したものを使いましょう。

ユーザデータを以下の通りに記載しておくことで初回起動時にSSM Agentがインストールされ、
SSM Session ManagerからEC2へログインできます。
SSHポートを開ける必要がなく、SSH鍵の管理が不要になります。

```bash
Content-Type: multipart/mixed; boundary="==BOUNDARY=="
MIME-Version: 1.0

--==BOUNDARY==
Content-Type: text/x-shellscript; charset="us-ascii"

#!/bin/bash
echo "running userdata script"

# SSM Agent のインストール
curl "https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/debian_amd64/amazon-ssm-agent.deb" -o "amazon-ssm-agent.deb"
dpkg -i amazon-ssm-agent.deb

# SSM Agent の設定
echo "rebooting to start SSM Agent"
nohup sudo systemctl restart amazon-ssm-agent &

--==BOUNDARY==--
```

起動後はシステムログからユーザ名とパスワードを取得します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/caae8a59-b2cc-844e-7992-f0519567ea29.png)

```bash
[   59.621532] device-mapper: ioctl: 4.43.0-ioctl (2020-10-01) initialised: dm-devel@redhat.com
[   59.308394] bitnami[587]: #########################################################################
[   59.314250] bitnami[587]: #                                                                       #
[   59.320008] bitnami[587]: #        Setting Bitnami application password to 'ここにパスワード'         #
[   59.324419] bitnami[587]: #        (the default application username is 'user')                   #
[   59.328632] bitnami[587]: #                                                                       #
[   59.333957] bitnami[587]: #########################################################################
```
:::note info
ここで出てこない場合は後にSSM Session Managerでの接続を行って確認するのでご安心を
:::

これでパブリックIPをブラウザから打鍵してユーザ名とパスワードを打てばまずは使えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/d83fce53-c911-012f-c3b4-34011ac04104.png)

のちにDB移行もあるのでちょっといじっておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/1c5bb302-fc4a-9abe-8962-160217eac49f.png)

:::note info
SSM Session ManagerからEC2へログインできなかった時に確認すること
もし、インスタンス作成後にロールを割り当てそこなった場合は
先ほど作成したEC2インスタンスにロールを割り当てて再起動が必要になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/9d773170-640f-b204-b8ae-4f86ef114096.png)
:::

という訳でEC2画面の接続をやってみると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/d3528a13-14fa-1913-73e5-3715abe795a9.png)
新しいタブが開き、接続できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/b63b83a4-d148-e254-1a4e-ba2e50415836.png)
もし、先ほどRedmineのパスワードを取得できていない場合は下記のコマンドで取得できます。
```bash
sudo cat /var/log/syslog
```

## Route53でドメイン取得
https通信に証明書取得などが必要で、ドメインなどが必要になるため作成します。
Route53画面からcloudcentric.comが空いてるか確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/b5148cc2-c8ac-d392-e4c0-40bda8a57089.png)

<font color="LightSlateGray">
<details><summary>ちなみに弊社はこれです</summary><div>

　https://www.cloudcentric.co.jp/

</div></details>
</font>

検索結果です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/1c53120e-7e60-a8af-3e9b-b3bdb4218818.png)
(.ninjaって候補出ますね。。海外で人気なんでしょうか？)
選択したらチェックアウトに進みます。

完了するとRoute53のダッシュボートでドメインの登録が進行中というステータスになります。
リソースで表示されるのは先ほど選択したドメインです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/535eb0e1-5ec0-8247-86f0-04471a88c304.png)
約1時間後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/28f884d7-9a50-7476-8fd3-65d2ce7dda99.png)

## ACMの設定
AWS Certificate Managerの画面に遷移して証明書のリクエストをします。
完全修飾ドメイン名は`*.取得したドメイン名`で記載します。
詳細はガイドを貼っておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/5e07885d-e666-63cb-271c-81bdc9eae4f4.png)

https://docs.aws.amazon.com/ja_jp/acm/latest/userguide/gs-acm-request-public.html

その他項目はデフォルトのままリクエストします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/97c65c61-e860-534f-c120-5fe369c8e1f4.png)

DNSプロバイダによっては24時間ほどかかるそうです。
が、必要な作業がまだあります。

:::note info
証明書にはCNAME名とCNAME値があると思います。
これをRoute53に登録し忘れて保留中の検証ステータスが進まなかったです。
なので、次のホストゾーン修正までは終えて明日に確認した方がいいかと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/29bee96d-d525-ba07-692b-1d2579f99311.png)

また、CNAME名とCNAME値に関しては以下に記載があります。
https://docs.aws.amazon.com/ja_jp/acm/latest/userguide/dns-validation.html
:::

## Route53でホストゾーン修正
Route53にもう一度戻ります。
Route53の画面からホストゾーンを選択します。
ホストゾーンの作成で、取得したドメインを入力して作成します。
作成したホストゾーン内でレコードを作成します。


* ACMで作成したCNAME名とCNAME名を追加します。
AWS Certificate Managerの画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/75278d9c-5bce-c4c2-b959-3db8dd46f836.png)
ホストゾーンのレコード追加画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/7206c4a7-9155-203f-b80b-b39f3fac01fd.png)

* redmineのレコードを追加
redmine用のAレコード追加
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/785320f2-f3f4-af98-ac9b-f63768d4b420.png)

AレコードとCNAMEレコードの2つが追加されていることを確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/4f170f9e-64f8-b6a1-d381-66044a3dbfd1.png)

:::note info
ここまで完了するとACMのDNS検証準備が終わっているので
一日放置するか、ELB設定を飛ばしてRDS移行を行うのどちらかです。
:::

## ELB設定
EC2の画面左にあるELBを作成します。
Application Load Balancerでないと証明書を使えないようなのでALBを使用します。
セキュリティグループはALB用に作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/13962b27-55b3-9a45-dcd0-91d97464b1e0.png)

セキュリティグループとリスナー、ターゲットグループのイメージはこちらです。
だれでもアクセスされるわけにはいかないので自分のGlobalIPを許可するようにしました。
![名称未設定ファイル.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/553aa8f0-1aaf-38a7-65da-b17934e0f166.jpeg)

ターゲットグループも作成します。
既にあるEC2を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/5db00733-fadd-0b4d-f2c0-5f40b6a34625.png)

リスナーは以下の設定ですが、DNS検証が終わっていないとこの証明書を選択できないのでご注意を。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/a48b852d-9a0d-e9d0-8be2-627f0cbc02f3.png)

1ruleとあるリンクに飛びます。
ルールを挿入します。
Route53で作成するELBのAレコードを記載します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/b65d414b-a0f1-e335-ddda-786f5166363d.png)
追加すると以下の状態になるかと思います。
これでHTTPホストヘッダーが特定の条件の時にEC2インスタンスへ、
その他の場合はHTTPステータスが503でエラーを返すような設定です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/01f0d509-c945-dfda-6618-051990b1a14b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/9fdb442f-f867-adcd-b147-ad1e8288b945.png)


## RDSへの移行
現状、EC2内部のDBを使っているのでオートスケールができない状態です。
RDSへ移行してWeb層とDB層を分けようと思います。
やることはざっくり以下です。
* RDSをデプロイ
* IMAロールを修正
* EC2インスタンス内部にあるDB情報をファイルへエクスポートする
* RDSに接続してエクスポートしたファイルをインポートする
* EC2インスタンスへログインして`/opt/bitnami/redmine/config/database.yml`でDBの接続先を設定する

また、RDSのパスワードをSystemManagerで管理してパスワードを定期変更できるようにしたかったです。
が、環境変数を`/opt/bitnami/redmine/config/database.yml`内で読み込むことがうまくできませんでした。
そのため、database.yml内にパスワードをべた書きしたままになってしまいました。。

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/rds-secrets-manager.html

### RDSをデプロイ
まずはRDS画面の左側からサブネットグループを作成します。
RDSの配置先で使うのでプライベートサブネットを指定して作成します。

次にSSM Session ManagerでEC2インスタンスへに入ってDB情報を確認します。
```bash
root@(略):/usr/bin# mysql --version
mysql  Ver 15.1 Distrib 10.11.4-MariaDB, for Linux (x86_64) using readline 5.1
root@(略):/usr/bin#
```
`Ver 15.1 Distrib 10.11.4-MariaDB`とあるのでRDSはMariaDBでいきます。
作成時に下記にチェックを入れるとSecretManagerでパスワードが自動ローテーションできます。
前述の通り、環境変数に埋め込むことはできましたがdatabase.ymlで読み込むことができなかったため
現状はローテーションをオフにしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/23180ea6-7fbc-9b09-1c8b-3999e5c1b6f4.png)
その他設定は大体以下で設定しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/a196e722-4d74-cab4-5de3-2a0ca3d5976d.png)


### IMAロールを修正
RDSの設定でIAMDB認証を有効にしているのでEC2インスタンスプロファイルにアタッチしているIAMロールを変更します。
追加したポリシーは`AmazonRDSFullAccess`と`SecretsManagerReadWrite`と`AWSKeyManagementServicePowerUser`にしました。
KMSが突然出てきたように感じますが、EC2からSecretManagerに格納されているパスワードを取得するなら必要です。
また、先に言っておくと`SecretsManagerReadWrite`と`AWSKeyManagementServicePowerUser`を実際に生かすに至ってないので`AmazonRDSFullAccess`だけよいです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/6bb31d03-3016-7adb-6c8e-c4596c9f66d0.png)

### EC2インスタンス内部にあるDB情報をファイルへエクスポートする
SSM Session ManagerでEC2インスタンスへログインしてDB情報を外に出します。
その上でEC2インスタンスにあるDBのパスワードが必要です。
`cat /opt/bitnami/redmine/config/database.yml | grep password`で取得します。
```bash
root@(略):/usr/bin# cat /opt/bitnami/redmine/config/database.yml | grep password
  password: "EC2インスタンス内のDBパスワード"
  password: ""
  password: ""
#  password: "postgres"
#  password: jenkins
root@(略):/usr/bin#
```
ユーザもdatabase.ymlに記載あるのでそれを採用してDBの情報を取り出します。
```bash
root@(略):/usr/bin# mysqldump -u bn_redmine -p bitnami_redmine > redmine_bakup.sql
Enter password:
root@(略):/usr/bin# ls -l redmine*
-rw-r--r-- 1 root root 74466 Jun 11 09:08 redmine_bakup.sql
root@(略):/usr/bin#
```
これでDB情報の取得完了です。

### RDSに接続してエクスポートしたファイルをインポートする
まずはEC2インスタンスからRDS接続確認します。
RDSへ接続するユーザとパスワードはSecretManagerに連携する設定で作成したのでそちらから確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/594e83b5-c975-a92d-dcaf-dbc4d371bce4.png)

その情報をもとに接続をしてredmineというDBを作ってみます。
(ここのDB名はRDSに付けた名前とは関係ないです)
```bash
root@(略):/usr/bin# mysql -u admin -p -h (RDSのエンドポイント)
Enter password:
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 70
Server version: 10.6.10-MariaDB-log managed by https://aws.amazon.com/rds/

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> CREATE DATABASE redmine;
Query OK, 1 row affected (0.003 sec)

MariaDB [(none)]> USE redmine
Database changed
MariaDB [redmine]> SHOW TABLES;
Empty set (0.001 sec)

MariaDB [redmine]> exit
Bye
root@(略):/usr/bin#
```
これでインポートしてみます。
```bash
root@(略):/usr/bin# mysql -u admin -p -h (RDSのエンドポイント) redmine < redmine_bakup.sql
Enter password:
root@(略):/usr/bin# echo $?
0
root@(略):/usr/bin#
```
DBに接続して確認してみます。
```bash
MariaDB [(none)]> USE redmine;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [redmine]> SHOW TABLES;
+---------------------------+
| Tables_in_redmine         |
+---------------------------+
| ar_internal_metadata      |
| attachments               |
| auth_sources              |
| boards                    |
| changes                   |
| changeset_parents         |
| changesets                |
| changesets_issues         |
| comments                  |
| custom_field_enumerations |
| custom_fields             |
| custom_fields_projects    |
| custom_fields_roles       |
| custom_fields_trackers    |
| custom_values             |
| documents                 |
| email_addresses           |
| enabled_modules           |
| enumerations              |
| groups_users              |
| import_items              |
| imports                   |
| issue_categories          |
| issue_relations           |
| issue_statuses            |
| issues                    |
| journal_details           |
| journals                  |
| member_roles              |
| members                   |
| messages                  |
| news                      |
| projects                  |
| projects_trackers         |
| queries                   |
| queries_roles             |
| repositories              |
| roles                     |
| roles_managed_roles       |
| schema_migrations         |
| settings                  |
| time_entries              |
| tokens                    |
| trackers                  |
| user_preferences          |
| users                     |
| versions                  |
| watchers                  |
| wiki_content_versions     |
| wiki_contents             |
| wiki_pages                |
| wiki_redirects            |
| wikis                     |
| workflows                 |
+---------------------------+
54 rows in set (0.001 sec)

MariaDB [redmine]>
```

がっつりインポートしてそうですね!

### EC2インスタンスのdatabase.ymlでDBの接続先を設定する
`/opt/bitnami/redmine/config/database.yml`の中身を修正します。
直す部分を切り出すと以下です。
```bash
production:
  database: redmine
  host: (RDSのエンドポイント)
  username: admin
  password: "SecretManagerで格納したパスワード"
```
修正が完了したらEC2を再起動して打鍵してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/f2716608-3d51-1c8a-875c-a1df1842a23a.png)

そして、RDSを停止してから打鍵してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/165ac121-4e40-a3b8-06ac-d7df4342601c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3330185/bccf88a0-eb8d-d41d-257b-834884d2f8ec.png)

という訳でパスワードべた書きのままになっていますがRDS化もできました。

## 番外編
ここからはうまくいかなかった部分の話です。
やりたいことは以下です。
* SecretManagerでパスワードローテーションが走る
* EC2のユーザーデータで起動ごとにSecretManagerからパスワードを取得して環境変数として`/etc/environment`へ格納
* database.ymlの中で環境変数としてパスワードを呼び出す


実際に以下のユーザーデータを記載しました。
`#!/bin/bash`の前に書いてある部分は再起動ごとにユーザデータを実行してもらうためのおまじないです。
```bash
Content-Type: multipart/mixed; boundary="//"
MIME-Version: 1.0

--//
Content-Type: text/cloud-config; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="cloud-config.txt"

#cloud-config
cloud_final_modules:

[scripts-user, always]
--//
Content-Type: text/x-shellscript; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="userdata.txt"

#!/bin/bash
sudo apt -y update ## jsonファイルをjqコマンドで成形するためにjqコマンドインストールを行う
sudo apt -y install jq ## jsonファイルをjqコマンドで成形するためにjqコマンドインストールを行う
rm -f /etc/environment ## environmentファイルを起動ごとに最新化したいのでいったん削除
touch /etc/environment ## environmentファイルのからファイル作成
chmod 744 /etc/environment ## environmentファイルの権限を元通りに
chown root:root /etc/environment ## environmentファイルの権限を元通りに
echo "export user="$(aws secretsmanager get-secret-value --secret-id '(SecretManagerのシークレットの値)' | jq .SecretString | jq fromjson | jq -r .username)"" >> /etc/environment ## SecretManagerから取得してそのまま環境変数設定ファイルに格納
aws secretsmanager get-secret-value --secret-id '(SecretManagerのシークレットの値)' | jq .SecretString | jq fromjson | jq -r .password ## SecretManagerからパスワードを取得(特殊文字があるので一旦取得だけ)
echo "export password=$pass" >> /etc/environment ## SecretManagerから取得したパスワードを環境変数設定ファイルに格納
source /etc/environment ## enviromentファイルを再読み込み
```

database.ymlは下記の要領で記載すると環境変数を読み込んでくれるとブログやChatGPTが教えてくれます。
が、色々試してもあまりうまくいきませんでした。
```bash
password: <%= ENV[`環境変数名`]>
```
また、下記を実行してもきちんと出力はされたので環境変数としての格納には問題なさそうです。
何が違うのか次回までに見つけたいです。。
```bash
echo $pass
```

以上です！