Carpe Diem

備忘録。https://github.com/jun06t

DockerでMongoDBのレプリカセットを構築

概要

MongoDBのレプリカセットを組んで検証する必要があったのでdockerを使って構築することにしてみました。

環境

  • Docker 1.12.0
  • Mongo 3.2.9

構成

コンテナ名 役割 ホスト側ポート
mongo1 Primary 30001
mongo2 Secondary 30002
arbiter Arbiter。データは持たない 30011

以下の様な形です。

f:id:quoll00:20160823131951p:plain

ref: https://docs.mongodb.com/manual/core/replica-set-architecture-three-members/

準備

mongoの最新イメージの取得

$ docker pull mongo

ネットワークの構築をします。default networkでもいいですが名前解決をしてくれる用に別途ネットワークを用意します。

$ docker network create my-mongo-cluster

確認します。

$ docker network ls
NETWORK ID          NAME                         DRIVER              SCOPE
6a5213fdac67        bridge                       bridge              local
d205b39c97f1        host                         host                local
ed7aae6ef398        my-mongo-cluster             bridge              local
3d8436389b6c        none                         null                local

構築

mongo1

$ docker run \
-p 30001:27017 \
--name mongo1 \
--net my-mongo-cluster \
-d \
mongo mongod --replSet my-mongo-set

mongo2

$ docker run \
-p 30002:27017 \
--name mongo2 \
--net my-mongo-cluster \
-d \
mongo mongod --replSet my-mongo-set

arbiter1

$ docker run \
-p 30011:27017 \
--name arbiter1 \
--net my-mongo-cluster \
-d \
mongo mongod --replSet my-mongo-set

設定

mongo1にアクセスします。

$ docker exec -it mongo1 mongo

設定を用意します。

> config = {_id: 'my-mongo-set',
    members: [
      {_id: 0, host: 'mongo1:27017'},
      {_id: 1, host: 'mongo2:27017'},
      {_id: 2, host: 'arbiter1:27017', arbiterOnly: true},
    ]
  }

反映します。

> rs.initiate(config)
{ "ok" : 1 }

以下のように設定されます。

my-mongo-set:PRIMARY> rs.status()
{
    "set" : "my-mongo-set",
    "date" : ISODate("2016-08-23T04:26:30.371Z"),
    "myState" : 1,
    "term" : NumberLong(1),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "members" : [
        {
            "_id" : 0,
            "name" : "mongo1:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 1042,
            "optime" : {
                "ts" : Timestamp(1471926384, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2016-08-23T04:26:24Z"),
            "infoMessage" : "could not find member to sync from",
            "electionTime" : Timestamp(1471926383, 1),
            "electionDate" : ISODate("2016-08-23T04:26:23Z"),
            "configVersion" : 1,
            "self" : true
        },
        {
            "_id" : 1,
            "name" : "mongo2:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 17,
            "optime" : {
                "ts" : Timestamp(1471926384, 1),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2016-08-23T04:26:24Z"),
            "lastHeartbeat" : ISODate("2016-08-23T04:26:29.931Z"),
            "lastHeartbeatRecv" : ISODate("2016-08-23T04:26:27.256Z"),
            "pingMs" : NumberLong(0),
            "syncingTo" : "mongo1:27017",
            "configVersion" : 1
        },
        {
            "_id" : 2,
            "name" : "arbiter1:27017",
            "health" : 1,
            "state" : 7,
            "stateStr" : "ARBITER",
            "uptime" : 17,
            "lastHeartbeat" : ISODate("2016-08-23T04:26:29.931Z"),
            "lastHeartbeatRecv" : ISODate("2016-08-23T04:26:30.256Z"),
            "pingMs" : NumberLong(0),
            "configVersion" : 1
        }
    ],
    "ok" : 1
}

同期の確認

データを入れてみます。

my-mongo-set:PRIMARY> use mydb
my-mongo-set:PRIMARY>  for(var i=0; i<10000; i++) db.logs.insert(
    { "uid":i, "value":Math.floor(Math.random()*10000+1) } )

Secondaryに同期されているか確認します。

$ docker exec -it mongo2 mongo
my-mongo-set:SECONDARY> use mydb
my-mongo-set:SECONDARY> db.getMongo().setSlaveOk()
my-mongo-set:SECONDARY> db.logs.count()
10000

大丈夫ですね。

db.getMongo().setSlaveOk()はプライマリ以外のノードからの読み込みを許可する設定です。これをしていないと以下のエラーが出ます。

2016-08-23T04:55:36.331+0000 E QUERY    [thread1] Error: count failed: { "ok" : 0, "errmsg" : "not master and slaveOk=false", "code" : 13435 } :

フェイルオーバーの確認

プライマリを落とします。

$ docker stop mongo1

mongo2に入ってみます。

$ docker exec -it mongo2 mongo

statusを確認します。

my-mongo-set:SECONDARY> rs.status()
{
    "set" : "my-mongo-set",
    "date" : ISODate("2016-08-23T04:59:15.672Z"),
    "myState" : 1,
    "term" : NumberLong(2),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "members" : [
        {
            "_id" : 0,
            "name" : "mongo1:27017",
            "health" : 0,
            "state" : 8,
            "stateStr" : "(not reachable/healthy)",
            "uptime" : 0,
            "optime" : {
                "ts" : Timestamp(0, 0),
                "t" : NumberLong(-1)
            },
            "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
            "lastHeartbeat" : ISODate("2016-08-23T04:59:15.266Z"),
            "lastHeartbeatRecv" : ISODate("2016-08-23T04:59:01.642Z"),
            "pingMs" : NumberLong(0),
            "lastHeartbeatMessage" : "HostUnreachable",
            "configVersion" : -1
        },
        {
            "_id" : 1,
            "name" : "mongo2:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 2990,
            "optime" : {
                "ts" : Timestamp(1471928353, 1),
                "t" : NumberLong(2)
            },
            "optimeDate" : ISODate("2016-08-23T04:59:13Z"),
            "infoMessage" : "could not find member to sync from",
            "electionTime" : Timestamp(1471928352, 1),
            "electionDate" : ISODate("2016-08-23T04:59:12Z"),
            "configVersion" : 1,
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "arbiter1:27017",
            "health" : 1,
            "state" : 7,
            "stateStr" : "ARBITER",
            "uptime" : 1980,
            "lastHeartbeat" : ISODate("2016-08-23T04:59:14.602Z"),
            "lastHeartbeatRecv" : ISODate("2016-08-23T04:59:10.851Z"),
            "pingMs" : NumberLong(0),
            "configVersion" : 1
        }
    ],
    "ok" : 1
}
my-mongo-set:PRIMARY>

mongo2がPRIMARYになっていました。

ここでmongo1を戻してみます。

$ docker start mongo1

構成がどうなるか確認します。

my-mongo-set:SECONDARY> rs.status()
{
    "set" : "my-mongo-set",
    "date" : ISODate("2016-08-23T05:00:48.908Z"),
    "myState" : 2,
    "term" : NumberLong(2),
    "syncingTo" : "mongo2:27017",
    "heartbeatIntervalMillis" : NumberLong(2000),
    "members" : [
        {
            "_id" : 0,
            "name" : "mongo1:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 11,
            "optime" : {
                "ts" : Timestamp(1471928353, 1),
                "t" : NumberLong(2)
            },
            "optimeDate" : ISODate("2016-08-23T04:59:13Z"),
            "syncingTo" : "mongo2:27017",
            "configVersion" : 1,
            "self" : true
        },
        {
            "_id" : 1,
            "name" : "mongo2:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 10,
            "optime" : {
                "ts" : Timestamp(1471928353, 1),
                "t" : NumberLong(2)
            },
            "optimeDate" : ISODate("2016-08-23T04:59:13Z"),
            "lastHeartbeat" : ISODate("2016-08-23T05:00:47.977Z"),
            "lastHeartbeatRecv" : ISODate("2016-08-23T05:00:48.890Z"),
            "pingMs" : NumberLong(1),
            "electionTime" : Timestamp(1471928352, 1),
            "electionDate" : ISODate("2016-08-23T04:59:12Z"),
            "configVersion" : 1
        },
        {
            "_id" : 2,
            "name" : "arbiter1:27017",
            "health" : 1,
            "state" : 7,
            "stateStr" : "ARBITER",
            "uptime" : 10,
            "lastHeartbeat" : ISODate("2016-08-23T05:00:47.977Z"),
            "lastHeartbeatRecv" : ISODate("2016-08-23T05:00:48.006Z"),
            "pingMs" : NumberLong(0),
            "configVersion" : 1
        }
    ],
    "ok" : 1
}

mongo2がPRIMARY、mongo1がSECONDARYで稼働するようになりました。

ソース