深夜迁云:一个IP引发的“血案”
作为一个有追求的程序猿,深夜加班迁移数据是常有的事。今晚用Navicat内置的SSH隧道连接云端MySQL,遇到一个让人抓狂的坑,特此记录,以防日后重蹈覆辙,供各位同行参考。
故事背景:被防火墙支配的恐惧 生产环境的防火墙策略向来严苛——直接开数据库端口?那是痴心妄想。于是,SSH隧道成了我们连接云端MySQL的唯一“地下通道”。配置思路很简单:Navicat ➜ SSH标签 ➜ 填写云服务器登录信息 ➜ 常规标签 ➜ 填写数据库账号密码。一套行云流水,自信满满地点击“测试连接”,然后……熟悉的转圈圈出现了。
不是报错,不是超时,就是那个永无止境的小圆圈,一圈一圈地转着,仿佛在嘲讽我的天真。这一刻,我相信很多同行都懂——连接转圈圈,是今晚加班的开始。
排查过程:从怀疑人生到怀疑一切 第一回合:检查SSH配置 用命令行 ssh user@host -p port 一试,秒通。这说明SSH账号密码、端口、密钥都没问题。好,Navicat,你是不是对SSH有什么意见?
第二回合:换工具对比 祭出HeidiSQL、DataGrip,用同一套SSH和MySQL信息,一把就通。嗯?难道是Navicat版本有bug?可重装、清缓存、换版本,一圈操作下来,转圈依旧。
第三回合:换网络、换电脑 换个WiFi,搬出家里的老笔记本,同样的配置,HeidiSQL依然欢快地连上,Navicat依然在转圈。此时我的内心:难道Navicat和我八字不合?
第四回合:怀疑MySQL权限 登录服务器,查看MySQL用户权限,mysql.user表里该用户host为'%',按理说不限制IP。而且通过其他工具能连,说明MySQL本身没问题。
第五回合:灵光一闪(其实是被逼无奈) 折腾了两个小时,喝了三杯咖啡,几乎要放弃时,我盯着Navicat的配置界面发呆:常规标签里,“主机”填的是云服务器的公网IP,而SSH隧道里填的也是同一台服务器的IP。这看起来没毛病啊?但鬼使神差地,我把主机地址改成了localhost,再点测试连接——
叮!连接成功!
但随之而来的是更大的疑问:为什么?
当SSH隧道遇到IP选择 SSH隧道的工作原理 Navicat通过SSH隧道连接MySQL,本质是在本地和远程服务器之间建立一条加密通道,然后数据库连接(MySQL协议)通过这条通道传输。具体来说,Navicat会在本地随机开启一个端口,通过SSH转发到远程服务器的目标端口(通常是3306)。这时,MySQL服务器接收到的连接请求来源IP是127.0.0.1(因为隧道在服务器本地建立连接),而不是你的本地公网IP。
为什么不能用公网IP? 当我们把“主机”填成公网IP时,Navicat会尝试通过隧道连接到那个公网IP的3306端口。但这里有两个可能的问题:
MySQL绑定地址限制 查看MySQL配置bind-address,如果设置为127.0.0.1,那么MySQL只监听本地连接。从服务器内部访问公网IP,实际上会绕到外部网络再回来,很可能被防火墙拦截,或者MySQL根本就不会响应非本地IP的请求(即使来源是服务器自身)。 而填localhost或127.0.0.1,直接连接本地socket或TCP 127.0.0.1,畅通无阻。
DNS解析与路由 如果MySQL开启了skip-name-resolve,它会跳过DNS反向解析,但这里都是IP,问题不大。然而,当目标主机是公网IP时,从服务器内部发起连接,系统路由表可能会把它送到网关而不是回环接口,导致连接失败。具体取决于网络配置,但绝大多数云服务器上,访问自身公网IP是会走外网网卡并经过iptables规则,如果防火墙策略禁止回环访问公网IP,就悲剧了。
从用户权限角度看 MySQL用户权限表中,host字段决定了允许从哪些IP连接。例如,'username'@'localhost'只允许本地连接。如果我们通过SSH隧道但填了公网IP,MySQL看到的客户端IP其实还是127.0.0.1吗?不一定——这取决于SSH隧道转发的实现方式。如果Navicat在远程服务器上建立的转发是-L 3306:公网IP:3306,那么MySQL看到的是公网IP(即服务器自己的外网IP),这时如果用户只允许localhost,就会拒绝。而其他工具可能实现更聪明,直接转发到127.0.0.1。所以,最稳妥的做法就是填127.0.0.1。
一个形象的比喻 SSH隧道就像你开车到朋友家(云服务器),然后让朋友帮你从屋里拿东西。如果你让朋友去“邻居家”(公网IP)拿,可能朋友不认识邻居,或者邻居不开门;但如果你让朋友直接从“自己家”(localhost)拿,那就顺理成章。
经验总结 隧道模式下,数据库主机永远填localhost或127.0.0.1 这是SSH隧道连接的黄金法则。因为隧道已经把你带到了服务器内部,再访问就应该用本地地址。
理解你的工具 Navicat、HeidiSQL、DataGrip在实现SSH隧道时可能有细微差别,但原理一致。遇到问题,先从原理出发,别盲目换工具。
MySQL权限检查不可少 确保你的MySQL用户允许从localhost连接,即使你通过隧道。很多时候,我们只记得开%,却忘了localhost是独立条目。
不要迷信公网IP 在服务器内部,公网IP不一定比localhost更好用,有时反而惹麻烦。
结语 一个IP地址的差别,让我度过了一个难忘的“转圈之夜”。写这篇博客,既是对自己踩坑的记录,也希望帮助遇到同样问题的朋友。技术之路漫漫,愿我们少一点转圈,多一点成功。
现在,我终于可以安心地把数据迁移上云了。夜深了,希望大家都做个好梦!
