AWS FargateにEFSをマウントしてRocket.Chatを移行する

# はじめに
「大人の事情でSlackが使えない」という話をよく聞きます、うちの会社も例外ではなく同じ悩みを抱えており[Rocket.Chat](https://rocket.chat/)を使っています。[前回](https://qiita.com/katuemon/items/d99b802427d682378bd7/)は、AWS EC2にパッケージインストールしていたものをDocker Containersへ移行した。[先日（Apr 8,2020）](https://aws.amazon.com/jp/about-aws/whats-new/2020/04/aws-fargate-launches-platform-version-14/)Fargateのプラットフォームバージョン1.4がリリースされ、コンテナにEFSをマウント出来る（EFSエンドポイントをサポート）様になったので思い切ってFargateに移行してみた。

```
Fargate タスクが Amazon Elastic File System (EFS) エンドポイントのサポートを開始
プラットフォームバージョン 1.4.0 を使用して、永続的な EFS ストレージで Fargate タスクを起動できるようになりました。この新機能は、Fargate タスク内に EFS ボリュームをマウントすることで、データの永続性と共有ストレージを必要とするアプリケーションを有効化します。お客様は、コンテンツ管理システム (WordPress や Drupal など) や共通のデータセットを共有するアプリケーションなどのアプリケーションを Fargate に移行できるようになりました。
```

# モチベーション
* 個人的な趣味と学習のため。
* 社員が何気なく使っている環境でコッソリ遊んで反応を見てやるぜ。
* もう俺しか運用出来なくなるけど、どうなっても知らないぜ。

# Rocket.Chatの構成

* 利用用途
社員（約５０名）のコミュニケーションツール。

* 移行前（Docker on EC2）
[internet]--[EC2(Docker)]

* 移行後（Fargate）
[internet]--[ALB]--[Fargate(Container)]

# 移行の流れ
1. Fargeteクラスター作成
2. セキュリティグループ作成
3. ALB作成
4. EFSファイルシステムを作成
5. タスク定義を作成
6. データ移行
7. DNSの登録（ここから移行前のRocket.Chatは停止)
8. サービスの作成と実行
9. 起動確認

# 移行手順
### 1. Fargeteクラスター作成
後でもいいけど、はやる気持ちを押さえきれないので、先ずはFargateクラスターを作成。

ⅰ）Fargateコンソールから作成
移行作業を楽にするため移行前のRcoket.Chatと同じリージョンで。
<img width="763" alt="スクリーンショット 2020-05-10 7.56.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/f7506d5e-e015-0618-64ad-fd9878d65d56.png">
ⅱ）クラスターテンプレートの作成
「ネットワーキングのみ（AWS Fargate を使用）」を選択
<img width="1004" alt="スクリーンショット 2020-05-10 8.11.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/f2188445-5904-b2b6-445b-cbbacc9bf42c.png">
ⅲ）クラスターの設定
適当なクラスター名を入力。
今回は「VPCの作成」のチェックは外したまま。
（移行じゃ無くて新規にRocket.Chatを構築する場合はこの手順で作成しておいた方が楽かも）
「CloudWatch Container Insights」は有効にしておく。
<img width="987" alt="スクリーンショット 2020-05-10 8.24.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/1be65462-cfa8-0fa0-40dc-c1ae423a19fc.png">
ⅳ）クラスターの作成完了
<img width="824" alt="スクリーンショット 2020-05-10 8.34.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/44487620-e4d6-a7d2-3233-a0daf261856a.png">

### 2. セキュリティグループ作成
今回構築する環境に適用するセキュリティグループを3つ作成しておく。

ⅰ）rocketchat-alb（ALBに適用するグループ：インターネット公開用）
検証だけならHTTP(TCP/80)でも良いかも。
ソースは全ての送信元から許可。
<img width="1226" alt="スクリーンショット 2020-05-10 9.18.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/ef61fe84-3e1c-3c8c-693f-16c08b1bf9bc.png">
ⅱ）rocketchat-fargate（Fargateに適用するグループ：ALBからの振分け用）
ソースは「rocketchat-alb」セキュリティグループから許可。
<img width="1226" alt="スクリーンショット 2020-05-10 9.27.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/375f6077-1565-4047-de27-8fe6908fe0a1.png">
ⅲ）rocketchat-efs（EFSに適用するグループ：Fargateからのマウント用）
ソースは「rocketchat-fargate」セキュリティグループから許可。
<img width="1228" alt="スクリーンショット 2020-05-10 9.29.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/bf504ebe-40b3-6883-08f7-82910b67ee75.png">

### 3. ALB作成
コンテナにはパブリックIP割当てが出来るので、そのままでもインターネットからアクセス可能。
でも、EIP（固定）は設定できないのでコンテナ起動の度にパブリックIPは都度変わるのでロードバランサーを使う。なお、Fargateは、ALBおよびNLBのみサポートとのこと。（詳しくは[ココ](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/AWS_Fargate.html)）

ⅰ）EC2コンソールから作成
<img width="1026" alt="スクリーンショット 2020-05-10 9.54.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/0aaa2fbb-a4f9-e18d-32be-7e10a1653d46.png">
ⅱ）Application Load Balancerを選択
<img width="1353" alt="スクリーンショット 2020-05-10 9.56.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/16658a52-b071-3e39-3923-23e866f9fcab.png">
ⅲ）ロードバランサーの設定
入力項目は環境に応じていい感じに設定。
「ロードバランサーのプロトコル」は「rocketchat-alb」セキュリティグループで設定したポート。
<img width="1360" alt="スクリーンショット 2020-05-10 10.09.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/cb0069a3-db3d-fcbf-137d-e9e0b637a412.png">
VPCやアベイラビリティーゾーン（サブネット）は移行前のRcoket.Chatと同じもの。
<img width="1350" alt="スクリーンショット 2020-05-10 10.53.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/05e4e658-7ff7-d8c5-61ca-834e4ef28495.png">
ⅳ）セキュリティ設定の構成
リスナーにHTTPSを選択した場合はサーバ証明書の選択が必要なので、あらかじめACMで作成しておいた証明書を使う。HTTPなら必要ないかもしれないけど、セキュリティ警告メッセージが出たりする。セキュリティポリシーは社内システムなので適当に最強にしておいた。
<img width="1353" alt="スクリーンショット 2020-05-10 10.54.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/c64aebfa-987b-3e87-c0ba-8e17de565701.png">
ⅴ）セキュリティグループの設定
前の手順で作成しておいた「rocketchat-alb」を選択。
<img width="1352" alt="スクリーンショット 2020-05-10 10.55.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/fcd2e5a3-6c0f-3025-cba1-48b02eafc8f8.png">
ⅵ）ルーティングの設定
入力項目は環境に応じていい感じに設定すればいいけど、ターゲットの種類は「IP」にする必要がある。
ここはちょっとハマった。（詳しくは[ココ](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/AWS_Fargate.html)）
<img width="1351" alt="スクリーンショット 2020-05-10 10.56.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/86dee8ca-5da5-02d5-54f5-ed1276f75fca.png">
ⅶ）ターゲットの登録
ここでは何もしない。
<img width="1352" alt="スクリーンショット 2020-05-10 10.57.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/4dcbfe4e-c424-1742-d445-5b438b363f21.png">
ⅷ）確認
<img width="1350" alt="スクリーンショット 2020-05-10 10.57.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/f16d64cf-9673-df4c-dcef-802b2425bd57.png">
ⅸ）作成完了
<img width="1350" alt="スクリーンショット 2020-05-10 10.58.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/bf614536-6ad0-47c7-58f8-107c18daa4f5.png">

### 4. EFSファイルシステムを作成
以下の手順にて、アップロードしたファイルの保存用、MongoDBのデータ用の2個を作成しておく。

ⅰ）EFSコンソールからEFSを作成
<img width="1355" alt="スクリーンショット 2020-05-10 11.53.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/6c299bc4-a352-e113-6411-7d1b6a7f14a8.png">
ⅱ）ネットワークアクセスを設定する
VPCやアベイラビリティーゾーン（サブネット）は移行前のRcoket.Chatと同じもの。セキュリティグループは前の手順で作成しておいた「rocketchat-efs」を選択。
<img width="1331" alt="スクリーンショット 2020-05-10 11.59.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/6c6e1d87-35b9-b9b8-1329-859816c52edd.png">
ⅲ）ファイルシステムの設定を行う
環境や好みに応じて設定。

ⅳ）クライアントアクセスを設定
環境や好みに応じて設定。

ⅴ）作成完了
<img width="1340" alt="スクリーンショット 2020-05-10 12.08.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/ca6f631d-f24a-c6e6-8450-fecf7f808971.png">

### 5. タスク定義を作成
ⅰ）Fargateコンソールから作成
<img width="792" alt="スクリーンショット 2020-05-10 12.13.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/16757b3a-2cf3-89ce-2b69-c947933babb8.png">
ⅱ）起動タイプの選択
「FARGATE」を選択
<img width="1206" alt="スクリーンショット 2020-05-10 13.02.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/59371a1a-c3f9-fdf3-637c-2104de41ab26.png">
ⅲ）タスクとコンテナ定義の設定
タスク定義名：適当
タスクロール：「なし」or「ecsTaskExecutionRole」
タスクの実行ロール：「ecsTaskExecutionRole」
タスクメモリ（GB）：環境に応じて選択（0.5GBではメモリ不足のため起動しなかった気がする）
タスクCPU（vCPU）：環境に応じて選択

なお、ecsTaskExecutionRoleが存在しない場合は[こちら](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_execution_IAM_role.html)で作成しておく。　
<img width="888" alt="スクリーンショット 2020-05-10 13.05.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/85e3df31-3f51-f2e3-a00d-e9880345678e.png">
<img width="877" alt="スクリーンショット 2020-05-10 13.05.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/cd1cc384-e17e-4465-de0c-06a1c2bbdc03.png">
ⅳ）ボリュームの設定
コンテナの追加の前に一旦画面を下にスクロールしボリュームを追加しておく。
この作業をしておかないとコンテナからEFSをマウントできない。（2020/5/10:時点）
どうでもいいけど、AWSのUIってこう言うところ良くあるよね、結構気になります。
<img width="879" alt="スクリーンショット 2020-05-10 13.08.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/a88ff2d5-b765-7d45-92fd-05d3d3efb69e.png">
以下の手順にて、アップロードしたファイルの保存用、MongoDBのデータ用の2個を追加しておく。暗号化や認証は環境や好みに応じて設定。

* アップロードしたファイルの保存用
<img width="782" alt="スクリーンショット 2020-05-10 13.23.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/e6858a81-6eb2-e191-fafd-f34e39bc0302.png">
* MongoDBのデータ用
<img width="766" alt="スクリーンショット 2020-05-10 13.26.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/781312fc-c169-723d-6e4c-89efc70117dc.png">

ⅴ）コンテナの追加
Rocket.Chatには3つのコンテナが必要です。
それぞれの設定内容（docker-compose.ymlとの相違点、ハマったポイント）を記載しておきます。

* rocketchat
 
コンテナ起動時の実行コマンドの書式が違うので、docker-compose.ymlからの単純コピペではエラーとなる。また、Docker Link機能が使えないので環境変数に定義するMONGOのURLはコンテナ名じゃなくて、127.0.0.1（localhost）を指定する必要がある。（詳しくは[ココ](https://awsexchage.tumblr.com/post/169571290481/aws-fargateでコンテナ間通信させたいとき-httpifttt2d3tapx)）。
<img width="1056" alt="スクリーンショット 2020-05-10 13.42.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/14d4b2be-7d01-b518-2dd9-5beedd1e1c9d.png">

```bash:実行コマンド（「JSONによる設定」コピペ用）
"command": [
  "bash",
  "-c",
  "for i in `seq 1 30`; do node main.js && s=$? && break || s=$?; echo \"Tried $i times. Waiting 5 secs...\"; sleep 5; done; (exit $s)"
],
```
* mongo

こちらは実行コマンドの書式が違うくらい。
<img width="1057" alt="スクリーンショット 2020-05-10 14.10.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/c40dcf66-d4aa-446e-2a8e-bc456418fd46.png">

```bash:実行コマンド（「JSONによる設定」コピペ用）
"command": [
  "mongod",
  "--smallfiles",
  "--oplogSize",
  "128",
  "--replSet",
  "rs0",
  "--storageEngine=mmapv1"
],
```
* mongo-init-replica

コンテナの「環境」設定の「基本」パラメータのチェックを外しておく（false）。このコンテナは起動後すぐに停止するので、チェックが入っている（true）場合はコンテナの停止によりタスクも停止します。また、rocketchatコンテナの時と同じく、Docker Link機能が使えないのでコマンドに定義するmongoはコンテナ名じゃなくて、127.0.0.1（localhost）を指定する必要がある。あとは他と同じで実行コマンドの書式が違うところ。
<img width="1055" alt="スクリーンショット 2020-05-10 14.18.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/adf6f903-8057-4721-b3cf-bde7d22b4036.png">

```bash:実行コマンド（「JSONによる設定」コピペ用）
"command": [
  "bash",
  "-c",
  "for i in `seq 1 30`; do mongo 127.0.0.1/rocketchat --eval \"rs.initiate({ _id: 'rs0', members: [ { _id: 0, host: '127.0.0.1:27017' } ]})\" && s=$? && break || s=$?; echo \"Tried $i times. Waiting 5 secs...\"; sleep 5; done; (exit $s)"
],
```

### 6. データ移行
データ移行のやり方はいろいろあるけど、今回は移行前のRcoket.Chat（EC2）と同じVPC内に構築したので以下のやり方で実施。（新規にRocket.Chatを構築する場合は不要）

ⅰ）EFSに対し移行前のRcoket.Chat（EC2）からのマウントを許可する
EFSコンソールから「4. EFSファイルシステムを作成」で作成した2個それぞれのEFSの「ファイルシステムアクセスの管理」を選択。
<img width="753" alt="スクリーンショット 2020-05-10 17.53.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/dd806a7f-524e-e2ee-fbd6-1827fae31459.png">
セキュリティグループに移行前のRcoket.Chat（EC2）のセキュリティグループを追加する。今回の環境では「sg-00da03d099b4baf25 - default」です。 
<img width="1155" alt="スクリーンショット 2020-05-10 17.54.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/687d408c-af88-1f93-08c7-621381965402.png">
ⅱ）移行前のRcoket.Chat（EC2）にログイン
ⅲ）2個のEFSをマウント
手順は以下を参考。
<img width="950" alt="スクリーンショット 2020-05-10 18.02.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/a30fe73b-a586-266b-d716-0494ec08fff2.png">
ⅳ）dockerを停止（sudo docker-compose down）
ⅴ）「./data/db」を「＜マウントポイント＞/data/db」に丸ごとコピー
ⅵ）「./uploads」を「＜マウントポイント＞/app/uploads」に丸ごとコピー
ⅶ）2個のEFSをマウント解除
ⅷ）ⅰ）で追加したセキュリティグループを削除

### 7. DNSの登録
作成したALBのDNS名をRocket.ChatドメインのCNANEとして登録しておきます。なお、Rocket.ChatのドメインをAmazon Route 53で管理している場合は、ALIASレコードとして登録する方が良いです。（詳しくは[ココ](https://dev.classmethod.jp/articles/amazon-route-53-alias-records/)）。これで、ALBのパブリックIPアドレスまで名前解決できるようになるはず。

### 8. サービスの作成と実行
いよいよ最後の手順です。
ⅰ）Fargateコンソールから作成
<img width="919" alt="スクリーンショット 2020-05-10 18.05.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/0a0deea6-2311-abb7-7760-417a6f52a058.png">
ⅱ）サービスの設定
プラットフォームのバージョンは明示的に「1.4.0」としなければならないようです。（2020/5/10:時点）後は、環境や好みに合わせて設定。
<img width="732" alt="スクリーンショット 2020-05-10 18.10.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/9f5eaab9-11fa-d993-9a17-fbfe271e0822.png">
ⅲ）ネットワーク構成
VPCやアベイラビリティーゾーン（サブネット）は移行前のRcoket.Chatと同じもの。セキュリティグループは前の手順で作成しておいた「rocketchat-fargate」を選択。パブリックIPの自動割り当ては「ENABLED」としておく。
<img width="602" alt="スクリーンショット 2020-05-10 18.13.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/f06e746d-ba15-03d2-ea4a-58f12fd0d8ab.png">
ヘルスチェックの猶予期間は「300」秒とした。
<img width="715" alt="スクリーンショット 2020-05-10 18.16.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/a1a16ddc-17f5-ec24-f1d5-a5520c1063be.png">
ロードバランサーの種類は「Application Load Balancer」を選択。ロードバランサー名は「RocketChat-ALB」（「3. ALB作成」で作成したALB名）
<img width="726" alt="スクリーンショット 2020-05-10 18.18.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/6cba83f3-582c-2c1c-9286-fb8480010ced.png">
ターゲットグループも「3. ALB作成」で作成したALB名としておく。
<img width="728" alt="スクリーンショット 2020-05-10 18.21.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/24293ec0-484a-5cd5-f775-91413b47aeb4.png">
Service Auto Scalingは「サービスの必要数を直接調整しない」を選択。
<img width="739" alt="スクリーンショット 2020-05-10 18.23.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/53d318b1-4a3d-61d6-e4e2-919384f53dd8.png">
確認画面を経て、正常にサービスが作成された。
<img width="831" alt="スクリーンショット 2020-05-10 18.25.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/b96021be-3adc-6c2e-ca74-df72f9167847.png">

### 9.起動確認
起動後、しばらくするとコンテナのステータスが以下のようになる。
<img width="1144" alt="スクリーンショット 2020-05-10 18.28.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/47bd2d1d-9256-f167-b8d1-f67cf5899bea.png">
「Logs」タブにて3個のコンテナのログを確認。おおよそ、以下のようなログが出力されているはず。

* rocketchat
<img width="758" alt="スクリーンショット 2020-05-10 18.43.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/7f48ba1f-3f02-0f1d-b87a-3453087a844e.png">
* mongo-init-replica
<img width="758" alt="スクリーンショット 2020-05-10 18.32.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/9a4517b1-92c0-e86b-fa4c-10362404f0fb.png">
* mongo
<img width="1144" alt="スクリーンショット 2020-05-10 18.33.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/422694df-66c2-9f9c-1895-f72f9e0caac5.png">
後はブラウザでアクセスしてログイン画面が表示されればOK。
<img width="636" alt="スクリーンショット 2020-05-10 18.47.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317297/4ae35151-d30d-5b85-b52e-dd9c66e1f3e5.png">

# 最後に
* ハマりにハマったが何とか移行できた。
* リソースを100%コンテナに割当てられるのでコストはトントンか？
* 信頼性についてはこれから検証。
* EFSをマウントすると「CloudWatch Container Insights」の値が取得できない。（2020/5/10:時点）

おしまい。