運用中のMongoDBのレプリカセットを行う in Docker

MongoDB4.0で遂にトランザクションに対応したので、先日アップデートを行った。
こちらの記事↓
[MongoDBをバージョン4.0にアップグレード in Docker](https://qiita.com/RyoheiTomiyama/items/9fab68e65d81fb2d4a3f)


ただ、Mongoを1台で起動しているため、今のままではトランザクションは使えないのだ。
トランザクションに対応するためには、レプリカセットしなければならない。

今まで面倒で行っていなかったレプリカセットを行おうと思う。

## レプリカセットの構成

最小限の構成として、プライマリー1台、セカンダリー2台、アービター1台で作成します。

プライマリーとかアービターについてはこちら
https://qiita.com/hakozaki/items/c41a5e6e7f0fad557c43

また、Dockerを使っているので、コンテナ1台につき1DBにして、ネットワークでそれぞれを繋ぐ構成でいきます。


## docker-compose.ymlを編集する

現在のコンテナの設定は、

```yml:docker-compose.yml
  mongo:
    restart: always
    image: mongo
    volumes:
      - /var/www/data/mongo:/data/db
```

いたってシンプルです。
3台共通のネットワークに繋げるために、dockerコマンドでmongo_linkというネットワークを作成します。

```bash
$ docker network create mongo_link
```
そして、`docker-compose.yml`を編集

```yml:docker-compose.yml
  mongo:
    restart: always
    image: mongo
    volumes:
      - /var/www/data/mongo:/data/db
    # 3行追加
    networks:
      - default
      - mongo_link
networks:
  mongo_link:
    external: true
```

*`- default`←これなんぞ？と思った方いると思いますが、`docker-compose`ではコンテナを作成したときに、勝手に`DIRECTORYNAME_default`というネットワーク内にいることになります。
`- default`を書かなければ、他のコンテナからアクセスできなく恐れがあるので、`docker-compose.yml`内に別のコンテナを含んでいる場合は設定しておきましょう。*

  
  
  

さらに、セカンダリーとアービター用のコンテナの追加とレプリカセット用の引数をセットします。
すると、`docker-compose.yml`は最終的にこんな感じになります。

```yml:docker-compose.yml
  mongo:
    restart: always
    image: mongo
    volumes:
      - /var/www/data/mongo:/data/db
    networks:
      - default
      - mongo_link
    command: --replSet mongo-set

  mongo-secondary:
    restart: always
    image: mongo
    volumes:
      - /var/www/data/mongo-secondary:/data/db
    networks:
      - default
      - mongo_link
    command: --replSet mongo-set

  mongo-arbiter:
    restart: always
    image: mongo
    networks:
      - default
      - mongo_link
    command: --replSet mongo-set

networks:
  mongo_link:
    external: true
```

プライマリーは、`--replSet mongo-set`というのを追加してレプリカセットできるようにしています。

セカンダリーはほぼプライマリーと同じですが、DBのデータを保存する空のディレクトリ`mongo-secondary/`を用意しておきます。

アービターは、データを保持しないので、`volumes`は不要となります。

## レプリカセットをセット

コンテナを生成しましょう

```bash
$ docker-compose up -d
```

**mongoが再起動するとアクセスできない時間ができます。運用中の場合は、速やかに下記の作業を行ってください**

```bash
# mongoコンテナにアクセス
$ docker-compose exec mongo bash
# mongo起動
% mongo
> rs.initiate()   # レプリカセットの初期化
```

一旦これで、mongo1台で稼働中となります。
（念ため、接続できているかアプリケーションの確認は行いましょう）

ここからセカンダリーとアービターをレプリカセットの中に追加していきます。

```bash
mongo-set:PRIMARY> rs.add({ host: 'mongo-secondary:27017' })
mongo-set:PRIMARY> rs.add({ host: 'mongo-arbiter:27017', arbiterOnly: true })
```

確認
membersの中に設定したセカンダリーとアービターがいれば成功です。

```bash
mongo-set:PRIMARY> rs.status()
{
	"set" : "mongo-set",
	"date" : ISODate("2018-08-17T10:02:29.297Z"),
	"myState" : 1,
	"term" : NumberLong(1),
	"syncingTo" : "",
        .
        .
        .
	"members" : [
		{
			"_id" : 0,
			"name" : "1b00277ff073:27017",
			"health" : 1,
			"state" : 1,
			"stateStr" : "PRIMARY",
			"uptime" : 987,
			"optime" : {
				"ts" : Timestamp(1534500141, 2),
				"t" : NumberLong(1)
			},
			"optimeDate" : ISODate("2018-08-17T10:02:21Z"),
			"syncingTo" : "",
			"syncSourceHost" : "",
			"syncSourceId" : -1,
			"infoMessage" : "",
			"electionTime" : Timestamp(1534499609, 2),
			"electionDate" : ISODate("2018-08-17T09:53:29Z"),
			"configVersion" : 3,
			"self" : true,
			"lastHeartbeatMessage" : ""
		},
		{
			"_id" : 1,
			"name" : "mongo-secondary:27017",
			"health" : 1,
			"state" : 2,
			"stateStr" : "SECONDARY",
			"uptime" : 18,
			"optime" : {
				"ts" : Timestamp(1534500141, 2),
				"t" : NumberLong(1)
			},
			"optimeDurable" : {
				"ts" : Timestamp(1534500141, 2),
				"t" : NumberLong(1)
			},
			"optimeDate" : ISODate("2018-08-17T10:02:21Z"),
			"optimeDurableDate" : ISODate("2018-08-17T10:02:21Z"),
			"lastHeartbeat" : ISODate("2018-08-17T10:02:27.858Z"),
			"lastHeartbeatRecv" : ISODate("2018-08-17T10:02:28.361Z"),
			"pingMs" : NumberLong(0),
			"lastHeartbeatMessage" : "",
			"syncingTo" : "1b00277ff073:27017",
			"syncSourceHost" : "1b00277ff073:27017",
			"syncSourceId" : 0,
			"infoMessage" : "",
			"configVersion" : 3
		},
		{
			"_id" : 2,
			"name" : "mongo-arbiter:27017",
			"health" : 1,
			"state" : 7,
			"stateStr" : "ARBITER",
			"uptime" : 7,
			"lastHeartbeat" : ISODate("2018-08-17T10:02:27.859Z"),
			"lastHeartbeatRecv" : ISODate("2018-08-17T10:02:27.887Z"),
			"pingMs" : NumberLong(0),
			"lastHeartbeatMessage" : "",
			"syncingTo" : "",
			"syncSourceHost" : "",
			"syncSourceId" : -1,
			"infoMessage" : "",
			"configVersion" : 3
		}
	],
	"ok" : 1,
	"operationTime" : Timestamp(1534500141, 2),
	"$clusterTime" : {
		"clusterTime" : Timestamp(1534500141, 2),
		"signature" : {
			"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
			"keyId" : NumberLong(0)
		}
	}
}
```

レプリカセットできていますね。


## セカンダリーからも読み込みできるように設定する

レプリカセットは完了しましたが、このままでは、プライマリー以外からは、DBの中身を参照することができませんので、参照できるように設定します。


```bash
$ docker-compose exec mongo-secondary bash                                                                                                                                                                                  % mongo
mongo-set:SECONDARY> db.getMongo().setSlaveOk()
```

確認

```
> use DATABASE
> db.COL.count()
113
```

参照できました。
さらに、セカンダリーのデータを保存するために用意した、mongo-secondaryというディレクトリがあるとおもいますが、、、

```bash
$ ls mongo-secondary
WiredTiger                             collection-15--2012905472052276688.wt  collection-34--2012905472052276688.wt  index-17--2012905472052276688.wt       index-3--2012905472052276688.wt        index-5--2012905472052276688.wt
WiredTiger.lock                        collection-18--2012905472052276688.wt  collection-37--2012905472052276688.wt  index-19--2012905472052276688.wt       index-30--2012905472052276688.wt       index-7--2012905472052276688.wt
WiredTiger.turtle                      collection-2--2012905472052276688.wt   collection-4--2012905472052276688.wt   index-21--2012905472052276688.wt       index-32--2012905472052276688.wt       index-9--2012905472052276688.wt
WiredTiger.wt                          collection-20--2012905472052276688.wt  collection-6--2012905472052276688.wt   index-22--2012905472052276688.wt       index-33--2012905472052276688.wt       journal/
WiredTigerLAS.wt                       collection-23--2012905472052276688.wt  collection-8--2012905472052276688.wt   index-24--2012905472052276688.wt       index-35--2012905472052276688.wt       mongod.lock
_mdb_catalog.wt                        collection-25--2012905472052276688.wt  diagnostic.data/                       index-26--2012905472052276688.wt       index-36--2012905472052276688.wt       sizeStorer.wt
collection-0--2012905472052276688.wt   collection-28--2012905472052276688.wt  index-1--2012905472052276688.wt        index-27--2012905472052276688.wt       index-38--2012905472052276688.wt       storage.bson
collection-14--2012905472052276688.wt  collection-31--2012905472052276688.wt  index-16--2012905472052276688.wt       index-29--2012905472052276688.wt       index-39--2012905472052276688.wt
```
こちらにもデータが保存されていることがわかりますね。



以上で、レプリカセットが完了となります。
お疲れ様でした。