エラーを理解する
MongoDBクラスター内でリーダーが見つからないためにアプリケーションがクラッシュするのを見るほど、苛立たしいことはありません。平たく言えば、このエラーはドライバーがリストされたノードに接続したものの、通信相手となる Primary ノードを見つけられなかったことを意味します。MongoDBは書き込み操作をPrimaryでのみ許可しているため、ドライバーはデータの不整合を避けるために、処理を継続せずにエラーを返します。
これは、議長を誰にするか合意が得られない取締役会のようなものだと考えてください。リーダーがいなければ、何の決定も下せません。通常、これは以下の4つの理由のいずれかによって発生します。
- 名前の不一致: コード内で指定したレプリカセット名が、サーバー上の名前と一致していない。
- 過半数の喪失: 有効な選出を行うために必要なノードが不足している。
- 通信の断絶: ノードは稼働しているが、ネットワーク経由で互いに通信できていない。
- 名前解決の混乱: アプリケーションからノードは見えているが、ノードが自身を識別するために使用しているホスト名を解決できない。
ステップバイステップの解決策
1. レプリカセット名を一致させる
ドライバーに production-rs という名前のセットを探すように指示しても、内部設定の名前が rs0 の場合、ドライバーは見つけたノードを無視します。これは、間違ったクラスターに接続したと判断するためです。
いずれかのノードで mongosh を開き、以下を実行してください。
rs.conf()._id
出力が "rs0" の場合、接続文字列が mongodb://host1:27017/?replicaSet=rs0 のようになっていることを確認してください。ここでのわずかなタイポ(打ち間違い)でも、接続は失敗します。
2. クォーラム(定足数)と投票権の確認
MongoDBは、リーダーを選出するために厳格な過半数(50% + 1)を必要とします。標準的な3ノードのクラスターでは、少なくとも2つのノードがオンラインである必要があります。同時メンテナンス中などに2つのノードがダウンすると、残されたノードは過半数を維持できなくなるため、自身を SECONDARY に降格させます。
クラスターの状態を以下のコマンドで確認します。
rs.status()
"members" リストの中から、以下の異常の兆候を探してください。
health: 0: ノードに到達できません。stateStr: 全員がSECONDARYの場合、リーダーが存在しない状態です。lastHeartbeatMessage: MongoDBが他のノードを認識できない理由(例: "Connection refused")がここに表示されます。
3. ネットワークの断絶を解消する
DockerやKubernetes環境では、到達可能なホスト名の代わりに内部コンテナIDや localhost が使用されているために、ノード間の通信に失敗することがよくあります。ノードAがノードBにpingを送信できなければ、投票を行うことはできません。
ノード間の経路を手動でテストします。
# 1番目のmongoノードから、2番目のノードへの接続を試行
nc -zv mongo-node-2 27017
接続がタイムアウトする場合、ファイアウォールやセキュリティグループによってポート 27017 がブロックされている可能性があります。
4. 緊急再設定(注意して使用)
ほとんどのノードが永久に失われたような災害シナリオでは、残されたノードを強制的にリーダーにする必要があるかもしれません。これは、古いノードが突然復帰した場合にデータのロールバックが発生する可能性があるため、「緊急時以外は使用禁止」の手段です。
生き残ったノードで以下を実行します。
let cfg = rs.conf();
cfg.members = [cfg.members[0]]; // 設定から他のすべてのメンバーを削除
rs.reconfig(cfg, {force: true});
このコマンドは、ノードに対して過半数を探すのをやめ、直ちに Primary として動作を開始するように指示します。
5. DNSホスト名の問題を解決する
これはよくある落とし穴です。アプリが最初に接続するときは、指定されたIPを使用します。しかし、その後クラスターは内部設定にあるホスト名のリストをアプリに渡します。アプリサーバーがそれらの特定のホスト名(mongo-db-0.internal.local など)を解決できない場合、Primary を見つけることができません。
クラスターが通知している名前を確認します。
rs.conf().members.map(m => m.host)
アプリケーションサーバーから、それらの正確な文字列に対してpingを試行してください。失敗する場合は、DNSまたは /etc/hosts ファイルを更新する必要があります。
修正の確認方法
設定を調整したら、クラスターの状態を確認します。rs.status() を実行し、1つのノードが "stateStr": "PRIMARY" となっていることを確認してください。次に、シェルを使用してアプリサーバーから直接接続を試みます。
mongosh "mongodb://user:pass@host1:27017,host2:27017/?replicaSet=rs0"
rs0 [direct: primary]> のようなプロンプトが表示されれば、正常に復旧しています。
予防的なメンテナンスのヒント
- 奇数個を維持する: 常に3、5、または7ノードでデプロイしてください。偶数個のノードでは投票が同点になる可能性が高くなり、Primary が不在になる原因となります。
- Arbiter(アービタ)を賢く使う: 3つ目のデータノードを構築するコストを抑えたい場合は、軽量な Arbiter インスタンスを追加してください。データは保持しませんが、選出に必要な決選投票権を提供します。
- 優先度を確認する: すべてのノードで誤って
priority: 0が設定されていないか確認してください。優先度が0のノードは、Primary になることが禁止されています。

