2026年10月8日
2026年10月8日
MongoDBレプリカセットの構築手順:3台構成・キーファイル認証・フェイルオーバー試験
MongoDB で高可用性を得る標準的な方法は、3台以上の mongod でレプリカセットを組むことです。プライマリが停止しても、残りのメンバーが選挙で新しいプライマリを選び、アプリはドライバーが自動で新しいプライマリに接続し直します。この記事では、3台構成のレプリカセットを内部認証(キーファイル)付きで構築し、管理ユーザーとアプリ用ユーザーを作り、状態確認とフェイルオーバーの試験、運用で気を付ける点までを説明します。
なお、WordPress は MongoDB を使いません。この記事は、WordPress と並行して運用しているアプリケーションやログ基盤などで MongoDB を使っている場合を想定しています。
レプリカセットの仕組みと台数の考え方
レプリカセットは、書き込みを受け付ける1台のプライマリと、その変更履歴(oplog)を複製し続けるセカンダリで構成されます。プライマリが応答しなくなると、投票権を持つメンバーの過半数の合意で新しいプライマリが選ばれます。
過半数が必要なので、投票メンバーの数は奇数にするのが基本です。2台だと1台が落ちた時点で過半数(2票中2票)を満たせず、自動で切り替わりません。最小構成は「データを持つ3台(プライマリ1+セカンダリ2)」です。
コストを抑えるために、データを持たない投票専用メンバー(アービター)を加える「プライマリ+セカンダリ+アービター」構成もあります。ただしこの構成では、データを持つメンバーが1台止まると、書き込み確認を過半数で取る設定(w: "majority")の書き込みが完了しなくなるなどの制約があり、公式ドキュメントにも注意書きがあります。可能であればデータを持つ3台で組んでください。
また、3台を同じ物理ホストや同じラックに置くと、ホスト障害でまとめて止まります。可用性が目的なら、別ホスト、可能なら別のアベイラビリティゾーンに分散させます。
事前準備
ここでは次の3台を例にします。ホスト名は自分の環境に置き換えてください。
mongo1.example.internalmongo2.example.internalmongo3.example.internal
準備として確認しておくことは3つです。
- 名前解決: 3台が互いのホスト名を解決でき、アプリサーバーからも同じ名前で引けること。レプリカセットのメンバー登録に
localhostや127.0.0.1を使うと、外部のクライアントが接続先を見つけられなくなります。 - ネットワーク: 27017 番ポートを、メンバー同士とアプリサーバーにだけ開けること。インターネットに公開しないでください。
- バージョン: 3台とも同じバージョンの MongoDB をインストールすること。インストール手順は公式ドキュメントのディストリビューション別の手順に従います。
既存のデータがあるサーバーで作業する場合は、先に mongodump やスナップショットでバックアップを取ってください。
キーファイルを作成して配布する
メンバー同士の通信を認証するために、共通のキーファイルを使います。1台目で作成します。
sudo mkdir -p /etc/mongodb
openssl rand -base64 756 | sudo tee /etc/mongodb/keyfile > /dev/null
sudo chmod 400 /etc/mongodb/keyfile
sudo chown mongodb:mongodb /etc/mongodb/keyfile
同じファイルを残りの2台にも、同じパス・同じ権限で置きます。scp などで安全な経路を使ってコピーし、コピー先でも chmod 400 と chown を実行してください。
mongod を動かす OS ユーザー名は、Ubuntu / Debian の公式パッケージでは mongodb、RHEL 系では mongod であることが多いので、ps -o user= -C mongod などで確認して合わせてください。ディレクトリ /etc/mongodb は例です。キーファイルのパーミッションが緩いと mongod が起動を拒否します。
キーファイルは手軽ですが、全メンバーで同じ秘密を共有する方式です。本番では、メンバー間認証に x.509 証明書を使い、通信も TLS で暗号化する構成のほうが望ましいとされています。まずキーファイルで動作を理解し、本番前に TLS 化を検討する進め方が現実的です。
mongod.conf を設定する
3台とも /etc/mongod.conf に次の設定を加えます。YAML なのでインデントはスペースで揃えます。
net:
port: 27017
bindIp: 127.0.0.1,mongo1.example.internal # 各ホストで自分のホスト名に変える
security:
keyFile: /etc/mongodb/keyfile
authorization: enabled
replication:
replSetName: rs0
replSetName は3台で同じ名前にします。keyFile を指定するとアクセス制御も有効になるため、ユーザーを作るまではログインできるユーザーがいない状態になります(後述の localhost 例外で最初のユーザーを作ります)。
設定後、3台とも再起動して起動を確認します。
sudo systemctl restart mongod
sudo systemctl status mongod
起動しない場合は、/var/log/mongodb/mongod.log(パスは mongod.conf の systemLog.path)でキーファイルの権限や YAML の書式エラーを確認します。
レプリカセットを初期化する
1台目(ここでは mongo1)にログインし、localhost から mongosh で接続して初期化します。古い mongo シェルは新しいバージョンには含まれないため、mongosh を使います。
mongosh --port 27017
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "mongo1.example.internal:27017" },
{ _id: 1, host: "mongo2.example.internal:27017" },
{ _id: 2, host: "mongo3.example.internal:27017" }
]
})
数秒から十数秒でプライマリが選ばれます。プロンプトが rs0 [direct: primary] のような表示になれば初期化完了です(表示形式は mongosh のバージョンで異なります)。rs.initiate() は1台目だけで実行し、ほかの2台では実行しません。
管理ユーザーとアプリ用ユーザーを作る
続けて、同じ mongosh のセッション(プライマリ上)で最初の管理ユーザーを作成します。ユーザーがまだ1人もいない状態では、localhost からの接続に限って最初のユーザー作成が許されます(localhost 例外)。
use admin
db.createUser({
user: "admin",
pwd: passwordPrompt(),
roles: [ { role: "root", db: "admin" } ]
})
passwordPrompt() を使うと、パスワードがシェルの履歴に残りません。最初のユーザーを作ると localhost 例外は使えなくなるので、次からは作成した管理ユーザーで認証して操作します。
mongosh "mongodb://mongo1.example.internal:27017/?replicaSet=rs0" -u admin --authenticationDatabase admin
アプリには管理ユーザーを使わせず、必要なデータベースに対する readWrite だけを持つ専用ユーザーを作ります。ここではアプリのデータベースを appdb、ユーザー名を appuser とします。
use appdb
db.createUser({
user: "appuser",
pwd: passwordPrompt(),
roles: [ { role: "readWrite", db: "appdb" } ]
})
ユーザーは use appdb の状態で作ったので、認証データベース(authSource)は appdb になります。後で出てくる接続文字列の authSource=appdb はこれに合わせたものです。ユーザー情報はレプリカセット全体に複製されるので、ほかのメンバーで作り直す必要はありません。
状態を確認する
管理ユーザーで接続した状態で、メンバーの状態を確認します。
rs.status().members.map(m => ({ name: m.name, state: m.stateStr, health: m.health }))
rs.printSecondaryReplicationInfo()
1台が PRIMARY、2台が SECONDARY で、health がすべて 1 なら正常です。rs.printSecondaryReplicationInfo() では、各セカンダリがプライマリからどれだけ遅れているかが分かります。
アプリからの接続文字列
アプリは、1台を名指しするのではなく、メンバーを並べてレプリカセット名を付けた接続文字列を使います。
mongodb://appuser:your-password@mongo1.example.internal:27017,mongo2.example.internal:27017,mongo3.example.internal:27017/appdb?replicaSet=rs0&authSource=appdb
ドライバーはこの一覧から現在のプライマリを探し、切り替わりにも自動で追従します。1台しか書いていないと、そのホストが落ちたときに初回接続ができません。
- パスワードに
@:/などの記号を含む場合は、URL エンコード(パーセントエンコード)して書きます。 - パスワードはソースコードに直接書かず、環境変数などから読み込みます。
接続できるかは、アプリサーバーから mongosh で確かめられます。
mongosh "mongodb://mongo1.example.internal:27017,mongo2.example.internal:27017,mongo3.example.internal:27017/appdb?replicaSet=rs0&authSource=appdb" -u appuser
読み込みをセカンダリに回す readPreference=secondaryPreferred も指定できますが、セカンダリは少し遅れているため、書いた直後に読むと古い値が返ることがあります。高可用性が目的なら、まずは既定のプライマリ読み込みのままにしておくのが無難です。
フェイルオーバーを試験する
構築したら、本番投入前に切り替えを試します。管理ユーザーでプライマリに接続し、次を実行するとプライマリが降格して選挙が起きます。
rs.stepDown()
数秒から十数秒で別のメンバーがプライマリになります。その間、アプリの書き込みは一時的に失敗するので、ドライバーの再試行設定(新しいドライバーの多くは再試行可能な書き込みが既定で有効)とアプリのエラー処理で吸収できるかを確認します。
あわせて、1台の mongod を sudo systemctl stop mongod で止める試験も行い、残り2台でプライマリが選ばれること、止めたメンバーを起動し直すと SECONDARY に戻って追いつくことまで確認しておくと安心です。
運用で気を付けること
- レプリカセットはバックアップではない: 誤って削除したデータは、すぐに全メンバーに複製されます。定期的なバックアップは別に取ります。
- oplog の保持期間: セカンダリが長時間止まり、その間の変更が oplog から押し出されると、差分では追いつけず再同期が必要になります。
rs.printReplicationInfo()で oplog がカバーしている時間を確認し、メンテナンスでメンバーを止める時間の目安にします。 - ディスクと監視: 3台とも同じデータを持つので、ディスク容量は全台で同じだけ必要です。
rs.status()のメンバー状態とレプリケーション遅延を監視対象に入れます。 - バージョンアップ: メンバーを1台ずつ更新するローリングアップグレードが基本です。手順や互換性の設定(featureCompatibilityVersion)はバージョンごとに異なるので、公式のアップグレード手順を確認してください(2026年10月時点)。
まとめ
- レプリカセットは投票メンバーの過半数で新しいプライマリを選ぶ。データを持つ3台構成を基本にし、別ホストに分散させる。
- メンバー登録にはホスト名を使い、
localhostを使わない。27017 番はメンバーとアプリサーバーにだけ開ける。 - キーファイル(本番では x.509 と TLS を検討)で内部認証し、localhost 例外で最初の管理ユーザーを作る。アプリには
readWriteだけの専用ユーザーを用意する。 - アプリは全メンバーと
replicaSetを書いた接続文字列を使い、rs.stepDown()とmongodの停止で切り替えを試験しておく。 - レプリカセットとは別に、バックアップは定期的に取る。