简体   繁体   English

docker-compose 无法等待 mysql 数据库

[英]docker-compose cannot wait for mysql database

I am having real problems trying to get a docker-compose script to initiate a mysql database and a Django project, but get the Django project to wait until the mysql database is ready.我在尝试使用 docker-compose 脚本来启动 mysql 数据库和 Django 项目时遇到了真正的问题,但是让 Django 项目等待 mysql 数据库准备就绪。

I have two files, a Dockerfile and a docker-compose.yml, which I have copied below.我有两个文件,一个 Dockerfile 和一个 docker-compose.yml,我在下面复制了它们。

When I run the docker-compose.yml, and check the logs of the web container, it says that it cannot connect to the database mydb.当我运行 docker-compose.yml 并检查 Web 容器的日志时,它说它无法连接到数据库 mydb。 However the second time that I run it (without clearing the containers and images) it connects properly and the Django app works.然而,我第二次运行它(没有清除容器和图像)它正确连接并且 Django 应用程序工作。

I have spent a whole day trying a number of things such as scripts, health checks etc, but I cannot get it to work.我花了一整天的时间尝试了很多东西,比如脚本、健康检查等,但我无法让它工作。

Dockerfile文件

FROM python:3.6

ENV PYTHONUNBUFFERED 1

RUN mkdir /code
WORKDIR /code

COPY ./ /code/

RUN pip install -r requirements.txt
RUN python manage.py collectstatic --noinput

docker-compose.yml docker-compose.yml

version: '3'

services:
  mydb:
    environment:
      - MYSQL_ROOT_PASSWORD=password
      - MYSQL_USER=django
      - MYSQL_PASSWORD=secret
      - MYSQL_DATABASE=dbMarksWebsite
    image: mysql:5.7
    ports:
      # Map default mysql port 3306 to 3308 on outside so that I can connect
      # to mysql using workbench localhost with port 3308
      - "3308:3306"
  web:
    environment:
      - DJANGO_DEBUG=1
      - DOCKER_PASSWORD=secret
      - DOCKER_USER=django
      - DOCKER_DB=dbMarksWebsite
      - DOCKER_HOST=mydb
      - DOCKER_PORT=3306
    build: .
    command: >
      sh -c "sleep 10 &&
             python manage.py migrate &&
             python manage.py loaddata myprojects_testdata.json &&
             python manage.py runserver 0.0.0.0:8080"
    ports:
      - "8080:8080"
    depends_on:
      - mydb

First run (with no existing images or containers):第一次运行(没有现有的图像或容器):

...
  File "/usr/local/lib/python3.6/site-packages/MySQLdb/__init__.py", line 84, in Connect
    return Connection(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/MySQLdb/connections.py", line 179, in __init__
    super(Connection, self).__init__(*args, **kwargs2)
django.db.utils.OperationalError: (2002, "Can't connect to MySQL server on 'mydb' (115)")

Second run:第二次运行:

System check identified no issues (0 silenced).
March 27, 2020 - 16:44:57
Django version 2.2.11, using settings 'ebdjango.settings'
Starting development server at http://0.0.0.0:8080/
Quit the server with CONTROL-C.

For anybody who is interested, I found a solution to this:对于任何感兴趣的人,我找到了解决方案:

1 - I wrote a python script to connect to the database every second, but with a timeout. 1 - 我写了一个 python 脚本来每秒连接到数据库,但有超时。 I set this timeout to be quite high at 60 seconds, but this seems to work on my computer.我将此超时设置为相当高的 60 秒,但这似乎适用于我的计算机。

2 - I added the command to wait into my compose file. 2 - 我在我的撰写文件中添加了等待命令。

It should mean that I can bring up a set of test containers for my website, where I can specify the exact version of Python and MySQL used.这应该意味着我可以为我的网站提供一组测试容器,我可以在其中指定所使用的 Python 和 MySQL 的确切版本。

The relevant files are listed below:相关文件如下:

Dockerfile: Dockerfile:

FROM python:3.6

ENV PYTHONUNBUFFERED 1

RUN mkdir /code
WORKDIR /code

COPY ./ /code/

RUN pip install -r requirements.txt
RUN python manage.py collectstatic --noinput

docker-compose.yml docker-compose.yml

version: '3'

services:
  mydb:
    container_name: mydb
    environment:
      - MYSQL_ROOT_PASSWORD=password
      - MYSQL_USER=django
      - MYSQL_PASSWORD=secret
      - MYSQL_DATABASE=dbMarksWebsite
    image: mysql:5.7
    ports:
      # Map default mysql port 3306 to 3308 on outside so that I can connect
      # to mysql using workbench localhost with port 3308
      - "3308:3306"
  web:
    container_name: web
    environment:
      - DJANGO_DEBUG=1
      - DOCKER_PASSWORD=secret
      - DOCKER_USER=django
      - DOCKER_DB=dbMarksWebsite
      - DOCKER_HOST=mydb
      - DOCKER_PORT=3306
    build: .
    command: >
      sh -c "python ./bin/wait-for.py mydb 3306 django secret dbMarksWebsite 60  &&
             python manage.py migrate &&
             python manage.py loaddata myprojects_testdata.json &&
             python manage.py runserver 0.0.0.0:8080"
    ports:
      - "8080:8080"
    depends_on:
      - mydb

wait-for.py等待.py

'''
I don't like adding this in here, but I cannot get the typical wait-for scripts
to work with MySQL database in docker, so I hve written a python script that
either times out after ? seconds or successfully connects to the database
The input arguments for the script need to be:

    HOST, PORT, USERNAME, PASSWORD, DATABASE, TIMEOUT

'''
import sys, os
import time
import pymysql

def readCommandLineArgument():
    '''
    Validate the number of command line input arguments and return the
    input filename
    '''

    # Get arguments
    if len(sys.argv)!=7:
        raise ValueError("You must pass in 6 arguments, HOST, PORT, USERNAME, PASSWORD, DATABASE, TIMEOUT")

    # return the arguments as a tuple
    return (sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5], sys.argv[6]) 

def connectToDB(HOST, PORT, USERNAME, PASSWORD, DATABASE):
    '''
    for now, just try to connect to the database.
    '''

    con = pymysql.connect(host=HOST, port=PORT, user=USERNAME, password=PASSWORD, database=DATABASE)

    with con:
        cur = con.cursor()
        cur.execute("SELECT VERSION()")

def runDelay():
    '''
    I don't like passing passwords in, but this is only used for a test docker
    delay script
    '''

    # Get the database connection characteristics.
    (HOST, PORT, USERNAME, PASSWORD, DATABASE, TIMEOUT) = readCommandLineArgument()

    # Ensure timeout is an integer greater than zero, otherwise use 15 secs a default
    try:
        TIMEOUT = int(TIMEOUT)
        if TIMEOUT <= 0:
            raise("Timeout needs to be > 0")
    except:
        TIMEOUT = 60

    # Ensure port is an integer greater than zero, otherwise use 3306 as default
    try:
        PORT = int(PORT)
        if PORT <= 0:
            raise("Port needs to be > 0")
    except:
        PORT = 3306

    # Try to connect to the database TIMEOUT times
    for i in range(0, TIMEOUT):

        try:
            # Try to connect to db
            connectToDB(HOST, PORT, USERNAME, PASSWORD, DATABASE)

            # If an error hasn't been raised, then exit
            return True

        except Exception as Ex:
            strErr=Ex.args[0]
            print(Ex.args)
            # Sleep for 1 second
            time.sleep(1)

    # If I get here, assume a timeout has occurred
    raise("Timeout")


if __name__ == "__main__":

    runDelay()

For testing/development purposes, you could use a version of the MySQL image that has health checks (I believe there's a healthcheck/mysql image), or configure your own (see example here: Docker-compose check if mysql connection is ready ).出于测试/开发目的,您可以使用具有运行状况检查的 MySQL 映像版本(我相信有一个healthcheck/mysql映像),或配置您自己的(请参见此处的示例: Docker-compose check if mysql connection is ready )。

For production use, you don't want to upgrade the database schema on startup, nor do you want to assume the database is up.对于生产用途,您不想在启动时升级数据库架构,也不想假设数据库已启动。 Upgrading schema automatically encourages you to not think about what happens when you deploy a bug and need to rollback, and parallel schema upgrades won't work.升级架构会自动鼓励您不要考虑部署错误并需要回滚时会发生什么,并且并行架构升级将不起作用。 Longer version: https://pythonspeed.com/articles/schema-migrations-server-startup/更长的版本: https : //pythonspeed.com/articles/schema-migrations-server-startup/

I solved it using the following function in my entrypoint.sh:我在 entrypoint.sh 中使用以下函数解决了它:

function wait_for_db()
{
  while ! ./manage.py sqlflush > /dev/null 2>&1 ;do
    echo "Waiting for the db to be ready."
    sleep 1
  done
}

Another option is to use a script to control the startup order, and wrap the web service's command.另一种选择是使用脚本来控制启动顺序,并包装 Web 服务的命令。

In the docker-compose's documentation " wait-for-it " is one of the recommended tools, but other exists.docker-compose 的文档中,wait-for-it ”是推荐的工具之一,但也存在其他工具。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM