Django无法在docker-compose中连接MySQL

11

我对docker非常陌生,现在我尝试通过docker-compose在docker中运行django和mariadb,但是我总是收到以下错误:

我使用的版本是:Docker version 17.09.1-ce, build 19e2cf6, docker-compose version 1.18.0, build 8dd22a9

django.db.utils.OperationalError: (2003, '无法连接到MySQL服务器\'mariadb55\'(111 "Connection refused")')

在本地或远程运行docker-compose up db后,我可以正确连接数据库,并且我甚至可以在anaconda虚拟环境中正确运行python manage.py runserver 0.0.0.0:6001以通过settings.py文件设置参数来连接docker中的db服务。

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',
        }
    }
}

这是我的 docker-compose.yml 文件。
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

我尽可能尝试了所有可以找到的方法,但仍然无法解决问题,希望能得到帮助!

我已经尝试过以下方法:

Docker Compose中MySQL连接失败

使用Docker Compose连接Django和MySQL容器

Django通过Docker Compose连接Postgres


你的 docker-compose.yml 文件中 MySQL 服务在哪里? - Janshair Khan
我使用 mariadb,它们是相同的。 - Belter
你能提供从Web连接到数据库所使用的URL吗? - yamenk
@yamenk 是哪个网址? - Belter
4个回答

17

最后,我弄清楚了!关键是,就像 @SangminKim 所说的那样,在 settings.py 中我需要使用 3306 而不是 3302,并将 HOST 使用 db 而不是 127.0.0.1

所以这是我的 docker-compose.yml 文件:

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

现在我们可以通过在shell中直接使用mysql -h 127.0.0.1 -P 3302 -u root -p连接到这个docker-mysql,但是在django的settings.py文件中我们必须使用db3306

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',
        }
    }
}

我们仍然可以通过在docker-compose.yml文件中运行额外的命令来检查此端口是否打开:

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

这里是check_db.py文件:

# 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)

顺便说一下,这是我用来构建django-py35Dockerfile

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

点击链接获取更多详情:https://github.com/OnlyBelter/django-compose


3
settings.py文件中,应该使用容器名称而非localhost(或127.0.0.1)。尝试在docker-compose.yml文件中为db服务提供一个容器名称,并用container_name属性替换settings.py中的主机名为container_name的值。(确保它们位于docker compose创建的同一网络中。)

我使用 docker ps 命令查找正在运行的容器名称为 djangopy35_db_1,并修改了我的 settings.py 文件。但是我仍然遇到了相同的错误:django.db.utils.OperationalError: (2003, 'Can\'t connect to MySQL server on \'djangopy35_db_1\' (111 "Connection refused")') - Belter
1
@Belter,您还应该在settings.py中将端口更改为3306而不是3302 - SangminKim
是的,你说得对!如果我把 db 作为 HOST,那么我需要使用 3306 作为 PORT - Belter

1

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

请确保 MYSQL_ROOT_HOST='%',这意味着 root 用户可以从任何 IP 连接。


0

我曾经遇到过一个类似的问题,是在使用 Django + Postgres 应用程序时出现的。我通过在我的 settings.py 中包含以下内容来解决它:

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

    }
}

这基于我几年前使用的一个解决方案:https://dev59.com/YV0Z5IYBdhLWcg3wiw4w#62147846


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接