两个 docker 容器之间的通信问题
Posted
技术标签:
【中文标题】两个 docker 容器之间的通信问题【英文标题】:Trouble communicating between two docker containers 【发布时间】:2018-07-15 03:55:54 【问题描述】:我是 docker 新手,我正在尝试将运行在 boot-example docker 容器中的 spring boot 应用程序连接到运行在 mymysql 中的 mysql 服务器6603 端口上的 docker 容器,两者都在同一台物理机器上运行。 事实是:如果我将我的 spring-boot 应用程序连接到我的 mymysql docker 容器以便与数据库通信,我不会收到任何错误,并且一切正常。
当我将 Spring Boot 应用程序 移动到我的 boot-example 容器中并尝试(通过 Hibernate)与我的 mymysql 容器进行通信时,然后我得到这个错误:
2018-02-05 09:58:38.912 ERROR 1 --- [ main] o.a.tomcat.jdbc.pool.ConnectionPool : Unable to create initial connections of pool.
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.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_111]
我的spring boot application.properties是:
server.port=8083
spring.jpa.hibernate.ddl-auto=create-drop
spring.datasource.url=jdbc:mysql://localhost:6603/mydockerdb
spring.datasource.username=root
spring.datasource.password=mypassword
在我的 spring boot 应用程序在端口 8082 上的 docker 容器中运行之前,它可以正常工作,(在正确构建 docker 映像之后):
docker run -it -p 8082:8083 boot-example
【问题讨论】:
【参考方案1】:您不能在容器内使用localhost
,它是容器本身。因此,您将始终收到连接被拒绝错误。
你可以做以下事情 -
在您的 Spring Boot 应用程序的 application.properties
文件中添加您的主机 IP。 (不推荐,因为它破坏了 docker 可移植性逻辑)
如果您想使用localhost
,请在启动容器时使用--net=host
。 (不推荐用于生产,因为不存在逻辑网络层)
使用--links
与 DNS 名称进行容器通信。 (已弃用/旧版)
创建一个 compose 文件并使用服务名称从 Spring Boot 应用程序调用您的数据库,因为它们将位于同一网络中并且彼此高度集成。 (推荐)
PS - 每当您需要将多个容器集成在一起时,请始终使用docker-compose version 3+
。使用docker run|build
了解基本原理并进行试运行。
【讨论】:
我建议使用链接进行容器通信。内部 Spring Boot application.yml 可以准备使用 MySQL 数据库的特定主机名,例如“jdbc:mysql://mydatabase:6603/mydockerdb”。如果我们想在开发过程中拥有一个本地数据库,可能会有一个特殊的“application-dev.yml”文件,其中包含数据库的本地主机 URL。可以通过启用“dev”Spring 配置文件来激活此配置。 我使用过 --net=host 并且它可以工作但是......即使我将 java 容器映射为 -p 8082:8083 我的 spring boot 服务在端口 8083 上可用而不是在 8082使用 Docker 端口...为什么?谢谢! 由于您的容器是在主机网络上启动的,它使用主机 TCP 端口空间来暴露在容器内运行的服务。当您开始使用--net=host
时,Docker 网络的工作方式不同。将其视为在您的主机上运行的另一个进程,没有任何隔离网络。【参考方案2】:
正如@vivekyad4v 建议的那样 - 实现您的愿望的最简单方法是使用具有更好容器通信集成的docker-compose
。
Docker-compose 是一个用于管理单个或多个 docker 容器的工具。它使用一个名为docker-compose.yml
的配置文件。
有关 docker-compose 的更多信息,请查看 documentation 和 compose file reference
根据我的经验,遵循 SRP(单一职责原则)是一种很好的做法,因此 - 为您的数据库创建一个容器,为您的应用程序创建一个容器。它们都使用您在配置中指定的网络进行通信。
以下docker-compose.yml
的示例可能会对您有所帮助:
version: '2'
networks:
# your network name
somename:
driver: bridge
services:
# php server
php:
image: dalten/php5.6-apache
ports:
- 80:80
volumes:
- .application_path:/some/application/path
# your container network name defined at the beggining
networks:
- somename
# Mysql server for backend
mysql:
image: dalten/mysql:dev
ports:
- 3306:3306
# The /var/lib/mysql volume MUST be specified to achieve data persistence over container restart
volumes:
- ./mysql_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: backend
# your container network name defined at the beggining
networks:
- somename
注意:网络内容器之间的通信可以通过在容器内调用service name
来实现。
从 PHP 到 MySQL 容器的连接参数,在这个例子中是:
hostname: mysql
port: 3306
database: backend
user: root
password: root
【讨论】:
【参考方案3】:根据上述建议,Docker-compose 是一种方法,但如果您不想使用 compose/swarm 模式。
-
只需使用
docker network create myNet
创建您自己的网络
部署您的容器侦听创建的网络--network myNet
将 spring.datasource.url 更改为 jdbc:mysql://mymysql:6603/mydockerdb
通过使用 docker demo 的 DNS 解析,容器可以发现彼此,从而进行通信。 [默认网桥不支持DNS。使用网桥的用户定义网络。]
欲了解更多信息:https://docs.docker.com/engine/userguide/networking/
【讨论】:
感谢您的回答,使用网络您可以加入识别同一网络的两个容器。但是,如果我只想将我的 mysql 容器部署到两个单独的服务器呢?我想像所有传统连接一样连接两个容器,它们只有它们的地址和端口。 您的意思是在另一台服务器上部署 MySQL 容器,然后将它与在另一台服务器上运行的另一个容器连接起来?请说清楚。 :)以上是关于两个 docker 容器之间的通信问题的主要内容,如果未能解决你的问题,请参考以下文章