【问题标题】:Unable to acquire JDBC Connection on integration test when using Docker bridge network使用 Docker 桥接网络时无法在集成测试中获取 JDBC 连接
【发布时间】:2018-12-12 18:06:22
【问题描述】:

当我在本地运行 maven test 时通过。但是当我在 CI 服务器上运行它时出现此错误。

Error Message
Could not open JPA EntityManager for transaction; nested exception is org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection
Stacktrace
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection
Caused by: org.hibernate.exception.JDBCConnectionException: Unable to acquire JDBC Connection
Caused by: com.mysql.jdbc.exceptions.jdbc4.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.
Caused by: java.net.UnknownHostException: mysql

运行本地测试时,全部通过,使用 IntelliJ IDEA 提供的 maven 测试默认设置。
由于错误抱怨数据库连接,所以我通过 Jenkins Audit 对数据库插件进行了检查。连接成功!

application.properties中的连接参数也对应这个

spring.datasource.url=jdbc:mysql://mysql:3306/database?useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.maxActive=5

URL 中的 MySQL 是 MySQL docker 容器名称。如果将其更改为localhostdocker container inspect mysql 中的私有IP,则错误消息相同,而最后两行的Stacktrace 略有不同。

本地主机

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
Caused by: java.net.ConnectException: Connection refused (Connection refused)

专用IP

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server. 
Caused by: java.net.SocketTimeoutException: connect timed out

我认为不同的是URL中的主机,localhost用于本地测试。 而 Jenkins 服务器使用 Docker 桥接网络。

容器状态为:

docker container ls
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS
                                          NAMES
51ea7c7864a4        mysql:5.7             "docker-entrypoint.s…"   19 hours ago        Up 19 hours         0.0.0.0:3306->3306/tcp                             mysql
de364f7b5eaf        maven:3-jdk-8         "/usr/local/bin/mvn-…"   21 hours ago        Up 21 hours
                                          optimistic_stallman
a6545591e358        jenkinsci/blueocean   "/sbin/tini -- /usr/…"   43 hours ago        Up 43 hours         0.0.0.0:50000->50000/tcp, 0.0.0.0:2048->8080/tcp   frosty_cray

当我在 IntelliJ 中运行 JUnit 测试时,它有时会在本地环境中失败。错误日志是这样的:

Caused by: org.h2.jdbc.JdbcSQLException: Schema "DATABASE" not found; SQL statement:
TRUNCATE TABLE database.data_log 

我已经搜索过这个问题,据说h2数据库默认使用大写。 运行maven test后,如果再次在IDE中运行JUnit测试就会出现这个问题。但这应该与根本原因无关。

搜索错误信息,找到一些类似的问题,但嵌套的异常不同:

Could not open JPA EntityManager for transaction; nested exception is javax.persistence.PersistenceException

SpingREST: Could not open JPA EntityManager for transaction; nested exception is org.hiberna

Could not open JPA EntityManager for transaction; org.hibernate.exception.GenericJDBCException: Could not open connection

Could not open JPA EntityManager for transaction in spring

都是关于nested exception is javax.persistence.PersistenceException
但是nested exception is org.hibernate.exception.JDBCConnectionException: 是我的情况。 阅读Connect Java to a MySQL database
但是由于该插件连接正常,意味着从 Jenkins 容器到 MySQL 容器的连接很好。

总结:
1. 使用 maven 进行本地测试通过
2. Jenkins插件连接MySQL成功
3. 从 Jenkins 运行时集成测试失败
4.本地测试环境为WIN10 64bit; Jenkins 在 Ubuntu 16.04 64 位服务器上的 docker 容器中运行,MySQL 5.7 容器连接到同一个桥接网络。

【问题讨论】:

  • 如果您使用 Spring boot 并在 docker 中运行您的测试数据库,我建议您查看 testcontainers.org ,这里是如何以最佳方式使用它的说明:@987654328 @(你可以跳过 Spock 部分)
  • @MaxFarsikov testcontainer 看起来很酷,但是,在阅读了他们的文档和示例之后,我没有足够的信心在当前项目中使用它。也许我稍后会在一些附带项目上尝试一下。
  • 错误是说no packets were sent,表明你的应用程序和mySQL没有互相交谈......你也提到Jenkins和mySQL正在互相交谈......那就是很好...但是您是否确认mySQL and your application(它部署在远程服务器上的哪个位置)正在相互交谈?检查此以获取更多信息stackoverflow.com/questions/2983248/…
  • @rohitthomas 我同意你的看法。问题可能存在于 MySQL 和应用程序之间。我已经阅读了那篇文章并在我的文章中提到了它。不过,不知道在哪里检查。本地测试有时会失败,但在我运行maven test 后会通过,稍后会通过一段时间。
  • 我需要了解的几件事......该应用程序在您的本地运行并且工作正常??该应用程序托管在另一台服务器上,它只在那里或本地失败?最后它什么时候失败有时你调试过那个场景?? maven 测试也将通过 --> 您是否只为这种情况编写了连接??

标签: java mysql spring docker jenkins


【解决方案1】:

您可以检查一些可能有助于解决此问题的事项。

  • 在 application.properties 中尝试使用 docker 主机 IP 地址。

    来自

    spring.datasource.url = jdbc:mysql://mysql:3306/DATABASE_URI_PATH
    

    spring.datasource.url = jdbc:mysql://192.168.99.100:33060/DATABASE_URI_PATH
    

    注意: 当您执行 docker run 或 docker 文件中的端口时,您将需要映射您的 IP 和端口。并且在您的容器之间使用相同的 docker 网络。

  • 验证您的服务器应用程序是否可以访问您的 mysql,反之亦然。进入 docker 容器并尝试 ping。

【讨论】:

  • @ShiheZhang 我刚刚编辑了我的答案。应该是从 jdbc:mysql://mysql:3306/DATABASE_URI_PATH 到 jdbc:mysql://192.168.99.100:3306/DATABASE_URI_PATH
【解决方案2】:

感谢@rohit-thomas。我们将问题缩小到与 URL 的主机相关的问题。
简单的答案就是将spring bootapplication.properties中JDBC URL的host改为docker主机IP地址。发件人
spring.datasource.url=jdbc:mysql://mysql:3306/database?

spring.datasource.url=jdbc:mysql://172.17.0.1:3306/database?

From inside of a Docker container, how do I connect to the localhost of the machine?
这篇文章也有助于作为最终解决方案。

ip addr show docker0
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default        
    ...   
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       ...

我的结论是:
从镜像构建的 Jenkins 容器能够通过其容器名称或 docker bridge 网络上的私有地址与 MySQL 容器通信。但是,由于 Jenkins 构建的应用程序无法做到这一点。 由于 MySQL 容器端口已经绑定到宿主机,应用程序可以通过宿主机端口与 MySQL 容器通信。

如果结论有误,欢迎cmets。

【讨论】:

    【解决方案3】:

    您应该将 docker 容器 mysql 端口绑定到 VM 中的端口。

    这在下面的帖子中有很好的解释。

    值得一试...
    How to connect with MySQL DB running as container in docker?

    【讨论】:

    • docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=password mysql:5.7 已运行
    • 如果是端口问题,插件连接不上。
    猜你喜欢
    • 2019-06-21
    • 1970-01-01
    • 1970-01-01
    • 2020-07-28
    • 1970-01-01
    • 2020-07-14
    • 2018-10-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多