Docker+MySQL连接问题——只能通过ip连接而localhost无效

问题描述

今天在自己的ECS(Ubuntu18.04)上使用docker-compos构建并生成两个容器,一个是jeecg-boot后端,一个是MySQL,构建成功启动后使用docker logs -f 命令查>看后端容器的日志,发现无法创建SQL连接,后来通过查看日志,查阅资料最终将问题定位在主机文件的缺失,并搞明白了mysql -h localhostmysql -h ipaddress两种方式创建连接的区别。

问题解决过程

首先是查看后端容器的日志:

1
2
3
4
5
2022-12-02 17:46:03.342 [Druid-ConnectionPool-Create-1412564235] ERROR com.alibaba.druid.pool.DruidDataSource:2781 - create connection SQLException, url: jdbc:mysql://127.0.0.1:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serve   rTimezone=Asia/Shanghai, errorCode 0, state 08S01
com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
……

从这个报错来看,后端的容器是能够成功向 MySQL Server发送数据包,但自身的driver(驱动,连接器)并没有收到确认回传。

MySQL容器这边的日志是:

1
2
3
4
5
6
7
……
2022-12-02T10:02:27.565007Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.19' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL.
2022-12-02T10:02:27.812639Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Socket: '/var/run/mysqld/mysqlx.sock' bind-address: '::' p ort: 33060
mbind: Operation not permitted
mbind: Operation not permitted
mbind: Operation not permitted
……

可以看到容器启动后内部的/usr/sbin/mysqld准备好接收连接,并通过/var/run/mysqld/mysqld.sock套接字监听3306端口。

特意检查了一下后端代码配置,我写的是

1
2
3
4
5
6
7
datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: xxx
password: xxx
driver-class-name: com.mysql.cj.jdbc.Driver
……

由于后端和MySQL在同一台机器上,所以这里写localhost应该没问题(事实证明就是这的问题)

看起来并没有什么问题,用Navicat也能连接到数据库并取到数据:

我再使用命令行连接一下数据库?神奇的一幕出现了(其实是本人才疏学浅少见多怪,见笑了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mysql -h localhost -u root -p
Enter password:
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)

mysql -h localhost -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 15
Server version: 8.0.19 MySQL Community Server - GPL

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> exit

我靠?localhost127.0.0.1还不一样,这么离谱的吗???

吃惊之余我赶紧将后端代码中的配置改成服务器公网地址,重新打包,果然发现后端正常运行了,真神奇。

事实上这种方式比较麻烦,一点小的改动就要mvn clean,mvn insrtall,浪费我们不少时间,其实可以把这些容易变动的全局性的配置参数绑定到一个文件中,后续会整理更新docker部署相关技巧

进一步原因分析与改进

如果你的机器上恰好也安装了docker并且没有安装mysql-server,你可以亲自复现一下这个问题,然后你就会发现这两种方式虽然都默认server在本机,但有着本质的区别

  • localhost的报错如下所示,可以看到是通过本地sock文件创建连接

    1
    ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
  • 127.0.0.1的报错如下所示,可以看到只说了无法连接到目标主机

    1
    ERROR 2003 (HY000): Can't connect to MySQL server on '127.0.0.1:3306' (111)

因此我可以大胆猜测,连接方式为localhost时,通过本地机器上的sock文件创建连接(但实际没有),而127.0.0.1虽然也是本机但还是把它当做远程主机,创建连接的驱动文件从server获取,那自然能成功

然后查找本机/var/run/mysqld发现直接没有这个文件夹,我的猜想得到了验证。之前曾安装过MySQL后来又卸载了,因此相关文件也就移除了。

那为什么我一开始在后端程序中写的是127.0.0.1也不行?

这个问题暂时没有找到答案,我猜测可能是docker容器或jeecg-boot配置了一些默认行为,当连接请求为127.0.0.1时自动转为localhost前者要经过网卡传输,受到防火墙和网卡相关限制,而后者并不会解析成IP地址,也不通过网卡传输数据。这样做能够节省带宽资源)

总结

通过这次docker容器的部署,锻炼了问题排查和问题分析能力,具体而言加深了对MySQL服务的理解、计算机网络方面相关知识。


Docker+MySQL连接问题——只能通过ip连接而localhost无效
https://dockingyuan.top/2022/12/02/docker/mysql-connection-error/
作者
Yuan Yuan
发布于
2022年12月2日
许可协议