docker -композиция не может ждать mysql база данных - PullRequest
0 голосов
/ 27 марта 2020

У меня возникли реальные проблемы при попытке создать сценарий docker, чтобы инициировать базу данных mysql и проект Django, но заставить проект Django ждать, пока база данных mysql не будет готова.

У меня есть два файла: Dockerfile и docker -compose.yml, которые я скопировал ниже.

Когда я запускаю docker -compose.yml и проверяю Журналы веб-контейнера, он говорит, что не может подключиться к базе данных mydb. Однако во второй раз, когда я запускаю его (без очистки контейнеров и изображений), он подключается правильно, и приложение Django работает.

Я провел целый день, пытаясь выполнить ряд задач, таких как сценарии, проверки работоспособности et c, но я не могу заставить его работать.

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

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

Первый запуск (без существующие изображения или контейнеры):

...
  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)")

Второй запуск:

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.

Ответы [ 3 ]

1 голос
/ 28 марта 2020

Для тех, кто заинтересован, я нашел решение этой проблемы:

1 - я написал скрипт python для подключения к базе данных каждую секунду, но с таймаутом. Я установил этот тайм-аут на 60 секунд, но это похоже на мой компьютер.

2 - я добавил команду для ожидания в свой файл композиции.

Это должно означать, что я могу вызвать набор тестовых контейнеров для своего веб-сайта, где я могу указать точную версию Python и MySQL.

Соответствующие файлы перечислены ниже:

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

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

'''
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()
1 голос
/ 27 марта 2020

В целях тестирования / разработки вы можете использовать версию образа MySQL с проверкой работоспособности (я думаю, что это изображение healthcheck/mysql) или настроить свою собственную (см. Пример здесь: Docker -compose проверить, если mysql соединение готово ).

Для производственного использования вы не хотите обновлять схему базы данных при запуске, и при этом вы не хотите предполагать, что база данных работает. Обновление схемы автоматически побуждает вас не думать о том, что происходит, когда вы развертываете ошибку и нуждаетесь в откате, а параллельные обновления схемы не будут работать. Более длинная версия: https://pythonspeed.com/articles/schema-migrations-server-startup/

0 голосов
/ 27 марта 2020

Другим вариантом является использование сценария для управления порядком запуска и переноса команды веб-службы.

В документации docker -compose" wait-for -it"является одним из рекомендуемых инструментов, но существует другой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...