3

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?

smeeb
  • 27,777
  • 57
  • 250
  • 447
  • 1
    Try `172.17.0.1` instead of localhost – Robert Mar 07 '18 at 22:28
  • Thanks @Robert (+1) I tried your suggestion but got the same results. – smeeb Mar 13 '18 at 14:08
  • The mysql server is on the host server? Try to use the ip of the server (the public ip if it has or the ip in the lan), also check the firewall. And use the --net arg. – Jose Da Silva Gomes Mar 14 '18 at 03:58
  • Docker for Mac has some [known limitations](https://docs.docker.com/docker-for-mac/networking/#known-limitations-use-cases-and-workarounds). Have you tried to use `docker.for.mac.host.internal` as it proposed in the section *"Use cases and workarounds"*? – tgogos Mar 14 '18 at 09:53

2 Answers2

2

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?

Rafał Pydyniak
  • 539
  • 4
  • 11
  • Thanks @Rafal (+1) please see my changes above. Even with running `--net="host"` I am getting the same errors. Any ideas? – smeeb Mar 13 '18 at 14:08
2

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.

Sasha Shpota
  • 9,436
  • 14
  • 75
  • 148
  • Thanks @Oleksandr (+1) I'll give it a try in a few hours! In the meantime, super quick question: let's say I *did* wrap my MySQL DB into a container that I named `myapp_db`. How would I change the `docker run` command in my question to reference/link to this container? Thanks again! – smeeb Mar 14 '18 at 17:31
  • You would need to create docker network and attach both containers to it, it is not just one command, see https://docs.docker.com/network/bridge/ – Sasha Shpota Mar 14 '18 at 18:05