Database Migration ServiceでAuroraからRedshiftへ継続的なマイグレーションを行う

## はじめに

Database Migration Service（以降、DMS）とは、AWSが提供する
DatabaseをMigrationするServiceです。


 - [AWS Database Migration Service とは - AWS Database Migration Service](http://docs.aws.amazon.com/ja_jp/dms/latest/userguide/Welcome.html)
 - [よくある質問 - AWS Database Migration Service | AWS](https://aws.amazon.com/jp/dms/faqs/)
 - [AWS Database Migration Serviceを使ったMySQLからRedshiftへのレプリケーション手順 まとめ ｜ Developers.IO](http://dev.classmethod.jp/cloud/aws/dms-mysql2redshift-replication/)

今回やりたかったのは、RDSにあるAuroraの特定のテーブルから、Redshiftへ継続的にマイグレーションを行い、擬似的なレプリケーションを実現させるというタスクでした。

## 設定

AWSのマネジメントコンソールから、Database Migration Serviceを選択して設定を始めます。
基礎知識として、Database Migration Serviceを使ってAurora to Redshiftのマイグレーションを行う際は、

[RDS] ---> [EC2] ---> [S3(csv)] ---> [Redshift]

という流れでデータの連携が行われます。
ここで利用されるEC2インスタンスは「レプリケーションインスタンス」と呼ばれ、
マイグレーションに必要な処理を良しなに実行してくれる機能を内包しているみたいです。

### Endpoints

マイグレーション元やマイグレーション先のDBへの接続情報を定義します。

#### マイグレーション元のDB設定（Aurora）

最初に、マイグレーション元のDBに関して設定してみます。

|         項目        |                    設定                    |
|---------------------|--------------------------------------------|
| Endpoint identifier | aurora-db                                  |
| Endpoint type       | Source                                     |
| Source engine       | Aurora                                     |
| Server name         | master-cluster.xxxx.xxxx.rds.amazonaws.com |
| Port                | 3306                                       |
| SSL mode            | none                                       |
| User name           | xxxx                                       |
| Password            | xxxx                                       |

**Endpoint identifier**

ここにはこのEndpoint設定の名前を設定します。何でも良いです。

**Server name**

データベースのhost名です。この設定値は、
「レプリケーションインスタンス」から接続される想定の値なので、
例えばレプリケーションインスタンスを同じVPC内に配置するのであれば、
PrivateIPでも繋げますし、ネットワーク構成にもよりますが
デフォルトのエンドポイントなども利用できると思います。


また、ここで指定するAuroraのDBは、

 - [MySQL 互換データベースの AWS Database Migration Service のソースとしての使用 - AWS Database Migration Service](http://docs.aws.amazon.com/ja_jp/dms/latest/userguide/CHAP_Source.MySQL.html)

に従ってバイナリーログの設定が行われている事が前提になります。

**User name, Password**

ここで指定するMySQLユーザーは、

 - [Amazon RDS の外部で実行される MySQL または MariaDB インスタンスとのレプリケーション - Amazon Relational Database Service](http://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/MySQL.Procedural.Importing.External.Repl.html)

に従って作られた権限を持つユーザーを指定する必要があります。

```
GRANT REPLICATION CLIENT,REPLICATION SLAVE,SELECT ON . TO 'db_migrator'@'%' IDENTIFIED BY PASSWORD '*****'
```

みたいな感じです。

#### マイグレーション先のDB設定（Redshift）

次に、マイグレーション先のDBに関して設定してみます。

|         項目        |                        設定                       |
|---------------------|---------------------------------------------------|
| Endpoint identifier | redshift-db                                       |
| Endpoint type       | Target                                            |
| Source engine       | redshift                                          |
| Server name         | redshift-cluster.xxxx.xxxx.redshift.amazonaws.com |
| Port                | 5439                                              |
| SSL mode            | none                                              |
| User name           | xxxx                                              |
| Password            | xxxx                                              |
| Database name       | dwh                                               |

**Database name**

Redshiftのデータベース名を指定します。

#### The cluster's region is local and the current region ... って言われる

VPC内向けのホスト名（例えばredshift-db.xxxx.localみたいなやつ）を指定した場合に、「Run Test」で、

```
AWSDatabaseMigrationService: The redshift cluster redshift-db needs to be in the same region as the current region. The cluster's region is local and the current region is ap-northeast-1.
```

と言われる事がありました。絶対繋げるはずなのでおかしいとおもい、試行錯誤した結果、

 1. Server nameを```redshift-cluster.xxxx.xxxx.redshift.amazonaws.com```みたいな外から繋げるホスト名に設定する
 2. 「Run Test」を実行　→　ここで繋げる事を確認
 3. 「Create Endpoint」で設定を保存
 4. Endpoint一覧から↑の設定を選択して「Modify」
 5. Server nameを```redshift-db.xxxx.local```に変更してRun Testする
 6. 接続に成功する
 
という手順で設定することができました。


### Replication instances

そして、マイグレーション処理を実行するレプリケーションインスタンスを設定します。

|          項目          |         設定         |
|------------------------|----------------------|
| Name                   | replication-instance |
| Instance class         | dms.t2.micro         |
| Multi-AZ               | No                   |
| Allocated storage (GB) | 50G                  |
| VPC Security Group(s)  | rds-group            |

**VPC Security Group(s)**

Instance classは、マイグレーション（特に継続的なもの）を行う際の
パフォーマンスに影響するので、大量のデータが短時間に追加／更新されるような
DBの場合は、インスタンスのスペックとストレージの容量を大きな値に調整してください。

**VPC Security Group(s)**

AuroraやRedshiftに接続できるセキュリティーグループを指定します。

**KMS マスターキー**

ここが若干ややこしくて、「必須」ってなってるんですが何も入力しいなくてOKです。
すると、自動的にデフォルトの暗号化キーが作成されます（aws/dms）。
IAMの暗号化キーから、アジア（東京）リージョンを指定すると表示されると思います。

### Tasks

どのDBからどのDBへ○○のテーブルを移行する、という設定の単位を「タスク」と呼ぶようです。

まずは上部で基本的な設定を行います。

|         項目         |                         設定                        |
|----------------------|-----------------------------------------------------|
| Task name            | test-task                                           |
| Replication instance | replication-instance                                |
| Source endpoint      | aurora-db                                           |
| Target endpoint      | redshift-db                                         |
| Migration type       | Migrate existing data and replicate ongoing changes |

※Migration typeには以下の３タイプがあります

 - Migrate existing data: 
     - マイグレーション先のDBにテーブルを作ってデータを一括移行する（１度だけ実行）
 - Migrate existing data and replicate ongoing changes: 
     - マイグレーション先にテーブルを作ってデータを一括移行し、元のDBが変更されるごとに同期する
 - Replicatie data changes only: 
     - 元のDBが変更されるごとに同期する

#### Task Settings

ここでどのテーブルを移行するなどの詳細な設定が行えます。

JSONタブを選び、以下をはっつけてCreate Taskします。

（jsonで設定する理由は後述）

```
{
    "rules": [
        {
            "rule-type": "selection",
            "rule-id": "1",
            "rule-name": "1",
            "object-locator": {
                "schema-name": "xxdb",
                "table-name": "product"
            },
            "rule-action": "include"
        },
        {
            "rule-type": "transformation",
            "rule-id": "2",
            "rule-name": "2",
            "rule-target": "schema",
            "object-locator": {
                "schema-name": "xxdb"
            },
            "rule-action": "rename",
            "value": "public"
        },
        {
            "rule-type": "transformation",
            "rule-id": "3",
            "rule-name": "3",
            "rule-target": "table",
            "object-locator": {
                "schema-name": "xxdb",
                "table-name": "product"
            },
            "rule-action": "rename",
            "value": "product_replica"
        }
    ]
}
```

詳細を説明すると、↑の設定は

 - Auroraのxxdbというデータベースのproductというテーブルを連携します
 - マイグレーションする際に、AuroraのxxdbというデータベースをRedshiftのpublicというスキーマに変更します
 - マイグレーションする際に、productという元のテーブル名をproduct_replicaにリネームして実行します

を意味しています。


### 実行する

Endpoints, Replication instances, Tasksの設定が完了したら、「Start」を選んで実行してみます。

うまく行けば、タスクが「Load complete」になり、継続的なレプリケーションが開始されます。



## 良い所

とにかく設定が簡単で、自前で作ろうとすると大変な異なるプラットフォーム間の継続的マイグレーション環境をすぐに立ち上げることができます。

## 悪い所

~~これはデータに依存する部分なんですが、改行がそのまま入っているカラムのマイグレーションがうまくいきませんでした。。~~
~~「そもそも改行を入れるなよ！」という話なんですが、現状すでにサービス運用が始まっているテーブルで、そのテーブルは移行できず...orz~~

こちら、私の勘違いだったようで、改行が含まれているレコードでも問題なく連携できました。
エラーの原因は、MySQLの日付カラムに「0000-00-00 00:00:00」が入っており、
それが原因で連携ができていないようでした。


## 備忘録

#### RDS to Redshiftの注意点

MySQL -> Redshiftの場合、スキーマが異なる可能性があるので、
Transformation roulesを指定して名前を変更してやる必要がある。
ここでいうMySQLのスキーマは、Database名に相当し、RedshiftのスキーマはSchemaに相当します。
なので、Redshiftのスキーマがpublicになっている場合は、
Transformation roulesを使い

 - MySQLのSchemaでMySQLのDB名
 - Redshiftのスキーマ名でRedshiftのSchema名

をそれぞれ指定してやる必要があります。


#### 同じテーブルのマイグレーションを再実行する際の注意点

タスクを再実行する際に、設定を変更せずにStartすると
前回実行済みという扱い？となってなにも処理されない事があった。
例えば、Taskの値を変更せずにデータベースの値を変えて検証したい場合は注意が必要。
特に、マイグレーション元となるテーブルがすでに
マイグレーションされた実績がある場合、
Transformation roulesをいくら変更しても、Load completeとなるもののテーブルのマイグレーションは実行されなかった。


#### 間違えて作っちゃったタスクを消すときめんどくさい

 - Creating
 - Starting
 - Running (ここでStop可能になる)
 - Stopping
 - Stopped (ここで削除できるようになる)
 - Deleting

の工程を待たないといけないのでかなり面倒。


#### Taskが失敗した時に設定が消えてしまう場合が...

あと、ERRORで失敗した場合にTaskを編集しようとしてModifyすると
設定がクリアされてしまう？ようで修正がいちいち面倒だった
面倒な場合は、テンプレートでの編集は諦めて、
jsonを直接編集する方法で設定を進めましょう。
（jsonの設定を手元にコピーしておけば、クリアされてもすぐ戻せます）