Written by TSUYOSHI

MongoDBのレプリカセット構築をDockerで対応する 【サーバ2台構成】

Docker MongoDB Uncategorized

本記事の内容

  • DockerでMongoDB レプリカセットを構築する概要
  • MongoDBでレプリカセットをDockerで構築する具体的な手順
  • Dockerでのレプリカセット構築時の注意点

この記事を読むことによって、「Dockerを使ってMongoDBのレプリカセットを構築できる」ようになります。

この記事を書いている僕は、エンジニア歴4年。
JavaScriptでの開発を得意としており、フロントエンドのエンジニアがMongoDBのレプリカセット構築をDockerで行う際に、注意すべき点を説明します。

DockerMongoDB レプリカセットを構築する概要

2台のサーバにDocker環境を構築して、そこにサービスのひとつとして、MongoDBでレプリカセットを構築するときの手順を説明します。

1台のサーバでDockerを使って解説している記事はあったのですが、Dockerを使った2台構成の記事はなく、サーバ間の連携などにつまづいたため記事を書きました。

具体的には、サーバAで重い処理を行いつつMongoDBに書き込みをして、サーバBでは読み込み専用として、通常のWebサービスの表示用MongoDB(読み取り専用で内容はサーバAとデータ共有されている)を使うということをしたかったため2台構成にしています。
本当はレプリカセットは3台(以上)にすべきですが、コストの問題で2台で解説をしています。

レプリカセットでサーバ2台にDockerを入れて構築する方法を解説

2台構築の理由

レプリカセットは本来、最低3台のサーバが必要です。

  • primary:
    読み書き用で、書き込みはprimaryのみ可能となる。
  • secondary:
    読み込み専用。primaryと同期して同じデータを保持する。
    設定により、primaryがダウンした時に、primaryに昇格する。
  • arbiter:
    primaryを決定するための投票用。データは保持しない。
    今回はsecondaryと同居させているが、本当は低スペックサーバでよいので別サーバにして独立させるべき。

本来は3台のサーバが必要ですが、個人開発だとコストの問題もあると思うので、2台で構成させるという前提で話を進めています。本当はarbiterも独立させた方がよいので、例えばAWS Lightsailの$3.5サーバとかでもよいので分ける方が好ましいです。レプリカセットは台数を増やすことが可能で、基本的にprimaryを決定する投票の関係で奇数台にします。

レプリカセット2台で構築するメリット

  • Dockerを使った方がMongoDBを簡単に起ち上げられる。
  • 安定運用のために2台以上のサーバでレプリカセット構築はしたいが、3台用意するのは(金銭的に)嫌な場合など、Dockerを使って安定的に運用できる。
  • 一度作ってしまえば、2台以上、上限の台数まで増やすのは容易。
  • Arbiterを2台中の1台にDockerで含めてしまっているが、本来の安定運用のためのArbiterを別サーバで運用するのも容易。
  • サーバ2台で運用するので、個人サービスでも安定するし、金銭的にも丁度よい落とし所。
  • サーバを2台にわけて片方で重い処理を行いながらMongoDBへ書き込み、もう一方の別サーバのMongoDBとデータを共有するといった使い方もできます。

MongoDBでレプリカセットをDockerで構築する具体的な手順

githubからソースコードをダウンロードして、摘便読み替えてください。

github: https://github.com/it-web-life/docker_mongodb_replicaset

レプリカセットを組んでいない、独立したMongoDB 3台をDockerで簡単に構築

今回は以下のような構成を組んでいます。server1, server2が独立して別サーバで起動するイメージです。

ローカルで動かすときは、defaultのbrigeネットワークでつながっているのでエイリアスでDockerネットワークでつながって動作します。

sever1
└mongodb (primary) ※ポート27017番を使用
sever2
└mongodb (secondary) ※ポート27018番を使用
└mongodb (arbiter) ※ポート27019番を使用

本来は27017番は攻撃されやすいから使わない方がよいとかありますので、必要に応じて変えてください。
今回は説明の便宜上、ローカルでバッティングしないようにポート27017から連番で設定しています。

事前準備1.extra_hosts」のIPアドレスを変更する。

server1/docker-compose.yml

version: '3.7'

services:
  mongodb_primary:
    build: mongodb
    image: mongodb-replset
    container_name: mongodb_primary
    hostname: mongodb_primary
    entrypoint: [ "mongod", "--config", "/etc/mongod_primary.conf" ]
    volumes:
      - ./mongodb/script:/srv/mongodb/script # Initialize script
      # - ./mongodb/etc/mongod-keyfile:/etc/mongod-keyfile:ro # 認証鍵作成後に有効化 Permission: 600
      - ./mongodb/db:/data/db # DBデータをマウント
      - ./mongodb/config/mongod_primary.conf:/etc/mongod_primary.conf # MongoDB設定
    ports:
      - 27017:27017
    # extra_hosts:
    #   - "mongodb_secondary:127.0.0.1" # 127.0.0.1部分をサーバのIPアドレスに書き換えてください
    #   - "mongodb_arbiter:127.0.0.1" # 127.0.0.1部分をサーバのIPアドレスに書き換えてください
    networks:
      - container-link
    restart: on-failure

networks:
  default:
    external:
      name: bridge
  container-link:
    name: container_network
  • サーバで起動する場合は「extra_hosts」のIPアドレス(サンプルコードで127.0.0.1となっている部分)を自分のサーバIPに変更してコメントアウトを外して有効化します。
    ローカルで試す場合は「extra_hosts」はコメントアウトします。
  • 認証鍵の部分はコメントアウトします。(githubコードでは最初コメントアウトされています)
    – ./mongodb/etc/mongod-keyfile:/etc/mongod-keyfile:ro

ここのdocker-compose.ymlについて、簡単に解説します。

  • entrypoint: [ “mongod”, “–config”, “/etc/mongod_primary.conf” ]
    →コンテナ起動時にMongoDB設定のconfファイルを読み込むようにしています。
  • volumes
    – ./mongodb/script:/srv/mongodb/script
    →後ほどレプリカセット構築で初期化に使うスクリプトを./mongodb/scriptに容易しているので、コンテナ内で使えるようにbind mountしています。
  • – ./mongodb/etc/mongod-keyfile:/etc/mongod-keyfile:ro
    →後ほどMongoDBの認証用キーとして作成するファイルで、これもMongoDBコンテナ内で使えるようbind mountしています。
  • – ./mongodb/db:/data/db
    →MongoDBのデータをコンテナが消えても大丈夫なようにbind mountしています。
  • – ./mongodb/config/mongod_primary.conf:/etc/mongod_primary.conf
    →「entrypoint」で読み込みするconfファイルをbind mountしています。
  • extra_hosts
    →エイリアスで通信できるようIPアドレスを指定しています。
    extra_hostsを使って、エイリアスにIPを割り当ててサーバ間で通信できるようにしています。

server2/docker-compose.yml

version: '3.7'

services:
  mongodb_secondary:
    build: mongodb
    image: mongodb-replset
    container_name: mongodb_secondary
    hostname: mongodb_secondary
    entrypoint: [ "mongod", "--config", "/etc/mongod_secondary.conf" ]
    volumes:
      # - ./mongodb/etc/mongod-keyfile:/etc/mongod-keyfile:ro # 認証鍵作成後に有効化 Permission: 600
      - ./mongodb/db:/data/db # DBデータをマウント
      - ./mongodb/config/mongod_secondary.conf:/etc/mongod_secondary.conf # MongoDB設定
    ports:
      - 27018:27018
    # extra_hosts:
    #   - "mongodb_primary:127.0.0.1" # 127.0.0.1部分をサーバのIPアドレスに書き換えてください
    networks:
      - container-link
    depends_on:
      - mongodb_arbiter
    restart: on-failure

  mongodb_arbiter:
    build: mongodb
    image: mongodb-replset
    container_name: mongodb_arbiter
    hostname: mongodb_arbiter
    entrypoint: [ "mongod", "--config", "/etc/mongod_arbiter.conf" ]
    volumes:
      # - ./mongodb/etc/mongod-keyfile:/etc/mongod-keyfile:ro # 認証鍵作成後に有効化 Permission: 600
      - ./mongodb/config/mongod_arbiter.conf:/etc/mongod_arbiter.conf # MongoDB設定
    ports:
      - 27019:27019
    # extra_hosts:
    #   - "mongodb_primary:127.0.0.1" # 127.0.0.1部分をサーバのIPアドレスに書き換えてください
    networks:
      - container-link
    restart: on-failure

networks:
  default:
    external:
      name: bridge
  container-link:
    name: container_network
  • サーバで起動する場合は「extra_hosts」のIPアドレス(サンプルコードで127.0.0.1となっている部分)を自分のサーバIPに変更してコメントアウトを外して有効化します。
    同じくローカルで試す場合は「extra_hosts」はコメントアウトします。
    他の内容についてはserve1と同じなので割愛します。
  • 同じく認証鍵の部分はコメントアウトします。(githubコードでは最初コメントアウトされています)
    – ./mongodb/etc/mongod-keyfile:/etc/mongod-keyfile:ro

事前準備2.confファイルの認証設定をコメントアウト

server1/mongodb/config/mongod_primary.conf
server2/mongodb/config/mongod_secondary.conf
server2/mongodb/config/mongod_arbiter.conf

上記3ファイルについて以下の部分をコメントアウトします(githubから落とした状態だとコメントアウト既にされています)

# security:
#   authorization: enabled
#   keyFile: /etc/mongod-keyfile

修正後のmongod_primary.conf の例(port設定以外は同じ)です。

# @see https://docs.mongodb.com/manual/reference/configuration-options/
replication:
  replSetName: "replset"
net:
  bindIpAll: true
  port: 27017
# 認証設定 (最初は無効化しておき、PRIMARYにて管理ユーザ作成後に有効化)
# security:
#   authorization: enabled
#   keyFile: /etc/mongod-keyfile

補足ですが、confにて、

net:
  bindIpAll: true

の設定により、サーバ間の通信を許可しています。今回は認証を入れるのですべてのIPを許可しています。

準備完了です。ローカルであればそのまま、サーバで動作させる場合はserve1,server2をそれぞれサーバにアップロードしてから始めます。

mongoDBイメージのビルド

docker-compose.ymlがあるディレクトリに移動します。

docker build -t mongodb-replset ./mongodb」をしてイメージをビルドします。
ローカル環境であればserve1server2のどちらかで1回行い(イメージは同じものを使うため)、サーバ上であればそれぞれでビルドをします。

$ docker build -t mongodb-replset ./mongodb

Successfully built fb1e787be2dc
Successfully tagged mongodb-replset:latest

secondary/arbiterの起動

先にsecondary/arbiter mongoDB側の起ち上げをします。

  • server2の「docker-compose.yml」があるディレクトリに移動します。
  • docker-compose up -d」コマンドでsecondary/arbiterを起動します。
  • docker ps」でstatusUpになっていて問題ないことを確認します。
$ docker-compose up -d

Creating network "container_network" with the default driver
Creating mongodb_arbiter ... done
Creating mongodb_secondary ... done

primaryの起動

primary mongoDBの起ち上げをします。

  • server1の「docker-compose.yml」があるディレクトリに移動します。
  • docker-compose up -d」コマンドでprimaryを起動します。
  • docker ps」でstatusUpになっていて問題ないことを確認します。
$ docker-compose up -d
Creating mongodb_primary ... done

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                 NAMES
a6959023402a        mongodb-replset     "mongod --config /et…"   47 seconds ago      Up 46 seconds       0.0.0.0:27017->27017/tcp              mongodb_primary
0b833250e0d0        mongodb-replset     "mongod --config /et…"   50 seconds ago      Up 49 seconds       27017/tcp, 0.0.0.0:27018->27018/tcp   mongodb_secondary
1777eab3ab67        mongodb-replset     "mongod --config /et…"   51 seconds ago      Up 49 seconds       27017/tcp, 0.0.0.0:27019->27019/tcp   mongodb_arbiter

MongoDBの起動に数分かかることもあるので、少し待ちます。

primary/secondary/arbiter それぞれで通信ができているかを確認する

通信の確認をします。
サーバのポート設定ができていないなどで、通信が正常にできていないとこの後の処理が正常にできないので、事前にサーバ間のMongoDB同士が通信できているか確認することが重要です。
疎通が出来ているか確認します。ncコマンド使えるようにDockerfilenetcatをインストールするよう記載しています。(githubのコードを使う場合は記載されているので特に何もしなくてよい)

mongodb_primary で確認する

docker exec -it mongodb_primary bash」コマンドでmongodb_primaryコンテナに入ります。

コンテナ内でsecondary, arbiterとの通信を確認します。(うまく行かない際にはMongoDBの起動中かもしれないので数分待ちます。それでもダメなら設定を見直します。)

# nc -vz mongodb_secondary 27018
# nc -vz mongodb_arbiter 27019

「ポート番号 (?) open」と表示がされればOKです。

$ docker exec -it mongodb_primary bash

root@mongodb_primary:/# nc -vz mongodb_secondary 27018
DNS fwd/rev mismatch: mongodb_secondary != mongodb_secondary.container_network
mongodb_secondary [172.23.0.3] 27018 (?) open

root@mongodb_primary:/# nc -vz mongodb_arbiter 27019
DNS fwd/rev mismatch: mongodb_arbiter != mongodb_arbiter.container_network
mongodb_arbiter [172.23.0.2] 27019 (?) open

正常に通信できていなそうな場合はサーバの設定などを確認します。正常に動作していないときは以下を確認します。

  • サーバのポート(今回だと27017-27019番ポート)は空いているか
    ※例えばさくらVPSだと管理画面でもポート開放の設定をしないといけないなどあります。
  • Dockerは問題なく立ち上がっているか。
    ※設定が間違っているとMongoDBコンテナがRestart… を繰り返してUpになっていないなどの場合もあります。
    (githubのコードをそのまま使っていれば初期設定は問題ないかと思います)

同様に「docker exec -it mongodb_secondary bash」コマンドでmongodb_secondaryコンテナに入って確認します。

# nc -vz mongodb_primary 27017
# nc -vz mongodb_arbiter 27019

mongodb_arbiterでも「docker exec -it mongodb_arbiter bash」コマンドでコンテナに入って確認します。

# nc -vz mongodb_primary 27017
# nc -vz mongodb_secondary 27018

$ docker exec -it mongodb_arbiter bash

root@mongodb_arbiter:/# nc -vz mongodb_primary 27017
DNS fwd/rev mismatch: mongodb_primary != mongodb_primary.container_network
mongodb_primary [172.23.0.4] 27017 (?) open

root@mongodb_arbiter:/# nc -vz mongodb_secondary 27018
DNS fwd/rev mismatch: mongodb_secondary != mongodb_secondary.container_network
mongodb_secondary [172.23.0.3] 27018 (?) open

すべて疎通OKなら次に進みます。exitで各コンテナから出ておきます。

認証なしでレプリカセットを構築する

レプリカセットを構築します。

server1のdocker-compose.yml ファイルがあるディレクトリに移動します。
初期化スクリプトの「001_init_replSet.js」を実行します。

$ docker-compose exec mongodb_primary mongo --port 27017 /srv/mongodb/script/001_init_replSet.js

MongoDB shell version v4.2.7
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("9d7d7601-bf07-4b01-890c-088d88fa46a6") }
MongoDB server version: 4.2.7

続いて管理者ユーザの作成を行います。
アクセス制御無効化状態でMongoDBのすべてのレプリカセットメンバーを起動中にprimaryに管理者ユーザの作成を行います。
必要に応じて実行前に server1/mongodb/script/002_creatAdmin.js user, pwdを書き換えてください。

coredb.createUser({
  user: 'admin',
  pwd: 'admin_password',

初期化スクリプトを実行します。

$ docker-compose exec mongodb_primary mongo --port 27017 /srv/mongodb/script/002_creatAdmin.js

MongoDB shell version v4.2.7
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("2633ad84-82bf-46ce-9244-476077fe8ed2") }
MongoDB server version: 4.2.7
Successfully added user: {
	"user" : "admin_user",
	"roles" : [
		"userAdminAnyDatabase",
		"dbAdminAnyDatabase",
		"clusterAdmin"
	]
}

レプリカセットが構築されました。「docker exec -it mongodb_primary bash」コマンドで、primary コンテナに入って「mongo admin -u admin_user -p admin_password」にて、確認します。

$ docker exec -it mongodb_primary bash

root@mongodb_primary:/# mongo admin -u admin_user -p admin_password
MongoDB shell version v4.2.7
connecting to: mongodb://127.0.0.1:27017/admin?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("e75bb003-b5d7-4cfb-9d63-1cca208173cd") }
MongoDB server version: 4.2.7
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
	http://docs.mongodb.org/
Questions? Try the support group
	http://groups.google.com/group/mongodb-user
Server has startup warnings:
2020-06-17T09:28:44.657+0000 I  CONTROL  [initandlisten]
2020-06-17T09:28:44.658+0000 I  CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2020-06-17T09:28:44.658+0000 I  CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2020-06-17T09:28:44.658+0000 I  CONTROL  [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2020-06-17T09:28:44.658+0000 I  CONTROL  [initandlisten]
---
Enable MongoDB's free cloud-based monitoring service, which will then receive and display
metrics about your deployment (disk utilization, CPU, operation statistics, etc).

The monitoring data will be available on a MongoDB website with a unique URL accessible to you
and anyone you share the URL with. MongoDB may use this information to make product
improvements and to suggest MongoDB products and deployment options to you.

To enable free monitoring, run the following command: db.enableFreeMonitoring()
To permanently disable this reminder, run the following command: db.disableFreeMonitoring()
---

replset:PRIMARY>

replset:PRIMARY>」の表示が出て、入れればOKです。

secondaryでも「docker exec -it mongodb_secondary bash」コマンドでコンテナに入り、「mongo –port 27018」で確認します。

$ docker exec -it mongodb_secondary bash

root@mongodb_secondary:/# mongo --port 27018
MongoDB shell version v4.2.7
connecting to: mongodb://127.0.0.1:27018/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("b5c9d9df-472d-4fa4-9cf4-6e63e9120a94") }
MongoDB server version: 4.2.7
Server has startup warnings:
2020-06-17T09:28:42.510+0000 I  CONTROL  [initandlisten]
2020-06-17T09:28:42.510+0000 I  CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2020-06-17T09:28:42.510+0000 I  CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2020-06-17T09:28:42.510+0000 I  CONTROL  [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
2020-06-17T09:28:42.510+0000 I  CONTROL  [initandlisten]
---
Enable MongoDB's free cloud-based monitoring service, which will then receive and display
metrics about your deployment (disk utilization, CPU, operation statistics, etc).

The monitoring data will be available on a MongoDB website with a unique URL accessible to you
and anyone you share the URL with. MongoDB may use this information to make product
improvements and to suggest MongoDB products and deployment options to you.

To enable free monitoring, run the following command: db.enableFreeMonitoring()
To permanently disable this reminder, run the following command: db.disableFreeMonitoring()
---

replset:SECONDARY>

replset:SECONDARY>」 の表示が出て、入れればOKです。

認証を作成する

次に認証を入れるので、一旦コンテナを落とします。

docker stop mongodb_primary
docker stop mongodb_secondary
docker stop mongodb_arbiter

ReplicaSetの認証鍵を作成します。
1つ作って、3台のMongoDBで共有します。
とりあえずprimaryで作るので server1/mongodb/etc ディレクトリを移動します。

openssl rand -base64 756 > mongod-keyfile」コマンドで認証鍵を作成します。そして「chmod 600 mongod-keyfile」コマンドで作成したファイルのパーミッションを600に変更します。

$ openssl rand -base64 756 > mongod-keyfile

$ ls
mongod-keyfile
$ chmod 600 mongod-keyfile

$ ls -l
total 8
-rw-------  1 xxx  staff  1024  6 17 18:42 mongod-keyfile

server1/mongodb/etc/mongod-keyfile にファイルが出来ていてパーミッションが問題ないことを確認します。

作成した「mongod-keyfile」をmongodb_secondary/mongodb_arbiter にコピーします。

サーバで作成した場合はローカルを経由するなどして摘便対応してください。

server2/mongodb/etc/mongod-keyfile にファイルがあり、パーミッションが同じく600になっていることを確認します。

confを認証設定に書き換える

次に、コンテナを落とした状態のままで、conf設定を書き換えて認証を有効にします。

confファイルの認証設定のコメントアウトを外して、有効にする

server1/mongodb/config/mongod_primary.conf
server2/mongodb/config/mongod_secondary.conf
server2/mongodb/config/mongod_arbiter.conf

上記3ファイルについて以下の部分のコメントアウトを外します。

security:
  authorization: enabled
  keyFile: /etc/mongod-keyfile

修正した各confファイルは以下のようになります。

server1/mongodb/config/mongod_primary.conf

# @see https://docs.mongodb.com/manual/reference/configuration-options/
replication:
  replSetName: "replset"
net:
  bindIpAll: true
  port: 27017
# 認証設定 (最初は無効化しておき、PRIMARYにて管理ユーザ作成後に有効化)
security:
  authorization: enabled
  keyFile: /etc/mongod-keyfile

server2/mongodb/config/mongod_secondary.conf

# @see https://docs.mongodb.com/manual/reference/configuration-options/
replication:
  replSetName: "replset"
net:
  bindIpAll: true
  port: 27018
# 認証設定 (最初は無効化しておき、PRIMARYにて管理ユーザ作成後に有効化)
security:
  authorization: enabled
  keyFile: /etc/mongod-keyfile

server2/mongodb/config/mongod_arbiter.conf

# @see https://docs.mongodb.com/manual/reference/configuration-options/
replication:
  replSetName: "replset"
net:
  bindIpAll: true
  port: 27019
# 認証設定 (最初は無効化しておき、PRIMARYにて管理ユーザ作成後に有効化)
security:
  authorization: enabled
  keyFile: /etc/mongod-keyfile

また、docker-compose.yml について、

- ./mongodb/etc/mongod-keyfile:/etc/mongod-keyfile:ro

のコメントアウトを外して有効にします。

server1/docker-compose.yml」「server2/docker-compose.yml」は変更後は以下のようになります。

server1/docker-compose.yml

version: '3.7'

services:
  mongodb_primary:
    build: mongodb
    image: mongodb-replset
    container_name: mongodb_primary
    hostname: mongodb_primary
    entrypoint: [ "mongod", "--config", "/etc/mongod_primary.conf" ]
    volumes:
      - ./mongodb/script:/srv/mongodb/script # Initialize script
      - ./mongodb/etc/mongod-keyfile:/etc/mongod-keyfile:ro # 認証鍵作成後に有効化 Permission: 600
      - ./mongodb/db:/data/db # DBデータをマウント
      - ./mongodb/config/mongod_primary.conf:/etc/mongod_primary.conf # MongoDB設定
    ports:
      - 27017:27017
    # extra_hosts:
    #   - "mongodb_secondary:127.0.0.1" # 127.0.0.1部分をサーバのIPアドレスに書き換えてください
    #   - "mongodb_arbiter:127.0.0.1" # 127.0.0.1部分をサーバのIPアドレスに書き換えてください
    networks:
      - container-link
    restart: on-failure

networks:
  default:
    external:
      name: bridge
  container-link:
    name: container_network

server2/docker-compose.yml

version: '3.7'

services:
  mongodb_secondary:
    build: mongodb
    image: mongodb-replset
    container_name: mongodb_secondary
    hostname: mongodb_secondary
    entrypoint: [ "mongod", "--config", "/etc/mongod_secondary.conf" ]
    volumes:
      - ./mongodb/etc/mongod-keyfile:/etc/mongod-keyfile:ro # 認証鍵作成後に有効化 Permission: 600
      - ./mongodb/db:/data/db # DBデータをマウント
      - ./mongodb/config/mongod_secondary.conf:/etc/mongod_secondary.conf # MongoDB設定
    ports:
      - 27018:27018
    # extra_hosts:
    #   - "mongodb_primary:127.0.0.1" # 127.0.0.1部分をサーバのIPアドレスに書き換えてください
    networks:
      - container-link
    depends_on:
      - mongodb_arbiter
    restart: on-failure

  mongodb_arbiter:
    build: mongodb
    image: mongodb-replset
    container_name: mongodb_arbiter
    hostname: mongodb_arbiter
    entrypoint: [ "mongod", "--config", "/etc/mongod_arbiter.conf" ]
    volumes:
      - ./mongodb/etc/mongod-keyfile:/etc/mongod-keyfile:ro # 認証鍵作成後に有効化 Permission: 600
      - ./mongodb/config/mongod_arbiter.conf:/etc/mongod_arbiter.conf # MongoDB設定
    ports:
      - 27019:27019
    # extra_hosts:
    #   - "mongodb_primary:127.0.0.1" # 127.0.0.1部分をサーバのIPアドレスに書き換えてください
    networks:
      - container-link
    restart: on-failure

networks:
  default:
    external:
      name: bridge
  container-link:
    name: container_network

認証有効状態で各コンテナを起動する

コンテナを順番に起動します。

起動する順番で、primaryが決定するので、順番に注意して起ち上げます。

mongodb_arbiter -> mongodb_primary -> mongodb_secondary の順にコンテナを起動します。

それぞれのdocker-compose.yml ファイルがあるディレクトリに移動して起動します。

※volumes設定を変えているので「docker-compose up」で設定を反映させます。

docker-compose up -d mongodb_arbiter
docker-compose up -d mongodb_primary
docker-compose up -d mongodb_secondary

primary, secondary, arbiterすべてのコンテナが起動して、MongoDBコンテナに入れるかを確認します。

※再起動の直後は立ち上がるまでmongoに入れなくなることがあるので、mongoコマンドで入れなくても2〜3分待ってから再度実行して確認すると入れることがあります。

各コンテナで確認します。

docker exec -it mongodb_primary bash」コマンドでprimary コンテナに入ります。
mongo –port 27017」mongoコマンドで入れるか確認して、primaryになっているか確認します。

$ docker exec -it mongodb_primary bash

root@mongodb_primary:/# mongo --port 27017
MongoDB shell version v4.2.7
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("a20b4e77-1e0e-43f6-97c2-9af0c5c6acbd") }
MongoDB server version: 4.2.7
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
	http://docs.mongodb.org/
Questions? Try the support group
	http://groups.google.com/group/mongodb-user
replset:PRIMARY>

replset:PRIMARY>」の表示になっていたらプライマリになっています。
もしsecondaryになっていたら、再度、「docker stop」 でコンテナを停止して、
mongodb_arbiter -> mongodb_primary -> mongodb_secondary の順に「docker start」で各コンテナを起動し直して再度確認します。
docker exec -it mongodb_secondary bash」コマンドで、secondary コンテナに入ります。
mongo –port 27018」mongoコマンドで入れるか確認します。

$ docker exec -it mongodb_secondary bash

root@mongodb_secondary:/# mongo --port 27018
MongoDB shell version v4.2.7
connecting to: mongodb://127.0.0.1:27018/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("f763767d-4c55-408b-9c08-dbf66ff1a152") }
MongoDB server version: 4.2.7
replset:SECONDARY>

docker exec -it mongodb_arbiter bash」コマンドで、arbiter コンテナに入ります。
mongo –port 27019」mongoコマンドで入れるか確認します。

$ docker exec -it mongodb_arbiter bash

root@mongodb_arbiter:/# mongo --port 27019
MongoDB shell version v4.2.7
connecting to: mongodb://127.0.0.1:27019/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("143a9b96-429c-4d59-8257-5abddd08ac8a") }
MongoDB server version: 4.2.7
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
	http://docs.mongodb.org/
Questions? Try the support group
	http://groups.google.com/group/mongodb-user
replset:ARBITER>

すべて問題ないことを確認できたら、exitして次に進みます。

ユーザの作成を行う

server1 primarydocker-compose.ymlがあるディレクトリに移動します。
以下、MongoDBのスクリプトを順番に実行します。

Collection作成
docker-compose exec mongodb_primary mongo admin -u admin_user -p admin_password /srv/mongodb/script/003_init_database.js

$ docker-compose exec mongodb_primary mongo admin -u admin_user -p admin_password /srv/mongodb/script/003_init_database.js

MongoDB shell version v4.2.7
connecting to: mongodb://127.0.0.1:27017/admin?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("700d1f79-d295-4cc8-99bd-0bd74832b877") }
MongoDB server version: 4.2.7

ユーザ設定
docker-compose exec mongodb_primary mongo admin -u admin_user -p admin_password /srv/mongodb/script/004_init_user.js

$ docker-compose exec mongodb_primary mongo admin -u admin_user -p admin_password /srv/mongodb/script/004_init_user.js

MongoDB shell version v4.2.7
connecting to: mongodb://127.0.0.1:27017/admin?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("f96a02de-6776-4703-b258-fdf1f0d1dded") }
MongoDB server version: 4.2.7
Successfully added user: {
	"user" : "user",
	"roles" : [
		{
			"role" : "readWrite",
			"db" : "test_db"
		}
	]
}

レプリカセットが問題なく構築されているかを確認する

最後にレプリカセット構築が問題ないか確認します。
docker exec -it mongodb_primary bash」コマンドでprimary コンテナに入ります。

mongo –port 27017 -u user -p user_password –authenticationDatabase test_db
mongoコマンドで入れるか確認します。

$ docker exec -it mongodb_primary bash

root@mongodb_primary:/# mongo --port 27017 -u user -p user_password --authenticationDatabase test_db
MongoDB shell version v4.2.7
connecting to: mongodb://127.0.0.1:27017/?authSource=test_db&compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("34e78d7b-6b8e-4817-8d9a-965e7b6657b6") }
MongoDB server version: 4.2.7
replset:PRIMARY>
replset:PRIMARY> use test_db
switched to db test_db
replset:PRIMARY> show collections
test_collection
replset:PRIMARY>

また「rs.status()」で状態を確認できます。「rs.conf()」でレプリカセットの構築状況を確認できます。

replset:PRIMARY> rs.status()

{
	"operationTime" : Timestamp(1592388190, 1),
	"ok" : 0,
	"errmsg" : "not authorized on admin to execute command { replSetGetStatus: 1.0, lsid: { id: UUID(\"34e78d7b-6b8e-4817-8d9a-965e7b6657b6\") }, $clusterTime: { clusterTime: Timestamp(1592388120, 1), signature: { hash: BinData(0, DCDC18FFCB6E8D8AD00661F5B4BE1160F2040A53), keyId: 6839247051033673731 } }, $db: \"admin\" }",
	"code" : 13,
	"codeName" : "Unauthorized",
	"$clusterTime" : {
		"clusterTime" : Timestamp(1592388190, 1),
		"signature" : {
			"hash" : BinData(0,"WBQ/7B7pATqWGxkhahGq2ZjIntw="),
			"keyId" : NumberLong("6839247051033673731")
		}
	}
}

replset:PRIMARY> rs.conf()

2020-06-17T10:03:25.361+0000 E  QUERY    [js] uncaught exception: Error: Could not retrieve replica set config: {
	"operationTime" : Timestamp(1592388200, 1),
	"ok" : 0,
	"errmsg" : "not authorized on admin to execute command { replSetGetConfig: 1.0, lsid: { id: UUID(\"34e78d7b-6b8e-4817-8d9a-965e7b6657b6\") }, $clusterTime: { clusterTime: Timestamp(1592388190, 1), signature: { hash: BinData(0, 58143FEC1EE9013A961B19216A11AAD998C89EDC), keyId: 6839247051033673731 } }, $db: \"admin\" }",
	"code" : 13,
	"codeName" : "Unauthorized",
	"$clusterTime" : {
		"clusterTime" : Timestamp(1592388200, 1),
		"signature" : {
			"hash" : BinData(0,"WEJg66Ii3ereyEl9ei6eKE5awg8="),
			"keyId" : NumberLong("6839247051033673731")
		}
	}
} :
rs.conf@src/mongo/shell/utils.js:1531:11
@(shell):1:1

次に「docker exec -it mongodb_secondary bash」コマンドで、secondary コンテナに入ります。
mongo –port 27018 -u user -p user_password –authenticationDatabase test_db
mongoコマンドで入れるか確認します。

$ docker exec -it mongodb_secondary bash

root@mongodb_secondary:/# mongo --port 27018 -u user -p user_password --authenticationDatabase test_db
MongoDB shell version v4.2.7
connecting to: mongodb://127.0.0.1:27018/?authSource=test_db&compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("42a478fc-4760-4c6a-9201-a1c408b720e3") }
MongoDB server version: 4.2.7
replset:SECONDARY> 

レプリカセットのsecondaryで操作するにはまず「rs.slaveOk()を実行して読み取り専用であることを宣言する必要があります
あとは 「use データベース名(test_db)」とすれば、「show collection」などMongoのコマンドを使って確認ができるようになります。

replset:SECONDARY> rs.slaveOk()

replset:SECONDARY> use test_db
switched to db test_db
replset:SECONDARY> show collections
test_collection
replset:SECONDARY> rs.status()
{
	"operationTime" : Timestamp(1592388320, 1),
	"ok" : 0,
	"errmsg" : "not authorized on admin to execute command { replSetGetStatus: 1.0, lsid: { id: UUID(\"42a478fc-4760-4c6a-9201-a1c408b720e3\") }, $clusterTime: { clusterTime: Timestamp(1592388300, 1), signature: { hash: BinData(0, 0570F002DAD57C17B32B57E6094F1FB897050213), keyId: 6839247051033673731 } }, $readPreference: { mode: \"secondaryPreferred\" }, $db: \"admin\" }",
	"code" : 13,
	"codeName" : "Unauthorized",
	"$clusterTime" : {
		"clusterTime" : Timestamp(1592388320, 1),
		"signature" : {
			"hash" : BinData(0,"c8rPhPYK8mqp4gWFhUtxJ8Kq8vw="),
			"keyId" : NumberLong("6839247051033673731")
		}
	}
}

以上で構築は完了となります。

Dockerでのレプリカセット構築時の注意点

構築時にハマったところを共有します。

  • 初期化のスクリプトをread onlyでコンテナの /root 内にbind mountしていたのですが、
    STORAGE  [main] In File::open(), ::open for ‘/root/.mongorc.js’ failed with Read-only file system
    というエラーが出てしまったことがあります。その時は/rootのマウントを外して解消できました。
    /root でのマウントは避けた方がよさそうです。
  • 認証鍵(今回作成したmongod-keyfile)はパーミッション設定を正確に設定しないとエラーが出ます。600よりも大きい値にしたら許可しすぎでエラーになります。
  • 1台のサーバ内でDockerによりmongoDBを3台起動する場合は、docker-compose.yml の記述をして立ち上げるだけで正常に動作しましたが、2台以上のサーバにわけて構築する場合は、手動の設定を挟まないと僕の場合は正常に起ち上げできませんでした。
    面倒ですがサーバをまたぐ場合は、手動で順番に設定をする必要がありそうです。

以上となります。

お疲れさまでした!