简体   繁体   中英

Dockerized Mac/Java app can't talk to localhost

MacOS + Docker (Version 17.12.0-ce-mac49 (21995)) here. I am trying to Dockerize an existing Spring Boot app. Here's my 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"]

Here's my Spring Boot application.yml config file. As you can see it expects Docker to inject environment variables from an env file:

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

Here's my myapp-local.env file:

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

It's worth noting that above in the env file, I have tried localhost , 127.0.0.1 and 172.17.0.1 and all of them produce identical errors below.

Then I build the container:

docker build -t myapp .

Success! Then I run the container:

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

...and I watch as the container quickly dies with MySQL connection-related exceptions (can't connect to the MySQL machine running locally). I can confirm that the Spring Boot app has no problem connecting to MySQL when it runs as an executable ("fat") jar outside of Docker, and I can confirm that the local MySQL instance is up and running and is perfectly healthy.

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)

When I turn TRACE-level logging on, I see it is trying to connect to:

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

So it does look like Docker is properly injecting the env file's vars into the Spring YAML-based config. So this doesn't feel like a config issue, moreover an isse with the container speaking to the MySQL port running on the Docker host.

Can anybody see where I'm going awry?

So basically Docker app is not in the same network as the host you're running it from and that's why you can't access MySQL by pointing to localhost (because this is another network from Docker's point of view). What you could try is to run docker with --net="host" option and then it will share the network with its host.

You can find better explanation on this issue in this topic From inside of a Docker container, how do I connect to the localhost of the machine?

Accessing the host machine from within a container is not recommended. Usually it can be solved by wrapping service you need into a container and accessing it via container name.

There is no solution, there are only workarounds, you can use one of them:

On Mac you can access the host services using docker.for.mac.host.internal DNS name.

You need to set environment variable like this:

DB_HOST=docker.for.mac.host.internal

And refer to the DB_HOST from your connection string.

For more details see the documentation :

From 17.12 onwards our recommendation is to connect to the special Mac-only DNS name docker.for.mac.host.internal, which resolves to the internal IP address used by the host.

Note: Having --net="host" doesn't let you reach the host machine via localhost . localhost always points to local machine, but in case if it is invoked from within a container it points to the container itself.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM