MongoDB
docker
docker-compose

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

MongoDB4.0で遂にトランザクションに対応したので、先日アップデートを行った。
こちらの記事↓
MongoDBをバージョン4.0にアップグレード in Docker

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

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

レプリカセットの構成

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

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

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

docker-compose.ymlを編集する

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

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

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

$ docker network create mongo_link

そして、docker-compose.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は最終的にこんな感じになります。

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は不要となります。

レプリカセットをセット

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

$ docker-compose up -d

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

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

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

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

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

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

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の中身を参照することができませんので、参照できるように設定します。

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

確認

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

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

$ 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

こちらにもデータが保存されていることがわかりますね。

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