上周半夜被一条“mongodb3.2.3连接不上”的告警吵醒,日志里清一色的dial tcp 192.168.20.10:27017: connect: connection refused。mongodb 3.2.3这个版本放到今天确实算老古董了,但不少遗留项目还在用它,尤其是那种七年前上线的内部系统,说换就换不现实,只能在旧版本上继续维护。连接不上这个现象看起来只是一句话,真正排查起来会发现服务、网络、权限、驱动四个层面都有可能,而且每层都有自己的坑。这篇文章把我自己处理过的真实案例、踩过的坑和最后沉淀下来的一套排查流程整理出来,希望能帮到还在维护旧版 MongoDB 的同行,也顺便给刚接手这类项目的新人一份可以直接照着做的操作手册。
1. 先别急着改代码,建立连接排查的四个层面
1.1 四层模型:服务层、网络层、权限层、驱动层
很多人在项目里报MongoDB connection error的第一反应是改连接字符串,甚至重装驱动,结果折腾半天还是连不上。我自己的经验是,碰到连接问题先别动手,脑子里要有一个四层排查模型,一层层过。
第一层是服务层,要确认 mongod 进程到底有没有跑起来,端口有没有监听,启动日志有没有报错。第二层是网络层,连接方到服务方之间的 TCP 能不能通,防火墙、安全组有没有挡。第三层是权限层,账号密码对不对,认证数据库是不是填错了,认证机制是否匹配。第四层是驱动层,也就是客户端代码和连接串的写法有没有问题,尤其是驱动版本和服务器版本之间是否兼容。
把这四层想象成自来水管道:服务端是水厂,网络是管道,权限是水表开没开,驱动是你家水龙头。水龙头拧开没水,第一步肯定先看水厂有没有生产,而不是把水龙头拆了重装。这个顺序能帮你快速缩小范围,避免对着一个错误的配置文件反复折腾。
1.2 我的排查工具清单
手边要备几个趁手的工具,不用多,够用就行。服务端状态我用ps -ef | grep mongod看进程,用netstat -tlnp | grep 27017或者ss -lntp | grep 27017看监听端口,这两个命令在任何 Linux 发行版上都能用。确认服务是否健康,直接用 mongo shell 跑一个db.runCommand({ ping: 1 }),能返回ok: 1就说明 mongod 是活的。
网络层我常用telnet <ip> 27017和nc -zv <ip> 27017做快速探测,能通就是端口开放,不通就继续查防火墙。需要看更细的就用tcpdump -i eth0 port 27017抓包,判断 SYN 包有没有被丢弃。
日志位置也要记住,Linux 下一般是/var/log/mongodb/mongod.log,Windows 下通常在安装目录的log子目录里。排查的时候开一个终端tail -f这个日志,另一边执行连接操作,服务端报错信息比客户端报错有时更有用。
2. mongodb3.2.3 连接失败的六大常见原因
2.1 服务根本没起来:安装失败和启动失败的连带问题
最基础但也最容易忽略的原因就是 mongod 服务压根没起来。之前遇到一个同事反馈连接不上,我说先看进程,他查完发现 mongod 根本没运行。这种情况往往和“mongodb安装失败”或启动失败有关。安装时如果没装全,比如缺少依赖、配置文件路径不对、data 目录不存在,mongod 会直接退出,服务自然连不上。
Linux 上最常见的启动失败原因是/data/db目录不存在或者权限不是 mongodb 用户。MongoDB 3.2.3 默认的 dbPath 在很多发行版里是/var/lib/mongodb,如果手贱改了配置指向一个不存在的路径,启动就会报dbpath (/data/db) does not exist。另外磁盘满了也会导致启动失败,日志里能看到No space left on device。Windows 上服务启动失败更多是安装目录权限问题,或者你把 data 目录放到了带中文的路径下。
所以收到“连接不上”的告警,第一命令永远应该是ps -ef | grep mongod或者去 Windows 服务管理器看 MongoDB 服务状态。如果服务没起来,先看日志,把日志里的报错解决掉,再谈连接。
2.2 bindIp 绑定地址只允许本机
第二类高发原因是bindIp配置问题。MongoDB 3.2.3 的默认配置文件/etc/mongod.conf中,net.bindIp默认是127.0.0.1,意思只监听本机回环地址。这样你在服务器本机上用mongo命令连没问题,但换任何一台机器来连都会报连接超时或被拒绝。
我当时排查过一个小项目,开发在服务器上明明能连,测试反馈连不上,最后看配置果然是bindIp: 127.0.0.1。改成0.0.0.0后重启,测试机马上就能连了。不过这里要提醒一句:生产环境千万别无脑改0.0.0.0,更稳妥的是绑定具体的内网 IP,比如bindIp: 192.168.20.10,这样只有能访问到该内网 IP 的机器才可能连到数据库,减少暴露面。
2.3 端口和防火墙的沙盘推演
即使 mongod 监听在0.0.0.0:27017,跨主机访问仍可能被防火墙拦住。默认端口 27017 是 MongoDB 的标配,很多云厂商的安全组默认不放行这个端口,Linux 服务器上还有可能同时存在 iptables、firewalld、ufw 多层管理。
排查方法是分层做沙盘推演:先看进程监听在哪个地址,再看连接方能不能 TCP 到目标端口,然后检查本机防火墙规则。CentOS 7 以下用iptables -L -n,CentOS 7 以上用firewall-cmd --list-all,Ubuntu 用ufw status。如果确认防火墙没放行,执行:
firewall-cmd --permanent --add-port=27017/tcp firewall-cmd --reload云环境还要去控制台检查安全组的入方向规则。这里有个很典型的症状:本机能连,远程telnet端口不通,但你在服务器本机telnet自己的公网 IP 却通。这种就基本锁定是中间链路防火墙或安全组的问题,不用再怀疑 mongod 本身。
2.4 认证授权:3.x版本的坑
MongoDB 3.2.3 正好处在认证机制从 MONGODB-CR 向 SCRAM-SHA-1 过渡的时间点,很多老库的用户是在 2.x 版本创建的,认证机制还是旧的 MONGODB-CR。到了 3.2.3,如果启用了 auth,新版客户端默认走 SCRAM-SHA-1,老用户可能认证失败,报Authentication failed。
解决思路有两个:一是把老用户删了重建,用新机制重新生成;二是升级用户认证机制,通过db.authSchemaUpgrade({})指令升级。更让我头疼的是连接串里忘了写authSource。MongoDB 的账号是跟着库走的,比如在 admin 库建的账号,连接串里必须带authSource=admin,否则驱动默认用当前库去认证,分分钟给你报认证失败。
另外,密码里如果包含@:/这类特殊字符,在 URI 连接串里一定要做百分号编码,否则解析出来的连接信息就是错的,表现为“用户名或密码不正确”,但实际上密码没问题。这一点在 Java、Python、C# 的连接字符串里都适用。
2.5 驱动版本不匹配:以C#为例的崩溃实录
驱动兼容性是最隐蔽的一个原因,尤其是用 C# 连接老版本 MongoDB 时。热词里有个“C# mongodb 集合 最大值”,恰好我处理过一个类似问题。当时项目用MongoDB.Driver 2.4连 3.2.3,基础查询没问题,但一旦执行类似“取集合中某个字段最大值”的操作就会抛异常。
原因是新版本驱动在某些聚合操作上会生成 3.6+ 才支持的管道语法,或者用到了旧服务器上不存在的操作符。我当时查了很多资料,最后确认问题出在驱动和服务器版本特性不匹配。解决办法不是改一堆业务代码,而是把驱动降到与 3.2.x 对应的MongoDB.Driver 2.3版本,或者改用兼容写法:先用Find按字段SortByDescending,再取FirstOrDefault,绕开聚合管道新语法。
C# 连接 3.2.3 的字符串写法也比较讲究:
var client = new MongoClient("mongodb://admin:yourpassword@192.168.20.10:27017/admin?authSource=admin"); var database = client.GetDatabase("mydb");如果你用MongoClientSettings配置,还要注意ServerSelectionTimeout不能设太短。默认 30 秒,有人为了快速失败改成 1 秒,结果服务端稍微慢一点就直接报超时,看起来像连接不上,其实是被自己坑了。
2.6 分片集群和副本集的连接字符串坑
连接副本集和分片集群时,很多人会犯一个低级错误:连接的是单个 mongod 实例,而连接字符串里没有写副本集名称。MongoDB 3.2.3 的副本集如果没有replicaSet=myrs这个参数,驱动连接第一个节点后可能找不到主节点,报no primary found in replica set。
分片集群更要注意,客户端应该连接的是mongos路由节点,而不是某个 shard 节点。或者你用到了“mongodb查看表分片”这个功能,执行db.collection.getShardDistribution()时需要先通过 mongos 连上集群,直接连 shard 是查不到分布的。连接字符串要写成:
mongodb://user:pass@mongos1:27017,mongos2:27017,mongos3:27017/admin?authSource=admin如果只写一个 mongos 地址,一旦这个 mongos 挂了,应用就连不上了,这也是很多人误以为“数据库挂了”的原因。3.2.3 的 mongos 同样受 bindIp 和认证影响,排查时不要只看 shard 的端口。
3. 实操:一次从“连接超时”到“顺利连上”的完整排除过程
3.1 第一步:确认服务端进程和监听状态
我处理的那次故障,应用服务器和数据库服务器是两台机器。我先登录数据库服务器,执行:
ps -ef | grep mongod结果进程不存在,等于说 mongod 已经挂了。接着看启动日志:
tail -n 100 /var/log/mongodb/mongod.log日志里提示Failed to create /data/db dir,一看/data/db目录因为上次清理磁盘被删了。于是创建目录并授权:
mkdir -p /data/db chown -R mongod:mongod /data/db然后启动服务。如果你用的是 systemd:
systemctl start mongod systemctl enable mongod启动后再看端口监听:
netstat -tlnp | grep 27017看到tcp 0 0 0.0.0.0:27017 0.0.0.0:* LISTEN才能说明 mongod 正常监听。如果这里显示127.0.0.1:27017,那后面远程连不上也就解释得通了。
3.2 第二步:本地客户端测试服务健康
服务起来了,先在数据库服务器本机做一次健康检查:
mongo --eval "db.runCommand({ ping: 1 })"能正常返回{ "ok" : 1 },说明服务本身没毛病。然后我尝试带认证连接:
mongo --host 127.0.0.1 --port 27017 --username admin --password 'yourpassword' --authenticationDatabase admin这里能连上,说明服务端认证也没问题。本机可以,远程就不行,问题基本锁定在网络层或者 bindIp 配置。这一步做起来很快,却能省下后面一大轮折腾。
3.3 第三步:抓包级网络检测
我在应用服务器上执行:
telnet 192.168.20.10 27017返回Unable to connect to remote host: Connection refused。这个报错有两种可能,一是目标端口真的没监听,二是被防火墙重置。但上一步已经确认本机端口监听了,那就重点查防火墙。
再执行nc -zv 192.168.20.10 27017,同样不通。然后回到数据库服务器检查:
iptables -L -n --line-numbers | grep 27017结果发现有一条 DROP 规则把 27017 的入站流量全丢了。如果你觉得命令结果不够直观,可以用 tcpdump 抓包:
tcpdump -i eth0 host 192.168.20.10 and port 27017 -nn在应用服务器发起连接的同时观察抓包结果,如果只有 SYN 包而没有 SYN-ACK,或者一直重传,基本就是防火墙在丢包。删掉那条规则或者放行端口,连接就通了。
3.4 第四步:修复配置并逐步验证
网络通之后,我再测试远程连接,这时候报的不再是 Connection refused,而是connect failed to host或直接卡住。回到数据库服务器看配置文件/etc/mongod.conf:
net: port: 27017 bindIp: 127.0.0.1果然只绑了回环地址。我改成具体的内网 IP:
net: port: 27017 bindIp: 192.168.20.10保存后重启:
systemctl restart mongod然后在应用服务器上再次执行:
mongo "mongodb://admin:password@192.168.20.10:27017/admin?authSource=admin"这次就进去了。这里我想劝一句,不要看到网上教程改0.0.0.0就照抄,除非你的网络环境完全隔离。绑定内网 IP 能避免把数据库暴露到所有接口上,尤其是有多网卡的服务器,绑错地址等于给安全埋雷。
3.5 第五步:密码和鉴权机制升级
后来我发现这台库虽然开了认证,但 old user 用的还是旧机制,为了稳妥,我重建了管理员账号。先停掉对外的应用访问,进入 admin 库:
use admin db.createUser({ user: "admin", pwd: "StrongPass@2024", roles: [ { role: "root", db: "admin" } ] })然后确认配置里启用了鉴权:
security: authorization: enabled重启后连接串必须注明认证库:
mongodb://admin:StrongPass%402024@192.168.20.10:27017/admin?authSource=admin注意密码里的@被编码成了%40。这一步做完,客户端、服务端、驱动、认证四个层面就全部对齐了。除了连接本身,我还建议顺手把用户密码规则改一改,不要再用admin123这种默认弱口令。
4. 常见问题速查表与独家避坑技巧
4.1 经典症状对照表
我在各种项目里把常见的连接报错整理成了一个速查表,遇到问题直接对照着看,能省很多时间。表格如下:
| 报错信息 | 可能原因 | 处理建议 |
|---|---|---|
connect ECONNREFUSED 192.168.x.x:27017 | mongod 未启动或端口未监听 | 检查进程和日志,启动服务 |
connect EHOSTUNREACH | 网络路由不通、跨网段限制 | ping 目标 IP,检查路由和访问控制 |
Authentication failed | 用户名密码错误、认证库错误、认证机制不匹配 | 确认 authSource,重建用户或升级机制 |
no primary found in replica set | 副本集初始化未完成或连接串缺replicaSet | 检查副本集状态,补全连接参数 |
could not connect to a single machine in list | 多个节点地址全部不可达 | 逐个检查节点监听和防火墙 |
Connection refused(本机连本机) | 本机服务没起来或监听地址不是127.0.0.1 | 看服务状态和 bindIp |
这张表是我从十几个故障单里提炼出来的。遇到报错先不要谷歌搜一行字,先对号入座,往往比盲目搜更准。
4.2 开发环境里最容易忽视的三个细节
开发环境出问题通常都不是什么高深原因,反而是一些“低级”细节。一个典型的坑是 Windows 上安装 MongoDB 3.2.3 时,服务已经装上了,但 data 目录权限不对,导致服务启动失败。打开服务管理器,发现 MongoDB 服务“正在启动”然后马上停止,系统日志里能看到权限拒绝。解决办法是把 data 目录授权给运行服务的用户,或者直接用管理员身份重新安装。
第二个细节是 Linux 下/tmp目录被清理或权限变更。MongoDB 在某些操作中会使用/tmp/mongodb-<port>.sock这类临时文件,如果/tmp权限被改成 000,连接时也会出问题。我还遇到过磁盘 /var 分区写满,导致无法生成 socket 文件,同样会连接失败。
第三个细节是安装包和系统版本不匹配。在比较新的 Ubuntu 22.04 上装 MongoDB 3.2.3 大概率失败,因为新发行版的 glibc 和 OpenSSL 版本已经和这个老版本不兼容。如果你必须用 3.2.3,要么用 CentOS 7、Ubuntu 16.04 这类同时代系统,要么用容器把旧环境隔离起来。很多“mongodb安装失败”的问题,根源不是操作步骤,而是系统太新了。
4.3 图形化工具连接时的参数坑
图形化工具同样有自己的坑。我用 NoSQLBooster for MongoDB 连接 3.2.3 时,第一次直接填了 IP、端口、用户名、密码,结果一直报认证失败。排查后发现工具默认的 Authentication Mechanism 是 SCRAM-SHA-256,而 MongoDB 3.2.3 根本不支持这个机制,只支持 SCRAM-SHA-1。
解决办法是在连接设置里把认证机制手动改成SCRAM-SHA-1,并且把 Authentication DB 设为admin。如果你用的是新版 Studio 3T、Robo 3T,也都有类似的选项。这里完全不涉及什么“特殊处理”,纯粹是 GUI 工具默认值跟老版本服务器不一致而已。有的工具还默认勾选 TLS/SSL,但服务器端没开,连接会卡住直到超时,记得把 TLS 关掉。
4.4 数据安全视角下的连接收尾
连接问题解决之后,我建议顺手做一次安全加固,毕竟 mongodb3.2.3 本身就是老版本,已知漏洞不少,不能再裸奔。第一,mongod.conf里必须开启security.authorization: enabled,不要用空密码。第二,bindIp不要绑定 0.0.0.0,绑定有业务需要的 IP 段。第三,防火墙只放行指定的应用服务器 IP,比如:
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.20.5" port protocol="tcp" port="27017" accept'第四,如果有条件,开启 TLS/SSL,或者通过内网专线访问,不要把 27017 暴露到公网。第五,定期导出备份并验证恢复流程,“mongodb数据库安全”不是一句口号,而是实实在在的权限最小化、网络隔离和备份兜底。老版本数据库如果实在升级不动,至少要保证连接层面安全可控,不要给别人留一个敞开的大门。
最后分享一个排查小技巧
连接不上这个问题,我在不同项目里前前后后处理了五六次,最大的体悟就是:绝大多数情况下,问题出在服务没启动、bindIp 没改、防火墙没放行这三件事上,别一上来就怀疑代码和驱动。每次排查前,先开一个终端执行tail -f /var/log/mongodb/mongod.log,一边看日志一边复现问题,很多报错在服务端日志里写得比客户端清楚得多。
还有一个我后来一直用的习惯:连接字符串里永远显式写上authSource=admin,哪怕用户名是在业务库里建的,也先确认清楚。别小看这一个参数,它至少能帮你排除掉一半的认证坑。希望这篇文章能让你少走点弯路,下次再看到“mongodb3.2.3连接不上”,能先对着日志笑一笑,然后按流程把问题干净利落地解决掉。