1

For a simple app with Django, Python3, Docker on mac

Dockerfile

FROM python:3
ENV PYTHONUNBUFFERED=1

RUN mkdir /code

WORKDIR /code
COPY requirements.txt /code/
RUN python3 -m pip install -r requirements.txt
CMD python3 manage.py runserver  

COPY . /code/

docker-compose.yml

version: "3.9"

services:
  # DB
  db:
    image: mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: '****'
      MYSQL_USER: '****'
      MYSQL_PASSWORD: '****'
      MYSQL_DATABASE: 'mydb'
    ports:
      - "3307:3306"
    expose:
      # Opens port 3306 on the container
      - '3307'
    volumes:
      - $HOME/proj/sql/mydbdata.sql:/mydbdata.sql

  # Web app
  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

Also, what I wanted is to execute the SQL the first time the image is created, after that database should be mounted.

    volumes:
  - $HOME/proj/sql/mydbdata.sql:/mydbdata.sql

Looks like the Docker is starting but from my browser, I get this response

localhost didn’t send any data.
ERR_EMPTY_RESPONSE

what is that I am missing. Please help

indianwebdevil
  • 4,879
  • 7
  • 37
  • 51

1 Answers1

3

Looks like your django project is running already when you create image. Since you use command option docker-compose.yml file, you don't need CMD command in Dockerfile in this case.

I would rewrite Dockerfile and docker-compose.yml as follows:

FROM python:3
ENV PYTHONUNBUFFERED 1

RUN mkdir /code

WORKDIR /code
COPY requirements.txt /code/
RUN python3 -m pip install -r requirements.txt

COPY . /code/
version: "3.9"

services:
  db:
    image: mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: '****'
      MYSQL_USER: '****'
      MYSQL_PASSWORD: '****'
      MYSQL_DATABASE: 'mydb'
    ports:
      - "3307:3306"  # make sure django project connects to 3306 port  
    volumes:
      - $HOME/proj/sql:/docker-entrypoint-initdb.d

  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

A few things to point out.

  1. When you run docker-compose up, you will probably see an error, because your django project will already be running even before db is initialised. That's natural. So you need customized command or shell program to force django project to wait to try to connect db.

In my case I would use a custom command.

version: "3.9"

services:

  db:
    image: mysql:8
    env_file:
      - .env
    command: 
      - --default-authentication-plugin=mysql_native_password
    restart: always
    ports:
      - "3308:3306"

  web:
    build: .
    command: >
      sh -c "python manage.py wait_for_db &&
             python manage.py makemigrations && 
             python manage.py migrate && 
             python manage.py runserver 0.0.0.0:8000"
    volumes:
      - .:/code
    ports:
      - "8001:8000"
    depends_on: 
      - db
    env_file:
      - .env

Next, wait_for_db.py. This file is what I created in myapp/management/commands/wait_for_db.py. With this you postpone db connection until db is ready. This SO post has helped me a lot.

See Writing custom django-admin command for detail.

import time

from django.db import connection
from django.db.utils import OperationalError
from django.core.management.base import BaseCommand


class Command(BaseCommand):
    """Wait to connect to db until db is initialised"""

    def handle(self, *args, **options):
        start = time.time()
        self.stdout.write('Waiting for database...')
        while True:
            try:
                connection.ensure_connection()
                break
            except OperationalError:
                time.sleep(1)

        end = time.time()
        self.stdout.write(self.style.SUCCESS(f'Database available! Time taken: {end-start:.4f} second(s)'))
  1. Looks like you want to populate your database with sql file when your db container starts running. Mysql docker hub says

Initializing a fresh instance When a container is started for the first time, a new database with the specified name will be created and initialized with the provided configuration variables. Furthermore, it will execute files with extensions .sh, .sql and .sql.gz that are found in /docker-entrypoint-initdb.d. Files will be executed in alphabetical order. You can easily populate your mysql services by mounting a SQL dump into that directory and provide custom images with contributed data. SQL files will be imported by default to the database specified by the MYSQL_DATABASE variable.

So your .sql file should be located in /docker-entrypoint-initdb.d in your mysql container. See this post for more.

  1. Last but not least, your db is lost when you run docker-compose down, since you don't have volumes other than sql file. It that's not what you want, you might want to consider the following
version: "3.9"

services:

  db:
...
    volumes:
      - data:/var/lib/mysql

...

volumes:
  data:
harryghgim
  • 1,340
  • 4
  • 12
  • 33