mongomirrorを用いてAWS上のMongoDBからAtlasにデータをインポートする

## 関連
MongoDB AtlasのLiveMigrationを用いるときの覚書
[AWS上のMongoDBをAtlasにLiveMigrationする](https://qiita.com/georgeOsdDev@github/items/1c46ad38a1a8ee402aed)
[NLBでTLS終端を行いAtlasのLiveMigrationを行う](https://qiita.com/georgeOsdDev@github/items/083f0d028c9ce8f834fb)

---
## 経緯
Atlas提供の、[LiveMigration](https://docs.atlas.mongodb.com/import/live-import/)を利用した場合にエラーが発生することが何回かあった。

![Screen_Shot_2019-10-07_at_4_39_19_PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4914/2190bd73-6a43-6c86-5fd8-bfdce1a8bf27.png)

LiveMigration側でエラーが発生するとユーザー的にはブラックボックスで [Troubleshooting](https://docs.atlas.mongodb.com/import/live-import-troubleshooting/)やチャットサポートでは解決策を見い出せなかったので、[mongomirror](https://docs.atlas.mongodb.com/import/mongomirror/)を用いてデータをインポートすることにした。

## 手順
### mongomirrorを動かすインスタンスを用意する

https://docs.atlas.mongodb.com/reference/mongomirror/#performance

> To avoid contention for network and CPU resources, do not run mongomirror on the same hosts that provide your replica set’s mongod instances.

> mongomirror must have network access to the source replica set.
> mongomirror must have network access to the target cluster.
> mongomirror has approximately the same performance impact on your source replica set as a secondary:
> For the initial sync stage, the load scales with the size of your data set.
> Once an initial sync completes, the load scales with oplog gigabytes used per hour.
 
EC2上のDB、およびAtlasに接続できる場所である必要がある = appサーバと同じサブネット内に、 DBサーバと同じスペックでインスタンスを作成する

#### mongomirrorのインストール

```shell

mkdir mongomirror_work
cd mongomirror_work

curl https://s3.amazonaws.com/mciuploads/mongomirror/binaries/linux/mongomirror-linux-x86_64-enterprise-amzn64-0.8.0.tgz -o mongomirror-linux-x86_64-enterprise-amzn64-0.8.0.tgz
tar -zxvf mongomirror-linux-x86_64-enterprise-amzn64-0.8.0.tgz


curl https://downloads.mongodb.org/linux/mongodb-shell-linux-x86_64-amazon-4.2.0.tgz -o mongodb-shell-linux-x86_64-amazon-4.2.0.tgz
tar -zxvf mongodb-shell-linux-x86_64-amazon-4.2.0.tgz
```

### ソース側のDB(EC2)に移行用のユーザーを作成する。

[Set up MongoDB user in the source replica set](https://docs.atlas.mongodb.com/import/mongomirror/#set-up-mongodb-user-in-the-source-replica-set)

```javascript

use admin
db.createUser(
  {
    user: "mySourceUser",
    pwd: "mySourceP@$$word",
    roles: [ "clusterMonitor", "readAnyDatabase" ]
  }
)
```

### Atlas側にAtlasAdmin権限でユーザーを作成する

[Set up MongoDB user in the target Atlas cluster](https://docs.atlas.mongodb.com/import/mongomirror/#set-up-mongodb-user-in-the-target-service-cluster)

ここで作成するユーザーは、`Save as temporary user`  にチェックを入れる

#### 疎通確認
```shell

mongodb-linux-x86_64-amazon-4.2.0/bin/mongo "mongodb+srv://<Atlasのホスト>/admin"  --username <Atlasに作成したユーザー>
```

## mongomirrorの起動

参考 https://www.slideshare.net/mongodb/mongodblocal-seattle-2019-best-practices-for-migrating-to-mongodb-atlas

```shell

nohup mongomirror-linux-x86_64-enterprise-amzn64-0.8.0/bin/mongomirror \
--host <srcのレプリカセット名>/srcのホスト1:27017,srcのホスト2:27017,srcのホスト3:27017 \
--username "<srcに作成した移行用のユーザー>" \
--password "<srcに作成した移行用のユーザーのパスワード>" \
--authenticationDatabase "admin" \
--destination "<Atlas上のレプリカセット名>/<Atlasクラスターのホスト1>:27017,<Atlasクラスターのホスト2>:27017,<Atlasクラスターのホスト3>:27017" \
--destinationUsername "<Atlas側に作成したユーザー>" \
--destinationPassword "<Atlas側に作成したユーザーのパスワード>" \
--httpStatusPort 8000 \
-j 16 > mongomirror.log 2>&1 &
```

この際、Atlas上のレプリカセット名や、接続情報の確認方法は
https://docs.atlas.mongodb.com/import/mongomirror/#copy-the-target-cluster-host-information
をよく読む。

接続情報が間違っていると

```shell

2019-10-08T05:27:30.881+0000    Error initializing mongomirror: could not initialize destination connection: could not connect to server: server selection error: server selection timeout
current topology: Type: ReplicaSetNoPrimary
Servers:
```
のようなエラーがでてmongomirrorが止まる

### -jオプションについて
collectionコピーの並列度を決める。
[numParallelCollections](https://docs.atlas.mongodb.com/reference/mongomirror/#cmdoption-numparallelcollections)

> --numParallelCollections <num>, -j <num>
> Default: 4

> The number of collections to copy and restore in parallel.

サポートに聞いたところ、

> we recommend this parameter to be in line with the number of CPU cores on the host machine hosting mongomirror. 

とのことなので、CPUサイズに合わせる。
デフォルトは4。おそらくLiveMigrationも裏ではmongomirrorを4で起動しているのではないかと思う。
今回16にすると、LiveMigrationでは16hかかっていたものが6hほどに短縮された。

## mongomirrorのログの読み方

### `initializing ` ~ `initial sync`のログ

```text

nohup: ignoring input
mongomirror version: 0.8.0
git version: a5cbb2d5babea8ed87191b31b12ffc6f3a3fa485
Go version: go1.12.4
   os: linux
   arch: amd64
   compiler: gc
2019-10-08T05:31:42.724+0000    Initializing HTTP status service on port 8000
2019-10-08T05:31:42.801+0000    Attempting initial sync from: 10.0.1.17:27017
2019-10-08T05:31:43.015+0000    Creating collection <DB名>.<コレクション名AAA>
2019-10-08T05:31:43.015+0000    Creating collection <DB名>.<コレクション名BBB>
2019-10-08T05:31:43.078+0000    Copying documents to collection <DB名>.<コレクション名AAA>
2019-10-08T05:31:43.116+0000    Copying documents to collection <DB名>.<コレクション名BBB>
2019-10-08T05:31:46.015+0000    [........................]       <DB名>.<コレクション名AAA>   39316/1033602  (3.8%)
2019-10-08T05:31:46.015+0000    [#.......................]       <DB名>.<コレクション名BBB>   50015/1161614  (4.3%)
```

コレクション毎の進捗がインジゲーター風に3秒ごとに出力される。
ここでJオプションで指定した並列度が効いてくる。

### indexのコピー
documentのコピーが終わるとindexの構築が始まる

```text

2019-10-08T05:43:32.944+0000    Copied 11043565 documents to <DB名>.<コレクション名AAA>
2019-10-08T05:43:32.944+0000    [########################]  <DB名>.<コレクション名AAA>  11043565/11043565  (100.0%)
2019-10-08T05:43:32.993+0000    Tailing the oplog on the source cluster starting at timestamp: {1570512697 1}
2019-10-08T05:43:32.994+0000    Oplog tailer has shut down. Oplog applier will exit.
2019-10-08T05:43:32.994+0000    Current lag until the end of initial sync: 0s
2019-10-08T05:43:33.032+0000    createIndexes for collection: `<DB名>.<コレクション名AAA>`, finished in 36.390104ms
2019-10-08T05:43:33.032+0000    createIndexes for collection: `<DB名>.<コレクション名BBB>`, finished in 36.834095ms
```

ここで注意したいのが、`createIndexes for collection` のログ出力は、index構築完了後に表示されるので、大きなコレクションでIndex作成自体に数分かかるものがある場合、ログの出力が止まってしまう。
ログ出力がない場合でもmongomirrorのプロセスは生きているのでそんなときはhttpサーバー機能(後述)で現在のステータスを確認できるので慌てない。

### `initial sync` ~ `oplog sync`のログ

documentのコピー、Indexの構築がおわると、mongomirrorは`oplog sync`状態にはいる。
このlag時間が0sの場合にカットオーバーすることができる。


```text

2019-10-08T06:00:48.161+0000    Index builds completed.
2019-10-08T06:00:48.161+0000    Initial sync from 10.0.1.17:27017 completed at oplog timestamp: {{1570513407 1} {1570513407 1}}
2019-10-08T06:00:48.161+0000    Proceeding to tail oplog.
2019-10-08T06:00:48.174+0000    Current lag from source: 17m20s
2019-10-08T06:00:48.174+0000    Tailing the oplog on the source cluster starting at timestamp: {1570513407 1}
2019-10-08T06:00:58.185+0000    Current lag from source: 0s
2019-10-08T06:01:08.175+0000    Current lag from source: 0s
```

### httpサーバー機能によるステータス確認

起動オプションで指定した、[`--httpStatusPort 8000`](https://docs.atlas.mongodb.com/reference/mongomirror/#cmdoption-httpstatusport) に対して、GETリクエストを行うことで、`mongomirror`のプロセスの現在の状態を知ることができる。

```shell

curl -X GET http://localhost:8000 | python -m json.tool
```

```json

{
    "details": {
        "<DB名>.<コレクション名AAA>": {
            "complete": true,
            "createIndexes": 2
        },
        "<DB名>.<コレクション名AAA>": {
            "complete": true,
            "createIndexes": 4
        },
        ....
    },
    "phase": "copying indexes",
    "stage": "initial sync"
}
```

## `mongomirror` の停止

```shell

ps aux | grep mongomirror
kill <pid>
```

```text

2019-10-08T06:10:28.175+0000    Current lag from source: 0s
2019-10-08T06:10:38.175+0000    Current lag from source: 0s
2019-10-08T06:10:48.130+0000    signal 'terminated' received; attempting to shut down
2019-10-08T06:10:48.130+0000    Quitting...
2019-10-08T06:10:48.131+0000    Current lag from source: 0s
2019-10-08T06:10:49.130+0000    Timestamp file written to /home/ec2-user/mongomirror_work/mongomirror.timestamp.
```

このとき作成された`mongomirror.timestamp`は次回の`mongomirror`実行時に、[bookmarkFileオプション](https://docs.atlas.mongodb.com/reference/mongomirror/#cmdoption-bookmarkfile)として指定することで、続きから実行することができる。

`--bookmarkFile`を指定して起動したときのログは以下のような出力となる。

```text
git version: a5cbb2d5babea8ed87191b31b12ffc6f3a3fa485
Go version: go1.12.4
   os: linux
   arch: amd64
   compiler: gc
2019-10-10T01:51:16.723+0000	Initializing HTTP status service on port 8000
2019-10-10T01:51:16.791+0000	Read timestamp from bookmark file: {1570671675 1}
2019-10-10T01:51:16.791+0000	Proceeding to tail oplog.
2019-10-10T01:51:16.792+0000	Current lag from source: 10m0s
2019-10-10T01:51:16.792+0000	Tailing the oplog on the source cluster starting at timestamp: {1570671675 1}
2019-10-10T01:51:26.803+0000	Current lag from source: 0s
2019-10-10T01:51:36.793+0000	Current lag from source: 0s
```