在Ubuntu的Shiny App中通过SSH隧道连MySQL遇权限/连接错误求助
问题:Ubuntu虚拟机Shiny中SSH隧道连接MySQL失败(Windows正常)
环境与现象
Windows 10 RStudio中可正常运行的代码
library(RMySQL) library(ssh) library(callr) rs <- r_session$new(wait = TRUE, wait_timeout = 3500) rs$call(function(){ session <- ssh::ssh_connect('<ssh-user>@<remote-ip-address>', keyfile = "<path-to-.pem-file>", verbose = FALSE) ssh::ssh_tunnel(session, port=3306, target = "127.0.0.1:3306") }) # Connect to TRAQ DB. Sys.sleep(1) db-connection <- dbConnect(MySQL(), user = '<remote-user-name>', password = '<remote-password>', dbname = '<remote-database>', port=3306, host='127.0.0.1')
注:原代码中db-connection是无效变量名,R中不能用减号,实际运行需改为db_connection。
Ubuntu 22虚拟机Shiny中的错误
- 使用
localhost作为host时:ERROR 1044 (42000): Access denied for user '<remote-user-name>'@'localhost' - 使用
127.0.0.1作为host时:ERROR 2003 (HY000): Can't connect to MySQL server on '127.0.0.1' (111)
还出现过错误码115的情况。
Ubuntu终端直接运行的修正方法
必须修改两处:
- 修改SSH隧道端口:
ssh::ssh_tunnel(session, port=3307, target = "127.0.0.1:3306")
- 对应修改DB连接端口:
db_connection <- dbConnect(MySQL(), user = '<remote-user-name>', password = '<remote-password>', dbname = '<remote-database>', port=3307, host='127.0.0.1')
若不改端口或使用localhost,会触发权限拒绝提示。
原因分析
- 端口占用差异:Windows上3306端口大概率未被本地MySQL占用,所以SSH隧道能正常绑定;但Ubuntu虚拟机默认可能安装并运行了本地MySQL服务,占用了3306端口,导致隧道无法创建,最终连接数据库时出现111错误(连接被拒绝)。
- MySQL连接方式差异:Ubuntu中
localhost默认采用socket文件连接,而127.0.0.1强制使用TCP/IP连接。你的远程MySQL用户是授权给TCP连接的,用localhost时MySQL会匹配user@localhost的权限(该权限不存在),所以触发1044错误;而Windows上localhost默认走TCP/IP,因此能正常连接。
解决步骤
针对Shiny应用,按以下调整代码:
- 更换未占用端口:选一个本地未被使用的端口(如3307、3308),同步修改SSH隧道和DB连接的端口号。
- 强制TCP/IP连接:DB连接时始终用
host='127.0.0.1',避开Ubuntu下socket连接的权限问题。 - 延长等待时间:把
Sys.sleep(1)改为Sys.sleep(3)或更久,确保SSH隧道完全建立后再尝试连接数据库。 - 检查端口占用:在Ubuntu终端执行
sudo lsof -i :3306,查看3306端口是否被本地MySQL占用。如果是,可停止本地服务(sudo systemctl stop mysql),或继续使用其他端口。
调整后的完整可用代码:
library(RMySQL) library(ssh) library(callr) rs <- r_session$new(wait = TRUE, wait_timeout = 3500) rs$call(function(){ session <- ssh::ssh_connect('<ssh-user>@<remote-ip-address>', keyfile = "<path-to-.pem-file>", verbose = FALSE) # 使用未被占用的3307端口 ssh::ssh_tunnel(session, port=3307, target = "127.0.0.1:3306") }) # 延长等待时间,确保隧道建立完成 Sys.sleep(3) # 修正变量名+使用对应端口和TCP/IP连接 db_connection <- dbConnect(MySQL(), user = '<remote-user-name>', password = '<remote-password>', dbname = '<remote-database>', port=3307, host='127.0.0.1')
额外注意事项
- Shiny应用若部署在服务器环境,需确保防火墙允许使用所选端口(如3307)。
- 开启SSH隧道的
verbose=TRUE,可查看隧道建立的详细日志,方便排查连接问题。 - 定期检查本地端口占用情况,避免后续端口被其他服务占用导致连接失败。
内容的提问来源于stack exchange,提问作者Garrett
相关产品推荐
相关产品推荐

