andrew andrew - 2 months ago 14
MySQL Question

Docker cannot connect application to MySQL

I am trying to run integration tests (in python) which depend on mysql. Currently they depend on SQL running locally, but I want them to depend on a MySQL running in docker.

Contents of Dockerfile:

FROM continuumio/anaconda3:4.3.1
WORKDIR /opt/workdir
ADD . /opt/workdir
RUN python setup.py install


Contents of Docker Compose:

version: '2'

services:
mysql:
image: mysql:5.6
container_name: test_mysql_container
environment:
- MYSQL_ROOT_PASSWORD=test
- MYSQL_DATABASE=My_Database
- MYSQL_USER=my_user
- MYSQL_PASSWORD=my_password
volumes:
- db_data:/var/lib/mysql
restart: always
expose:
- "3306"

my_common_package:
image: my_common_package
depends_on:
- mysql
restart: always
links:
- mysql

volumes:
db_data:


Now, I try to run the tests in my package using:

docker-compose run my_common_package python testsql.py


and I receive the error


pymysql.err.OperationalError: (2003, "Can't connect to MySQL server on
'localhost' ([Errno 99] Cannot assign requested address)")

Answer Source

docker-compose will by default create virtual network were all the containers/services in the compose file can reach each other by an IP address. By using links, depends_on or network aliases they can reach each other by host name. In your case the host name is the service name, but this can be overridden. (see: docs)

Your script in my_common_package container/service should then connect to mysql on port 3306 according to your setup. (not localhost on port 3306)

Also note that using expose is only necessary if the Dockerfile for the service don't have an EXPOSE statement. The standard mysql image already does this.

If you want to map a container port to localhost you need to use ports, but only do this if it's necessary.

services:
   mysql:
     image: mysql:5.6
     container_name: test_mysql_container
     environment:
       - MYSQL_ROOT_PASSWORD=test
       - MYSQL_DATABASE=My_Database
       - MYSQL_USER=my_user
       - MYSQL_PASSWORD=my_password
     volumes:
       - db_data:/var/lib/mysql
     ports:
       - "3306:3306"

Here we are saying that port 3306 in the mysql container should be mapped to localhost on port 3306.

Now you can connect to mysql using localhost:3306 outside of docker. For example you can try to run your testsql.py locally (NOT in a container).

Container to container communication will always happen using the host name of each container. Think of containers as virtual machines.

You can even find the network docker-compose created using docker network list:

1b1a54630639        myproject_default             bridge              local
82498fd930bb        bridge                        bridge              local

.. then use docker network inspect <id> to look at the details.

Assigned IP addresses to containers can be pretty random, so the only viable way for container to container communication is using hostnames.