menu

Questions & Answers

django cannot connect mysql in docker-compose

I'm very new for docker, now I am trying to run django with mariadb in docker through docker-compose, but I always get this error:

I use Docker version 17.09.1-ce, build 19e2cf6, docker-compose version 1.18.0, build 8dd22a9

django.db.utils.OperationalError: (2003, 'Can\'t connect to MySQL server on \'mariadb55\' (111 "Connection refused")')

I can connect db correctly after run docker-compose up db in local or remote, and I even can run python manage.py runserver 0.0.0.0:6001 correctly in anaconda virtual environment to connect db service in docker by setting parameters of settings.py file like below:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'test',
        'USER': 'belter',
        # 'HOST': 'mariadb55',
        'HOST': '127.0.0.1',
        'PORT': '3302',
        'PASSWORD': 'belter_2017',
        'default-character-set': 'utf8',
        'OPTIONS': {
            'sql_mode': 'traditional',
        }
    }
}

This is my docker-compose.yml file

version: '3'

services:
  db:
    image: mariadb:5.5
    restart: always
    environment:
      - MYSQL_HOST=localhost
      - MYSQL_PORT=3306
      - MYSQL_ROOT_HOST=%
      - MYSQL_DATABASE=test
      - MYSQL_USER=belter
      - MYSQL_PASSWORD=belter_2017
      - MYSQL_ROOT_PASSWORD=123456_abc
    volumes:
      - /home/belter/mdbdata/mdb55:/var/lib/mysql
    ports:
      - "3302:3306"
  web:
    image: onlybelter/django_py35
    command: python3 manage.py runserver 0.0.0.0:6001
    volumes:
      - /mnt/data/www/mysite:/djcode
    ports:
      - "6001:6001"
    depends_on:
      - db
    links:
      - db:mariadb55

I almost tried everything I can find, but still cannot figure it out, any help would be nice!

What I have tried:

Docker compose mysql connection failing

Linking django and mysql containers using docker-compose

Django connection to postgres by docker-compose

Comments:
2023-05-26 23:55:15
Where is your MySQL service in docker-compose.yml file?
2023-05-26 23:55:15
I use mariadb, they are same.
2023-05-26 23:55:15
Can you provide the url that you use to connect from web to the db.
2023-05-26 23:55:15
@yamenk What url?
Answers(4) :

Finally, I figured it out! The key point is, just as @SangminKim said, I need to use 3306 not 3302 in settings.py, and use db as HOST not 127.0.0.1.

So this is my docker-compose.yml file now:

version: '3'

services:
  db:
    image: mariadb:5.5
    restart: always
    environment:
      - MYSQL_HOST=localhost
      - MYSQL_PORT=3306  # cannot change this port to other number
      - MYSQL_ROOT_HOST=%
      - MYSQL_DATABASE=test
      - MYSQL_USER=belter
      - MYSQL_PASSWORD=belter_2017
      - MYSQL_ROOT_PASSWORD=123456_abc
    volumes:
      - /home/belter/mdbdata/mdb55:/var/lib/mysql
    ports:
      - "3302:3306"
  web:
    image: onlybelter/django_py35
    command: python3 manage.py runserver 0.0.0.0:6001
    volumes:
      - .:/djcode
    ports:
      - "6001:6001"
    depends_on:
      - db

So now we can connect this docker-mysql by mysql -h 127.0.0.1 -P 3302 -u root -p in shell directly, but we have to use db and 3306 in django settings.py file:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'test',
        'USER': 'belter',
        # 'HOST': 'mariadb55',
        'HOST': 'db',  #<---
        'PORT': '3306',   #<---
        'PASSWORD': 'belter_2017',
        'default-character-set': 'utf8',
        'OPTIONS': {
            'sql_mode': 'traditional',
        }
    }
}

And we can still check if this port is open, by running extra command in docker-compose.yml file:

...
  web:
    image: onlybelter/django_py35
    command: /bin/sh -c "python check_db.py --service-name mysql --ip db --port 3306"
    volumes:
      - .:/djcode
...

Here is check_db.py file:

# check_db.py 

import socket
import time
import argparse
""" Check if port is open, avoid docker-compose race condition """

parser = argparse.ArgumentParser(description='Check if port is open, avoid\
                                 docker-compose race condition')
parser.add_argument('--service-name', required=True)
parser.add_argument('--ip', required=True)
parser.add_argument('--port', required=True)

args = parser.parse_args()

# Get arguments
service_name = str(args.service_name)
port = int(args.port)
ip = str(args.ip)

# Infinite loop
while True:
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    result = sock.connect_ex((ip, port))
    if result == 0:
        print("{0} port is open! Bye!".format(service_name))
        break
    else:
        print("{0} port is not open! I'll check it soon!".format(service_name))
        time.sleep(3)

By the way, this is my Dockerfile for build django-py35:

FROM python:3.5-alpine
MAINTAINER Xin Xiong "xiongxin20008@126.com"
ENV PYTHONUNBUFFERED 1
RUN set -e; \
        apk add --no-cache --virtual .build-deps \
                gcc \
                libc-dev \
                linux-headers \
                mariadb-dev \
                python3-dev \
                postgresql-dev \
                freetype-dev \
                libpng-dev \
                g++ \
        ;
RUN mkdir /djcode
WORKDIR /djcode
ENV REFRESHED_AT 2017-12-25
ADD requirements.txt /djcode/
RUN pip install --no-cache-dir -r /djcode/requirements.txt
RUN pip install uwsgi
ADD . /djcode/  # copy . to /djcode/
EXPOSE 6001

See more details from here: https://github.com/OnlyBelter/django-compose

You should use the container name instead of localhost (or 127.0.0.1) in your settings.py file. Try providing a container name to the db service in the docker-compose.yml file using container_name attribute and replace the host name in the settings.py by the value of the container_name. (Make sure that they are in the same network that docker compose creates for you.)

Comments:
2023-05-26 23:55:15
I use docker ps to find the running container's name, which is djangopy35_db_1, and changed my settings.py file. But I got same error: django.db.utils.OperationalError: (2003, 'Can\'t connect to MySQL server on \'djangopy35_db_1\' (111 "Connection refused")')
2023-05-26 23:55:15
@Belter, You should also change the port to 3306 not 3302 in settings.py.
2023-05-26 23:55:16
Yes, you are right! If I use db as HOST, I need to use 3306 as PORT.

I have encountered something similar with a django + postgres application and was able to solve it by including this in my settings.py:

DATABASES = { 
        "default": {
         ...
        'HOST': 'host.docker.internal',
        ...

    }
}

It is based on a solution I used years ago: https://stackoverflow.com/a/62147846/7924573

Build container with this:

docker run --name mysql-latest  \
-p 3306:3306 -p 33060:33060  \
-e MYSQL_ROOT_HOST='%' -e MYSQL_ROOT_PASSWORD='strongpassword'   \
-d mysql/mysql-server:latest

Make sure MYSQL_ROOT_HOST='%', that means root can connect from any IP.