エラーの解析
30秒間のフリーズの後に大量のエラーメッセージが表示されることほど、ストレスが溜まることはありません。このエラーは、Node.jsドライバーがクラスター内の利用可能なサーバーを検出しようとしたものの、デフォルトのタイムアウト時間内に到達できなかった場合に発生します。
MongoServerSelectionError: Server selection timed out after 30000 ms while waiting for a connection to the server
at Timeout._onTimeout (/node_modules/mongodb/lib/sdam/topology.js:312:34)
at listOnTimeout (internal/timers.js:554:17)
at processTimers (internal/timers.js:497:7) {
reason: TopologyDescription {
type: 'ReplicaSetNoPrimary',
servers: Map(3) { ... },
setName: 'atlas-xyz-shard-0'
}
}
根本原因
これは「データベースに到達できない」という抽象的なシグナルだと考えてください。ドライバーはハンドシェイクプロセスを開始しましたが、割り当てられた30,000ミリ秒以内に正常なプライマリノードを見つけることができませんでした。通常、問題はコードではなく、アプリケーションとデータの間に存在する障壁にあります。主な原因は以下の通りです。
- IPアクセス: 現在のネットワークIPが MongoDB Atlas の許可リスト(allowlist)に含まれていない。
- ファイアウォールの制限: ローカルルーター、企業のファイアウォール、またはクラウドのセキュリティグループによってポート27017がブロックされている。
- DNSの失敗: システムが接続文字列内のSRVレコードを解決できない。
- バインディングの問題: ローカル環境の設定で、MongoDB が内部トラフィックのみをリッスンしている。
ステップ1:IPアクセスリストを確認する
MongoDB Atlas を使用している場合は、まずここを確認してください。原因の90%はこれです。オフィスからカフェに移動しませんでしたか?パブリックIPが変更されたため、Atlas が接続を拒否しています。
- MongoDB Atlas のダッシュボードにログインします。
- Securityタブの下にある Network Access に移動します。
- 現在のIPがリストにあるか確認します。自宅で作業している場合は、「Add IP Address」をクリックし、次に「Add Current IP Address」をクリックします。
- AWS EC2 や DigitalOcean などのクラウドデプロイメントの場合は、サーバーのパブリックIPまたは VPC CIDR 全体(例:
10.0.0.0/16)がホワイトリストに含まれていることを確認してください。
ステップ2:Netcatでネットワーク経路をテストする
ネットワークパスがブロックされているなら、JavaScriptのデバッグに時間を費やすのは無駄です。ターミナルから直接接続をテストしましょう。nc (netcat) を使用して、ポート27017が実際に到達可能かどうかを確認します。
# 構文: nc -zv [ホスト名] [ポート]
nc -zv cluster0-shard-00-00.mongodb.net 27017
接続に成功すると、Connection to [host] port 27017 [tcp/itls] succeeded! と返されます。コマンドがハングするか「Connection refused」が返される場合、ネットワークがトラフィックをブロックしています。これは、標準外のポートを制限している企業のVPNや公共のWi-Fiでよく発生します。
ステップ3:DNSとSRVレコードの問題を解決する
最新の MongoDB 接続文字列は mongodb+srv:// プロトコルを使用します。これは、クラスターノードを特定するためにDNSのSRVレコードに依存しています。ローカルのDNSプロバイダーが遅い、またはこれらのレコードを解決できない場合、ドライバーはタイムアウトします。
マシンのDNSを Google (8.8.8.8) または Cloudflare (1.1.1.1) に切り替えてみてください。また、次のコマンドを使用してSRVレコードを手動で確認することもできます。
nslookup -type=SRV _mongodb._tcp.your-cluster-url.mongodb.net
ステップ4:Dockerとローカルのバインディングを解決する
コンテナ内で MongoDB を実行していますか?デフォルトでは、MongoDB は 127.0.0.1 のみをリッスンしている場合があります。これにより、外部アプリや他の Docker コンテナからの接続が妨げられます。mongod.conf を更新して、より広いアクセスを許可します。
# mongod.conf
net:
port: 27017
bindIp: 0.0.0.0 # すべての利用可能なネットワークインターフェースでリッスンする
docker-compose.yml では、アプリとデータベースが同じネットワークを共有していることを確認してください。localhost ではなく、サービス名(例:mongodb://db_container:27017)を使用します。
ステップ5:タイムアウトを調整する(最終手段)
単にネットワークが遅いだけの場合もあります。遅延の大きい接続を使用している場合は、ドライバーがサーバーを見つけるまでの時間を長く設定できます。接続文字列に serverSelectionTimeoutMS を追加してください。ただし、これは応急処置に過ぎず、ファイアウォールによる物理的なブロックを解決するものではないことに注意してください。
// タイムアウトを60秒に延長
const uri = "mongodb+srv://user:pass@cluster.mongodb.net/db?serverSelectionTimeoutMS=60000";
mongoose.connect(uri);
予防のためのプロのヒント
クラウド環境を構築する際、CIDR範囲の計算ミスは非常によくある間違いです。私は VPC ピアリングを設定するとき、サブネット計算機を開いたままにしています。これにより、アプリサーバーを実際には含まない範囲ではなく、正しく 10.0.1.0/24 をホワイトリストに登録できているか確認できます。
最後に、必ず mongosh でテストしてください。公式の MongoDB シェルがサーバーから接続できない場合、Node.js アプリも接続できません。これを使用して、問題が環境にあるのかコードにあるのかを切り分けましょう。

