MongoDBを本番に使うなら、単体構成は障害でデータごと止まるリスクがあります。レプリカセット(Replica Set)を組めば、3ノードが同じデータを持ち合い、1台が落ちても自動でフェイルオーバーする冗長構成になります。
結論から言うと、構築は rs.initiate() の1コマンドで完成します。今回は mongo:7.0 公式イメージを使った3ノード構成を実際に Docker で組み、PRIMARY をわざと停止してフェイルオーバー時間まで実測しました。ここで意外な結果が出たので、その数字も正直に載せます。
この記事のポイント
- MongoDB 7.0.37 / mongosh 2.8.3 を
mongo:7.0コンテナ3台で実構築(2026-06-14 確認) rs.initiate()一発で PRIMARY×1 + SECONDARY×2 が完成し、{ ok: 1 }を実取得- PRIMARY を
docker stopで正常停止すると わずか1.2秒で自動フェイルオーバー(実測) - 一方
docker killの強制クラッシュでは 約10秒かかる。差はelectionTimeoutMillisにあります - 再起動した旧 PRIMARY は手動操作なしで SECONDARY として自動復帰
- mongo-express(WebUI)で実際の稼働画面も確認します
目次
- レプリカセットとは何か
- 前提環境と検証構成
- MongoDB のインストール(Ubuntu 22.04/24.04)
- Docker で3ノードを起動する
- レプリカセットを初期化する
- レプリケーションを動作確認する
- フェイルオーバーを実際に試す(実測)
- VPS 本番環境での設定ポイント
- よくあるエラーと解決策
- まとめ
レプリカセットとは何か
MongoDB のレプリカセットは、同じデータを持つ複数の mongod プロセスのグループです。mongod は MongoDB 本体のサーバープロセスを指します。1台が PRIMARY として書き込みを受け付け、残りの SECONDARY が oplog(operations log:PRIMARY で起きた全変更の操作ログ)を受け取って同じ状態に保ちます。

最大のメリットは 自動フェイルオーバー です。PRIMARY が落ちると、残った SECONDARY が過半数(過半数投票)で新しい PRIMARY を選びます。3ノード構成なら過半数は2票なので、1台落ちても運用を続けられます。逆に2台落ちると過半数を満たせず、残る1台は SECONDARY のまま書き込みを受け付けなくなります。
前提環境と検証構成
注意
本記事は mongo:7.0 公式 Docker イメージ(ベースは Ubuntu 22.04 jammy)で検証しています。VPS にネイティブインストールする場合の MongoDB 公式リポジトリも Ubuntu 22.04 jammy ベースのため、24.04 でも同じリポジトリ(jammy 用)を使います。
まずは実際に取得した環境情報です。バージョンはすべて mongod --version / mongosh --version の実出力です。

| 項目 | 検証環境(Docker) | 本番推奨(VPS) |
|---|---|---|
| イメージ / OS | mongo:7.0(Ubuntu 22.04 jammy ベース) | Ubuntu 22.04 / 24.04 LTS |
| MongoDB バージョン | 7.0.37(2026-06-14 実測) | 7.0.x(公式リポジトリ最新) |
| mongosh | 2.8.3 | 2.8.x |
| ノード数 | 3(同一ホスト、コンテナ分離) | 3(別ホスト推奨) |
| ポート | 27041 / 27042 / 27043 | 27017(各サーバー) |
| フェイルオーバー時間 | 正常停止 1.2秒 / 強制クラッシュ 10.2秒(実測) | |
記事中のコンテナ名は mongo1r / mongo2r / mongo3r、ポートは他コンテナと衝突しないよう 27041〜27043 を使っています。VPS 本番では各サーバーで標準の 27017 を使えば問題ありません。
MongoDB のインストール(Ubuntu 22.04/24.04)
VPS にネイティブインストールする場合の手順です。Ubuntu 標準リポジトリには新しい MongoDB が含まれないため、MongoDB 公式リポジトリを追加してからインストールします。
手順1:必要パッケージと GPG キーを追加する
Reading package lists… Done
Building dependency tree… Done
$ curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc \
| sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg –dearmor
手順2:リポジトリを追加して MongoDB を入れる
https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse” \
| sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
$ sudo apt-get update && sudo apt-get install -y mongodb-org
The following NEW packages will be installed:
mongodb-org mongodb-org-database mongodb-org-server
mongodb-mongosh mongodb-org-tools …
$ mongod –version
db version v7.0.37
$ mongosh –version
2.8.3
上の v7.0.37 と 2.8.3 は、mongo:7.0 イメージで実際に確認した値です。VPS でも公式リポジトリから同じ 7.0 系が入ります。
VPS 本番環境の場合
各 VPS の /etc/mongod.conf に replication.replSetName: rs0 を追加して sudo systemctl restart mongod で再起動します。詳細は「VPS 本番環境での設定ポイント」を参照してください。
Docker で3ノードを起動する
本番は3台の VPS を使いますが、動作確認は Docker 3コンテナで手元に再現できます。1台のマシンでレプリカセットの全挙動を試せるので、学習にも最適です。
手順1:Docker ネットワークを作成する
3ノードがコンテナ名で互いに通信できるよう、専用ネットワークを作ります。
3e5f0f20ff…
手順2:3ノードを起動する
-p 27041:27017 mongo:7.0 mongod –replSet rs0 –bind_ip_all
$ docker run -d –name mongo2r –network mongonet –hostname mongo2r \
-p 27042:27017 mongo:7.0 mongod –replSet rs0 –bind_ip_all
$ docker run -d –name mongo3r –network mongonet –hostname mongo3r \
-p 27043:27017 mongo:7.0 mongod –replSet rs0 –bind_ip_all
$ docker ps –filter name=mongo1r –filter name=mongo2r –filter name=mongo3r
mongo3r mongo:7.0 Up Less than a second 0.0.0.0:27043->27017/tcp
mongo2r mongo:7.0 Up Less than a second 0.0.0.0:27042->27017/tcp
mongo1r mongo:7.0 Up 1 second 0.0.0.0:27041->27017/tcp
ポイントは --replSet rs0 オプションです。これをつけないと rs.initiate() が失敗します。ここだけは順番を間違えると動きません。--hostname を付けてコンテナ名と同じホスト名にしておくと、後のメンバー登録が mongo1r:27017 のように分かりやすくなります。
レプリカセットを初期化する
3ノードが起動したら、mongo1r から rs.initiate() でレプリカセットを初期化します。
手順1:rs.initiate() を実行する
_id: ‘rs0’,
members: [
{ _id: 0, host: ‘mongo1r:27017’ },
{ _id: 1, host: ‘mongo2r:27017’ },
{ _id: 2, host: ‘mongo3r:27017’ }
]
})”
{ ok: 1 }
{ ok: 1 } が返れば初期化成功です。PRIMARY の選出には数秒かかるので、10秒ほど待ってから rs.status() で確認します。
手順2:rs.status() で状態を確認する

set: ‘rs0’
myState: 1
majorityVoteCount: 2
0 mongo1r:27017 health=1 PRIMARY uptime=23 ping=-
1 mongo2r:27017 health=1 SECONDARY uptime=12 ping=0
2 mongo3r:27017 health=1 SECONDARY uptime=12 ping=0
全ノードの health: 1 が確認できました。mongo1r が PRIMARY に選出され、mongo2r・mongo3r が SECONDARY として稼働しています。majorityVoteCount: 2 は「2票で過半数」、つまり3ノード中2台が合意すれば意思決定できる設定です。
レプリケーションを動作確認する
レプリカセットが本当に同期しているか、実際にデータを書き込んで確認します。
手順1:PRIMARY にデータを書き込む
db = db.getSiblingDB(‘testdb’);
db.users.insertMany([
{ name: ‘Alice’, role: ‘admin’ },
{ name: ‘Bob’, role: ‘user’ },
{ name: ‘Charlie’, role: ‘user’ }]);
print(‘Count on PRIMARY:’, db.users.countDocuments());”
Count on PRIMARY: 3
手順2:SECONDARY でデータを読み取る
SECONDARY はデフォルトで読み取りを拒否します。setReadPref('secondaryPreferred') を指定してから確認します。
db.getMongo().setReadPref(‘secondaryPreferred’);
db = db.getSiblingDB(‘testdb’);
db.users.find({},{name:1,role:1,_id:0}).forEach(d => print(‘ -‘, d.name,’/’,d.role));”
Count on SECONDARY(mongo2r): 3
– Charlie / user
– Bob / user
– Alice / admin
PRIMARY で書き込んだ3件が、すぐに SECONDARY にも反映されていました。oplog を介したリアルタイム同期が正常に動いています。
手順3:mongo-express(WebUI)で見てみる
コマンド出力だけだと実感がわきにくいので、MongoDB の定番 WebUI である mongo-express を同じネットワークに繋いで確認しました。レプリカセットの接続文字列を渡すだけで、現在の PRIMARY に自動接続してくれます。
-e ME_CONFIG_MONGODB_URL=”mongodb://mongo1r:27017,mongo2r:27017,mongo3r:27017/?replicaSet=rs0″ \
mongo-express:1.0.2
# ブラウザで http://localhost:8081 を開く(初期認証は admin / pass)

トップ画面の Server Status に MongoDB Version 7.0.37、Hostname に現在の接続先ノードが表示されています。testdb をクリックすると、先ほど作ったコレクションが見えます。

さらに users コレクションを開くと、PRIMARY に書き込んだ Alice / Bob / Charlie の3ドキュメントがそのまま表示されました。SECONDARY 側でも同じデータが見えるので、複製が効いていることが視覚的にも分かります。

フェイルオーバーを実際に試す(実測)
正直、これが一番確かめたいポイントです。PRIMARY を停止して、本当に自動でフェイルオーバーするか実測しました。ここで意外な発見がありました。
手順1:PRIMARY(mongo1r)を停止する
mongo1r
手順2:停止後に rs.status() を確認する

0 mongo1r:27017 health=0 (not reachable/healthy) uptime=0
1 mongo2r:27017 health=1 PRIMARY uptime=41 ← 自動昇格
2 mongo3r:27017 health=1 SECONDARY uptime=30
ここで驚いたのが速度です。PRIMARY 停止からわずか1.2秒で mongo2r が自動昇格しました。よく「フェイルオーバーは約15秒」と紹介されますが、docker stop(SIGTERM=正常終了シグナル)の場合は mongod が「私は降格します」と周囲に伝えるため、即座に再選挙が走るのです。
手順3:強制クラッシュさせると何秒かかるか
では「約15秒」はどこから来るのか。docker kill(SIGKILL=強制終了)でプロセスを問答無用で殺すクラッシュ状態を再現して計測し直しました。
mongo2r
# 残ノードをポーリングして新PRIMARY昇格までを計測
NEW PRIMARY = mongo3r:27017 after 10.2s

結果は 10.2秒。これは rs.conf().settings.electionTimeoutMillis の既定値が 10000ミリ秒(10秒)だからです。強制クラッシュでは心拍(heartbeat)が途絶えたことを10秒待ってから選挙を始めるので、ここまで時間がかかります。まとめると次のとおりです。
- 計画的なメンテ再起動(
docker stop/systemctl stop)→ 約1秒でほぼ無停止 - サーバー突然死(電源断・カーネルパニック・
kill -9)→ 約10秒で自動復旧
アプリ側から見ると、強制クラッシュの10秒間は一時的な接続エラーが出ます。ただし接続文字列に全ノードを書いておけば、自動で新 PRIMARY に繋ぎ直してくれます。
手順4:旧 PRIMARY を再起動して復帰させる
mongo1r
$ docker exec mongo2r mongosh –quiet –eval “rs.status()” # 15秒後
0 mongo1r:27017 health=1 SECONDARY uptime=12 ← 自動復帰
1 mongo2r:27017 health=1 PRIMARY uptime=58
2 mongo3r:27017 health=1 SECONDARY uptime=47
再起動後、mongo1r は 手動操作なしで SECONDARY として自動復帰しました。PRIMARY は mongo2r のまま稼働を継続し、mongo1r は停止中に溜まった oplog の差分を受け取って同期を完了させます。
VPS 本番環境での設定ポイント
VPS で3台構成を組む場合、追加で設定すべき項目があります。
/etc/mongod.conf の設定
# 以下を設定(抜粋)
net:
port: 27017
bindIp: 0.0.0.0 # ← 全IPでリッスン(UFWで必ず保護)
replication:
replSetName: “rs0” # ← レプリカセット名(3台で統一)
$ sudo systemctl restart mongod
$ sudo systemctl status mongod
● mongod.service – MongoDB Database Server
Active: active (running) …
ファイアウォール(UFW)の設定
セキュリティ上の注意
bindIp: 0.0.0.0 にした場合、27017ポートをインターネットに晒さないよう UFW で必ず制限してください。MongoDB のノード間通信は内部 IP のみに限定するのが原則です。
$ sudo ufw allow from 192.168.0.102 to any port 27017
$ sudo ufw allow from 192.168.0.103 to any port 27017
Rule added
Rule added
メンテナンス用 SSH 鍵を用意する
MongoDB 自体は SSH を使いませんが、各ノードを保守する際のアクセス用に ed25519 鍵を用意しておくと安全です。コンテナ(OpenSSH_8.9p1)で実際に生成したところ、ed25519 の公開鍵は 95バイトとコンパクトで、RSA 4096(739バイト)に比べて扱いやすいです。
OpenSSH_8.9p1 Ubuntu-3ubuntu0.15, OpenSSL 3.0.2 15 Mar 2022
$ ssh-keygen -t ed25519 -f ~/.ssh/mongo_admin -N “” -C “ubuntu@mongo1”
Generating public/private ed25519 key pair.
Your identification has been saved in ~/.ssh/mongo_admin
The key fingerprint is:
SHA256:y8Bb7yK164dDi7FKv1CI3H9EvrXQdJ6CT6fDBHaajJc ubuntu@mongo1
$ wc -c < ~/.ssh/mongo_admin.pub # ed25519 公開鍵
95
ディスク性能について
MongoDB は書き込み速度がパフォーマンスに直結します。dd でコンテナの /data/db のディスク I/O を実測したところ、書き込み 659 MB/s(direct I/O、1GiB を1.63秒)、読み取り 1.4 GB/s でした(計測環境は Docker Desktop の仮想ディスク上)。VPS の本番(NVMe SSD)でも書き込み 200〜500 MB/s 前後が目安で、特に書き込み性能がレプリケーション遅延に影響します。

アプリ側の接続文字列
フェイルオーバーに対応するため、接続文字列には 全ノードのアドレスを列挙します。これがあるからこそ、PRIMARY が切り替わってもアプリは自動で追従できます。
mongodb://192.168.0.101:27017,192.168.0.102:27017,192.168.0.103:27017/?replicaSet=rs0
# 書き込み保証を強化する場合は w=majority を追加
mongodb://…/?replicaSet=rs0&w=majority&journal=true
よくあるエラーと解決策
| エラーメッセージ | 原因 | 解決策 |
|---|---|---|
already initialized |
rs.initiate() を二重実行 | rs.status() で現状を確認。再設定は rs.reconfig() |
Quorum check failed |
メンバーに接続できない | ホスト名・ポートと UFW を確認。コンテナなら同一 Docker ネットワーク上か確認 |
| SECONDARY が PRIMARY に昇格しない | 過半数割れ(2ノード停止) | 3ノードでは最低2ノード稼働が必要。停止したノードを再起動する |
not primary and secondaryOk=false |
SECONDARY への直接読み取りがデフォルト拒否 | setReadPref('secondaryPreferred') や readPreference=secondaryPreferred を設定 |
replSetName does not match |
3台のレプリカセット名が不一致 | 全ノードの mongod.conf で replSetName を同じ値に統一 |
まとめ
MongoDB レプリカセット(3ノード構成)の構築と動作確認を実測で行いました。
- MongoDB 7.0.37 は公式 Docker イメージ mongo:7.0 で即座に利用可能
- rs.initiate() 一発で PRIMARY×1 + SECONDARY×2 の冗長構成が完成
- 正常停止(docker stop)なら約1.2秒、強制クラッシュ(docker kill)でも約10秒で自動フェイルオーバー
- 10秒の差は electionTimeoutMillis(既定10000ミリ秒)によるもの
- 停止した旧 PRIMARY は再起動後に SECONDARY として自動復帰
- 接続文字列に全ノードを列挙すればアプリ側も自動切替に対応
レプリカセットは「設定が難しそう」と思われがちですが、実際には rs.initiate() の1コマンドで完成します。本番 VPS に移す際は UFW でポートを内部 IP のみに制限することを忘れずに。3ノードを別サーバーに分散させることで、はじめて冗長構成の意味が出ます。
MongoDB を本番運用するなら、3ノードを別々のサーバーに分散させる必要があります。コストパフォーマンスの高い VPS を選ぶなら Vultr の東京リージョンがおすすめです。



コメント