N-LAB

MongoDB 6.0のレプリケーション(レプリカセット)をDockerコンテナ上で構築する方法


目標


前提


注意点

https://www.mongodb.com/docs/manual/release-notes/6.0-compatibility/#legacy-mongo-shell-removed

目次

  1. MongoDBレプリケーション(レプリカセット)概要
  2. レプリケーション(レプリカセット)設定
  3. レプリケーション(レプリカセット)動作確認
  4. データ同期確認
  5. フェイルオーバー動作確認


1. MongoDBレプリケーション(レプリカセット)概要

PRIMARY:レプリカセット中に1台だけ存在する書き込み可能なサーバー。
SECONDARY:Primaryのデータ更新を行う読み取り専用のサーバー。
ARBITER:データは保持せず、Primaryへの昇格投票のみを行うサーバー。自身がPrimaryになることはなく、Arbiterは必須ではない。

2. レプリケーション(レプリカセット)設定

(1). レプリケーション(レプリカセット)を構築するにあたり、ディレクトリ構成は以下になります。

mongo
├── docker-compose.yml
├── data ← MongoDBのデータの格納場所
│     ├── primary
│     └── secondary
└── etc
     ├── mongod-keyfile ← 認証キーファイル
     └── init
          └── init.js ← 初期化スクリプト


(2). 上記のディレクトリ構成に基づき必要なディレクトリを作成します。

$ sudo mkdir /usr/local/mongo
$ cd /usr/local/mongo
$ sudo mkdir data
$ sudo mkdir data/primary
$ sudo mkdir data/secondary
$ sudo mkdir etc
$ sudo mkdir etc/init


(3). docker-compose.ymlファイルを新規作成し、以下を記載して保存します。

$ cd /usr/local/mongo
$ sudo vi docker-compose.yml
version: '3.9'
services:

  mongodb-primary:
    image: mongo:6.0.5
    container_name: mongodb-primary
    hostname: mongodb-primary
    command: mongod --replSet replset --auth --keyFile /etc/mongod-keyfile
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: password
    volumes:
      # データの永続化
      - ./data/primary:/data/db
      # 初期化スクリプトの配置
      - ./etc/init/:/docker-entrypoint-initdb.d/:ro
      # 認証鍵(キーファイル)配置
      - ./etc/mongod-keyfile:/etc/mongod-keyfile:ro
    expose:
    - 27017
    ports:
      - 27011:27017
    networks:
      - replset
    restart: always

  mongodb-secondary:
    image: mongo:6.0.5
    container_name: mongodb-secondary
    hostname: mongodb-secondary
    command: mongod --replSet replset --auth --keyFile /etc/mongod-keyfile
    volumes:
      # データの永続化
      - ./data/secondary:/data/db
      # 認証鍵(キーファイル)配置
      - ./etc/mongod-keyfile:/etc/mongod-keyfile:ro
    expose:
    - 27017
    ports:
      - 27012:27017
    networks:
      - replset
    restart: always

  mongodb-arbiter:
    image: mongo:6.0.5
    container_name: mongodb-arbiter
    hostname: mongodb-arbiter
    command: mongod --replSet replset --auth --keyFile /etc/mongod-keyfile
    volumes:
      # arbiterはデータを保持しないため、データの永続化は不要
      # 認証鍵(キーファイル)配置
      - ./etc/mongod-keyfile:/etc/mongod-keyfile:ro
    expose:
    - 27017
    ports:
      - 27013:27017
    networks:
      - replset
    restart: always

networks:
  replset:
    ipam:
      config:
      - subnet: 192.168.1.0/24

※各サーバーの役割はそれぞれ以下になります。
mongodb-primary:PRIMARY
mongodb-secondary:SECONDARY
mongodb-arbiter:ARBITER

(4). 認証鍵(キーファイル)を作成します。

$ su - root
# cd /usr/local/mongo
# openssl rand -base64 756 > etc/mongod-keyfile
# chmod 600 etc/mongod-keyfile
# chown 999 etc/mongod-keyfile
# exit


(5). 初期化スクリプトを作成し、以下を記載して保存します。

$ cd /usr/local/mongo
$ sudo vi etc/init/init.js
rs.initiate({
  _id: "replset",
  members: [{
    _id: 0,
    host: "mongodb-primary:27017",
    priority: 100
  }, {
    _id: 1,
    host: "mongodb-secondary:27017",
    priority: 10
  }, {
    _id: 2,
    host: "mongodb-arbiter:27017",
    arbiterOnly: true
  }]
});


(6). コンテナを起動します。

$ cd /usr/local/mongo
$ docker-compose up -d
[+] Running 4/4
 ✔ Network mongo_replset        Created
 ✔ Container mongodb-secondary  Started
 ✔ Container mongodb-primary    Started
 ✔ Container mongodb-arbiter    Started


(7). コンテナが正しく起動しているか確認します。

$ docker ps
CONTAINER ID   IMAGE         COMMAND                   CREATED         STATUS                  PORTS                                           NAMES
34a735f80f3e   mongo:6.0.5   "docker-entrypoint.s…"   5 seconds ago   Up 4 seconds            0.0.0.0:27012->27017/tcp, :::27012->27017/tcp   mongodb-secondary
0bcfbf484fb5   mongo:6.0.5   "docker-entrypoint.s…"   5 seconds ago   Up Less than a second   0.0.0.0:27011->27017/tcp, :::27011->27017/tcp   mongodb-primary
a52856076f23   mongo:6.0.5   "docker-entrypoint.s…"   5 seconds ago   Up 3 seconds            0.0.0.0:27013->27017/tcp, :::27013->27017/tcp   mongodb-arbiter


(8). 初期化スクリプトを実行します。

docker-compose exec mongodb-primary mongosh admin -u root -p password /docker-entrypoint-initdb.d/init.js


3. レプリケーション(レプリカセット)動作確認

mongodb-primary:PRIMARY
mongodb-secondary:SECONDARY
mongodb-arbiter:ARBITER

(1). mongodb-primaryへ接続します。

$ docker exec -it mongodb-primary bash


(2). MongoDBへログインします。

root@mongodb-primary:/# mongosh
replset:PRIMARY> use admin
replset:PRIMARY> db.auth("root","password")


(3). レプリケーション(レプリカセット)の状態を表示します。

replset:PRIMARY> rs.status()


(4). 正しくレプリケーション(レプリカセット)設定を行えていた場合は以下のようなステータス(一部抜粋)が表示されます。

"members" : [
        {
                "_id" : 0,
                "name" : "mongodb-primary:27017",
                "stateStr" : "PRIMARY"
        },
        {
                "_id" : 1,
                "name" : "mongodb-secondary:27017",
                "stateStr" : "SECONDARY"
        },
        {
                "_id" : 2,
                "name" : "mongodb-arbiter:27017",
                "stateStr" : "ARBITER"
        }
]

※stateStrが各サーバーのレプリケーション(レプリカセット)における役割を示しています。上記のコマンド結果から現在各サーバーに以下の役割が設定されています。
mongodb-primary:PRIMARY
mongodb-secondary:SECONDARY
mongodb-arbiter:ARBITER

4. データ同期確認

(1). mongodb-secondaryへ接続します。

$ docker exec -it mongodb-secondary bash


(2). MongoDBへログインします。

root@mongodb-secondary:/# mongosh
replset:SECONDARY> use admin
replset:SECONDARY> db.auth("root","password")


(3). mongodb-secondaryで読取操作を行うために以下のコマンドを入力し
ます。

replset:SECONDARY> db.getMongo().setReadPref("primaryPreferred")

※rs.slaveOk()とrs.secondaryOk()は非推奨になったため上記コマンドを使用しています。

(4). ドキュメントを検索します。

replset:SECONDARY> use test
replset:SECONDARY> db.user.find()

※この時点では検索結果は0件になります。
※(3)のコマンドを事前に実行していないと以下のエラーが表示されます。
MongoServerError: not primary and secondaryOk=false - consider using db.getMongo().setReadPref() or readPreference in the connection string

(5). mongodb-primaryでドキュメントを挿入します。

$ docker exec -it mongodb-primary bash
root@mongodb-primary:/# mongosh
replset:PRIMARY> use admin
replset:PRIMARY> db.auth("root","password")
replset:PRIMARY> use test
replset:PRIMARY> db.user.insertOne({name: 'test'})
replset:PRIMARY> db.user.find()
[ { _id: ObjectId("643baec213cf4070041c5fcc"), name: 'test' } ]

※ドキュメントを挿入したため検索結果は1件表示されます。

(6). mongodb-secondaryでドキュメントを検索します。

$ docker exec -it mongodb-secondary bash
root@mongodb-secondary:/# mongosh
replset:SECONDARY> use admin
replset:SECONDARY> db.auth("root","password")
replset:SECONDARY> db.getMongo().setReadPref("primaryPreferred")
replset:SECONDARY> use test
replset:SECONDARY> db.user.find()
[ { _id: ObjectId("643baec213cf4070041c5fcc"), name: 'test' } ]

※データが同期され検索結果は1件表示されます。

5. フェイルオーバー動作確認

(1). mongodb-primaryを停止します。

$ docker stop mongodb-primary


(2). mongodb-primaryが表示されないことを確認します。

$ docker ps
CONTAINER ID   IMAGE         COMMAND                   CREATED          STATUS          PORTS                                           NAMES
34a735f80f3e   mongo:6.0.5   "docker-entrypoint.s…"   22 minutes ago   Up 22 minutes   0.0.0.0:27012->27017/tcp, :::27012->27017/tcp   mongodb-secondary
a52856076f23   mongo:6.0.5   "docker-entrypoint.s…"   22 minutes ago   Up 22 minutes   0.0.0.0:27013->27017/tcp, :::27013->27017/tcp   mongodb-arbiter


(3). mongodb-secondaryへ接続します。

$ docker exec -it mongodb-secondary bash


(4). MongoDBへログインします。PRIMARYに昇格したため、replset:PRIMARYと表示されることを確認します。

root@mongodb-secondary:/# mongosh
replset:PRIMARY> use admin
replset:PRIMARY> db.auth("root","password")


(5). レプリケーション(レプリカセット)の状態を表示します。

replset:PRIMARY> rs.status()


(6). mongodb-secondaryがPRIMARYに昇格していることを確認します。

"members" : [
        {
                "_id" : 0,
                "name" : "mongodb-primary:27017",
                "health" : 0,
                "stateStr" : "(not reachable/healthy)"
        },
        {
                "_id" : 1,
                "name" : "mongodb-secondary:27017",
                "health" : 1,
                "stateStr" : "PRIMARY"
        },
        {
                "_id" : 2,
                "name" : "mongodb-arbiter:27017",
                "health" : 1,
                "stateStr" : "ARBITER"
        }
]


(1). mongodb-primaryを起動します。

$ docker start mongodb-primary


(2). mongodb-primaryが表示されていることを確認します。

$ docker ps
CONTAINER ID   IMAGE         COMMAND                   CREATED          STATUS          PORTS                                           NAMES
34a735f80f3e   mongo:6.0.5   "docker-entrypoint.s…"   26 minutes ago   Up 26 minutes   0.0.0.0:27012->27017/tcp, :::27012->27017/tcp   mongodb-secondary
0bcfbf484fb5   mongo:6.0.5   "docker-entrypoint.s…"   26 minutes ago   Up 2 seconds    0.0.0.0:27011->27017/tcp, :::27011->27017/tcp   mongodb-primary
a52856076f23   mongo:6.0.5   "docker-entrypoint.s…"   26 minutes ago   Up 26 minutes   0.0.0.0:27013->27017/tcp, :::27013->27017/tcp   mongodb-arbiter


(3). mongodb-secondaryへ接続します。

$ docker exec -it mongodb-secondary bash


(4). MongoDBへログインします。SECONDARYに降格したため、replset:SECONDARYと表示されることを確認します。

root@mongodb-secondary:/# mongosh
replset:SECONDARY> use admin
replset:SECONDARY> db.auth("root","password")


(5). レプリケーション(レプリカセット)の状態を表示します。

replset:SECONDARY> rs.status()


(6). mongodb-primaryサーバーが復帰し、PRIMARYとして設定されていることを確認します。

"members" : [
        {
                "_id" : 0,
                "name" : "mongodb-primary:27017",
                "health" : 1,
                "stateStr" : "PRIMARY",
        },
        {
                "_id" : 1,
                "name" : "mongodb-secondary:27017",
                "health" : 1,
                "stateStr" : "SECONDARY"
        },
        {
                "_id" : 2,
                "name" : "mongodb-arbiter:27017",
                "health" : 1,
                "stateStr" : "ARBITER"
        }
]

※復帰したmongodb-primaryサーバーをPRIMARYとするかSECONDARYにするかはpriorityにより決定します。
本手順書ではmongodb-primaryサーバーのpriorityを100に設定し、mongodb-secondaryサーバーのpriorityを10に設定しているため、mongodb-primaryサーバーはPRIMARYとして復帰します。


以上で全ての手順は完了になります