【问题标题】:Dockerized Mac/Java app can't talk to localhostDockerized Mac/Java 应用程序无法与 localhost 对话
【发布时间】:2018-08-16 01:48:05
【问题描述】:

MacOS + Docker(版本 17.12.0-ce-mac49 (21995))在这里。我正在尝试 Dockerize 现有的 Spring Boot 应用程序。这是我的Dockerfile

FROM openjdk:8

RUN mkdir /opt/myapp

ADD build/libs/myapp.jar /opt/myapp
ADD application.yml /opt/myapp
ADD logback.groovy /opt/myapp
WORKDIR /opt/myapp
EXPOSE 9200
ENTRYPOINT ["java", "-Dspring.config=.", "-jar", "myapp.jar"]

这是我的 Spring Boot application.yml 配置文件。如您所见,它希望 Docker 从 env 文件中注入环境变量:

logging:
  config: 'logback.groovy'
server:
  port: 9200
  error:
    whitelabel:
      enabled: true
spring:
  cache:
    type: none
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://${DB_HOST}:3306/myapp_db?useSSL=false&nullNamePatternMatchesAll=true
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
    testWhileIdle: true
    validationQuery: SELECT 1
  jpa:
    show-sql: false
    hibernate:
      ddl-auto: none
      naming:
        physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
        implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
    properties:
      hibernate.dialect: org.hibernate.dialect.MySQL5InnoDBDialect
      hibernate.cache.use_second_level_cache: false
      hibernate.cache.use_query_cache: false
      hibernate.generate_statistics: false
      hibernate.hbm2ddl.auto: validate
myapp:
  detailsMode: ${DETAILS_MODE}
  tokenExpiryDays:
    alert: 5
  jwtInfo:
    secret: ${JWT_SECRET}
    expiry: ${JWT_EXPIRY}
  topics:
    adminAlerts: admin-alerts

这是我的myapp-local.env 文件:

DB_HOST=localhost
DB_USERNAME=root
DB_PASSWORD=
DETAILS_MODE=Terse
JWT_SECRET=12345==
JWT_EXPIRY=86400000

值得注意的是,在上面的env文件中,我尝试了localhost127.0.0.1172.17.0.1,它们都在下面产生了相同的错误。

然后我构建容器:

docker build -t myapp .

成功!然后我运行容器:

docker run -it -p 9200:9200 --net="host" --env-file myapp-local.env --name myapp myapp

...我看到容器因 MySQL 连接相关异常而迅速死亡(无法连接到本地运行的 MySQL 机器)。我可以确认 Spring Boot 应用程序在作为 Docker 外部的可执行(“胖”)jar 运行时连接到 MySQL 没有问题,并且我可以确认本地 MySQL 实例已启动并运行并且非常健康。

Unable to connect to database. }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.
    at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:590)
    at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:57)
    at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:1606)
    at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:633)
    at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:347)

当我打开 TRACE 级别的登录时,我看到它正在尝试连接到:

url=jdbc:mysql://localhost:3306/myapp?useSSL=false&nullNamePatternMatchesAll=true

所以 确实 看起来 Docker 正在将 env 文件的变量正确地注入到基于 Spring YAML 的配置中。所以这感觉不像是配置问题,而且是容器与 Docker 主机上运行的 MySQL 端口通信的问题。

谁能看出我哪里出错了?

【问题讨论】:

  • 尝试172.17.0.1而不是本地主机
  • 谢谢@Robert (+1) 我尝试了你的建议,但得到了相同的结果。
  • mysql服务器在宿主服务器上?尝试使用服务器的ip(如果有公共ip或局域网中的ip),还要检查防火墙。并使用 --net 参数。
  • Docker for Mac 有一些known limitations。您是否尝试过按照 “用例和解决方法” 部分中的建议使用 docker.for.mac.host.internal

标签: java mysql macos docker


【解决方案1】:

所以基本上 Docker 应用程序与您运行它的主机不在同一个网络中,这就是为什么您不能通过指向 localhost 来访问 MySQL(因为从 Docker 的角度来看,这是另一个网络)。 您可以尝试使用 --net="host" 选项运行 docker,然后它将与其主机共享网络。

您可以在本主题From inside of a Docker container, how do I connect to the localhost of the machine?中找到有关此问题的更好解释

【讨论】:

  • 感谢@Rafal (+1) 请在上面查看我的更改。即使运行--net="host",我也会遇到同样的错误。有什么想法吗?
【解决方案2】:

不建议从容器内访问主机。通常可以通过将您需要的服务包装到容器中并通过容器名称访问它来解决。

没有解决办法,只有变通办法,您可以使用其中一种:

在 Mac 上,您可以使用 docker.for.mac.host.internal DNS 名称访问主机服务。

你需要像这样设置环境变量:

DB_HOST=docker.for.mac.host.internal

并从您的连接字符串中引用DB_HOST

更多详情see the documentation:

从 17.12 起,我们的建议是连接到特殊的 仅限 Mac 的 DNS 名称 docker.for.mac.host.internal,解析为 主机使用的内部 IP 地址。

注意:拥有--net="host" 不会让您通过localhost 访问主机。 localhost 总是指向本地机器,但如果从容器内调用它,它指向容器本身。

【讨论】:

  • 谢谢@Oleksandr (+1) 我会在几个小时内试一试!同时,一个超级快速的问题:假设我确实将我的 MySQL DB 包装到我命名为 myapp_db 的容器中。我将如何更改我的问题中的 docker run 命令以引用/链接到此容器?再次感谢!
  • 您需要创建 docker 网络并将两个容器附加到它,这不仅仅是一个命令,请参阅docs.docker.com/network/bridge
猜你喜欢
  • 1970-01-01
  • 2021-09-20
  • 1970-01-01
  • 2020-12-26
  • 1970-01-01
  • 2017-12-29
  • 1970-01-01
  • 2022-06-19
  • 2020-06-24
相关资源
最近更新 更多