Search code examples
dockerdocker-network

Docker container cannot connect to another container that I know is running


I have a MySQL container that I define with a docker-compose.yml file like so:

version: "3.7"
services:
  mydb:
    image: mysql:8
    container_name: my_db_local
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    ports:
      - 3306:3306
    environment:
      MYSQL_ROOT_PASSWORD: 12345
      MYSQL_DATABASE: my_db_local
      MYSQL_USER: someuser
      MYSQL_PASSWORD: somepassword
    volumes:
      - ./my-db-data:/var/lib/mysql

If I run docker-compose up -d I see that it spins up pretty quickly and I am able to connect to it from a SQL client running on my host machine (I connect to it at 0.0.0.0:3306).

I also have a containerized Java Spring Boot application that I manage with the following Dockerfile:

FROM openjdk:8-jdk-alpine as cce


COPY application.yml application.yml
COPY build/libs/myservice.jar myservice.jar

HEALTHCHECK CMD curl --fail https://localhost:9200/healthCheck || exit 1

EXPOSE 443

ENTRYPOINT [ \
    "java", \
    "-Dspring.config=.", \
    "-Ddb.hostAndPort=0.0.0.0:3306", \
    "-Ddb.name=my_db_local", \
    "-Ddb.username=someuser", \
    "-Ddb.password=somepassword", \
    "-jar", \
    "cim-service.jar" \
]

I can build this image like so:

docker build . -t myorg/myservice

And then run it like so:

docker run -d -p9200:9200 myorg/myservice

When I run it, it quickly dies on startup because it cannot connect to the MySQL container (which it uses as a database). Clearly the MySQL container is running since I can connect to it from my host with a SQL client. So its pretty obvious my network/port settings are awry in either the Docker Compose file, or more likely, inside my Spring Boot app's Dockerfile. I just don't know enough about Docker to figure out where I could have the misconfiguration. Any ideas?


Solution

  • The database host is not 0.0.0.0, that address is IPv4 for "listen on all interfaces" and some OS's interpret it to connecting back to a local interface, none of which will work in a container. Container networks are namespaced, so the container has it's own network interface separate from the host, and separate from the other containers.

    To connect between containers, you need to run the containers on the same docker network, that network needs to be user created (not the default bridge network named "bridge"), you connect by the container name or network alias, and you connect to the container port, not the host published port.

    What that looks like:

    ENTRYPOINT [ \
        "java", \
        "-Dspring.config=.", \
        "-Ddb.hostAndPort=mydb:3306", \
        "-Ddb.name=my_db_local", \
        "-Ddb.username=someuser", \
        "-Ddb.password=somepassword", \
        "-jar", \
        "cim-service.jar" \
    ]
    

    and:

    docker run -d -p9200:9200 --net $network_name_of_mysql myorg/myservice
    

    mydb will work because compose automatically creates an alias for the service name. There's no need to define a container_name in compose for this, and you often don't want one to allow multiple projects to start separately and for scaling a container.

    Note that it's a bad practice to include configuration like the database connection data in the image itself. You'll want to move this logic into an external config file that's mounted into the container, environment variables, or the compose file.