Django База [2023]: Докеризация проекта Django 4.1 🐋 #49
В этом уроке мы поговорим о том, как докеризировать наш Django проект, что позволит нам легко развернуть его на любой платформе и упростит процесс масштабирования.
Если вы хотите выразить благодарность автору сайта, статей и курса по Django, вы можете сделать это по ссылке ниже:
Докер - это платформа для разработки, доставки и запуска приложений в контейнерах. Контейнеры докера запускаются в изолированной среде, где они могут взаимодействовать только с другими контейнерами и ресурсами, которые были специально выделены для них.
Мы рассмотрим создание dev версии проекта, для локальной работы. А в следующем уроке с деплоем мы уже создадим prod версию нашего проекта для сборки на основе dev.
Расположение файлов, папка docker
Создадим папку docker в корне проекта, а внутри папки docker создадим следующие папки: env, logs, nginx. В них мы будем хранить переменные для файлов продакшена и разработки, а в логах логи celery и celery-beat, а в nginx будем хранить конфиги.
Создание env файла
Сначала мы создадим .env.dev файл в папке docker/env, для запуска локальной версии переменных с настройками. Дальше перед деплоем мы создадим тоже самое, но с настройками для продакшн версии.
Чтобы корректно работали переменные виртуального окружения, установим в наш проект пакет environ: pip install django-environ
Результат установки:
(venv) PS C:\Users\Razilator\Desktop\Base\backend> pip install django-environ
Collecting django-environ
Downloading django_environ-0.10.0-py2.py3-none-any.whl (19 kB)
Installing collected packages: django-environ
Successfully installed django-environ-0.10.0
[notice] A new release of pip available: 22.3 -> 23.0.1
[notice] To update, run: python.exe -m pip install --upgrade pip
Если мы настроим переменные в терминале VSCODE и запустим Django из него, проект не запустится, потому что Django не сможет их прочитать. Однако, если мы запустим проект через контейнер, то все будет работать, но это не решает проблему запуска проекта без Docker. Бывают ситуации, когда нужно быстро запустить, проверить и исправить проект, не используя Docker. Иногда настройка среды разработки может быть запутанной и сложной, поэтому мы ставим environ и можем работать с проектом.
Настроим виртуальные переменные в env.dev:
SECRET_KEY=<djangokey>
DEBUG=1
ALLOWED_HOSTS=127.0.0.1 localhost
CSRF_TRUSTED_ORIGINS=http://127.0.0.1 http://localhost
POSTGRES_DB=<dbName>
POSTGRES_USER=<dbUser>
POSTGRES_PASSWORD=<dbPassword>
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
REDIS_LOCATION=redis://redis:6379/1
CELERY_BROKER_URL=redis://redis:6379/0
CELERY_RESULT_BACKEND=redis://redis:6379/0
RECAPTCHA_PUBLIC_KEY=<recaptchaPublicKey>
RECAPTCHA_PRIVATE_KEY=<recaptchaPrivateKey>
EMAIL_HOST=<emalHost>
EMAIL_PORT=<emailPort>
EMAIL_USE_TLS=1
EMAIL_HOST_USER=<email>
EMAIL_HOST_PASSWORD=<emailPassword>
Локально .env.dev будет выглядеть так, где <>
необходимо заполнить свои данные.
Перейдем в файл конфигурации Django: settings.py и изменим параметры добавив в них переменные из виртуальной среды .env.dev. Сначала добавим импорт envrion в самом верху файла:
from pathlib import Path
from celery.schedules import crontab
import environ
# Работа с env.dev
env = environ.Env()
environ.Env.read_env(env_file=Path('./docker/env/.env.dev'))
Далее параметры, меняем следующим образом, и добавим ещё CSRF_TRUSTED_ORIGINS параметр для правильной работы
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = int(env('DEBUG', default=1))
ALLOWED_HOSTS = env('ALLOWED_HOSTS').split()
CSRF_TRUSTED_ORIGINS = env('CSRF_TRUSTED_ORIGINS').split()
# Другие параметры
# Cache and Celery settings
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': env('REDIS_LOCATION'),
}
}
# Celery settings
CELERY_BROKER_URL = env('CELERY_BROKER_URL')
CELERY_RESULT_BACKEND = env('CELERY_RESULT_BACKEND')
CELERY_TASK_TRACK_STARTED = True
CELERY_TASK_TIME_LIMIT = 30 * 60
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TASK_SERIALIZER = 'json'
CELERY_TIMEZONE = 'Europe/Moscow'
# Database
# https://docs.djangoproject.com/en/4.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': env('POSTGRES_DB'),
'USER': env('POSTGRES_USER'),
'PASSWORD': env('POSTGRES_PASSWORD'),
'HOST': env('POSTGRES_HOST'),
'PORT': env('POSTGRES_PORT'),
}
}
# Другие параметры
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = env('EMAIL_HOST')
EMAIL_PORT = env('EMAIL_PORT')
EMAIL_USE_TLS = int(env('EMAIL_USE_TLS', default=1))
EMAIL_HOST_USER = env('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = env('EMAIL_HOST_PASSWORD')
EMAIL_SERVER = EMAIL_HOST_USER
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
EMAIL_ADMIN = list(EMAIL_HOST_USER)
# Другие параметры
RECAPTCHA_PUBLIC_KEY = env('RECAPTCHA_PUBLIC_KEY')
RECAPTCHA_PRIVATE_KEY = env('RECAPTCHA_PRIVATE_KEY')
Таким образом мы заполнили параметры переменными из окружения, которые мы можем держать у себя на компьютере и публиковать проект на github без публичного обозрения наших ключей и паролей.
Создадим файл requirements.txt для получение зависимостей
Перед докеризацией нам необходимо получить все необходимые зависимости используемые в нашем проекте Django, для этого мы можем сгенерировать файл requirements.txt, делается это следующей командой: pip freeze > requirements.txt
Вот что получилось в сгенерированном файле на момент урока:
amqp==5.1.1
asgiref==3.6.0
async-timeout==4.0.2
billiard==3.6.4.0
celery==5.2.7
click==8.1.3
click-didyoumean==0.3.0
click-plugins==1.1.1
click-repl==0.2.0
colorama==0.4.6
Django==4.1.5
django-ckeditor-5==0.2.4
django-cleanup==7.0.0
django-environ==0.10.0
django-js-asset==2.0.0
django-mptt==0.14.0
django-recaptcha==3.0.0
django-taggit==3.1.0
django_debug_toolbar==3.8.1
kombu==5.2.4
lxml==4.9.2
Pillow==9.4.0
prompt-toolkit==3.0.38
psycopg2==2.9.5
pytils==0.4.1
pytz==2022.7.1
redis==4.5.1
six==1.16.0
sqlparse==0.4.3
tzdata==2022.7
vine==5.0.0
wcwidth==0.2.6
В будущем этот файл понадобится для сборки контейнера с Django.
Конфигурация nginx для Django
Создадим в папке docker/nginx папку dev, в которой создадим конфиг сервера nginx: django.conf со следующим содержимым:
upstream django {
server django:8000;
}
server {
listen 80;
listen [::]:80;
server_name _;
server_tokens off;
location / {
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Url-Scheme $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://django;
}
location /static/ {
alias /app/static/;
expires 15d;
}
location /media/ {
alias /app/media/;
expires 7d;
}
}
Это версия без SSL, работающая на 80 порту для локального сервера. На основе нее мы создадим в следующем уроке версию для продакшена.
В целом, данная конфигурация nginx настраивает обратный прокси-сервер для Django-приложения, обслуживает статические и медиа-файлы напрямую и настраивает заголовки для прокси-сервера, чтобы он мог корректно обрабатывать запросы, переданные ему nginx.
Создание Dockerfile
В корне нашего проекта создадим два файла: Dockerfile и docker-compose.dev.yml.
В файле Dockerfile мы определим окружение, в котором будет работать наш проект Django, а также установим необходимые модули.
Dockerfile будет выглядеть следующим образом:
FROM python:alpine
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1
# Устанавливаем обновления и необходимые модули
RUN apk update && apk add libpq
RUN apk add --virtual .build-deps gcc python3-dev musl-dev postgresql-dev
# Обновление pip python
RUN pip install --upgrade pip
# Установка пакетов для проекта
COPY requirements.txt ./requirements.txt
RUN pip install -r requirements.txt
WORKDIR /app
# Удаляем зависимости билда
RUN apk del .build-deps
# Копирование проекта
COPY . .
# Настройка записи и доступа
RUN chmod -R 777 ./
Данный код содержит инструкции для создания Docker-образа, который будет использоваться для запуска Django-приложения. Разберем по шагам:
FROM python:alpine
- указывает, что образ будет основан на образе Alpine Linux с предустановленной Python версии.
ENV PYTHONUNBUFFERED=1
и ENV PYTHONDONTWRITEBYTECODE=1
- задаются значения переменных окружения, чтобы Python не использовал буферизацию ввода-вывода и не создавал скомпилированные файлы для ускорения запуска.
RUN apk update && apk add libpq
- обновляет репозитории и устанавливает библиотеку libpq, необходимую для подключения к PostgreSQL.
RUN apk add --virtual .build-deps gcc python3-dev musl-dev postgresql-dev
- устанавливает зависимости, необходимые для компиляции некоторых Python-модулей и библиотек.
RUN pip install --upgrade pip
- обновляет pip до последней версии.
COPY requirements.txt ./requirements.txt
- копирует файл requirements.txt из локальной директории в директорию контейнера.
RUN pip install -r requirements.txt
- устанавливает зависимости Python-приложения из файла requirements.txt.
WORKDIR /app
- задает рабочую директорию контейнера.
COPY . .
- копирует содержимое локальной директории в директорию контейнера.
RUN chmod -R 777 ./
- устанавливает права доступа 777 для всех файлов и директорий внутри контейнера. Это может быть полезно для отладки и тестирования, но не рекомендуется для продакшн среды, так как может привести к уязвимостям безопасности.
Создание docker-compose.dev.yml
Далее нам необходимо создать файл docker-compose.dev.yml, создаем его также в корне и заполняем слудющим содержимым:
version: '3.8'
volumes:
pgdata:
static:
media:
services:
django:
build:
context: .
ports:
- '8000:8000'
container_name: django
env_file:
- docker/env/.env.dev
volumes:
- ./:/app
- static:/app/static
- media:/app/media
depends_on:
- postgres
- redis
command: sh -c "python manage.py collectstatic --no-input &&
python manage.py makemigrations &&
python manage.py migrate &&
python manage.py runserver 0.0.0.0:8000"
nginx:
container_name: nginx
working_dir: /app
image: nginx:stable-alpine
restart: always
ports:
- "80:80"
volumes:
- static:/app/static
- media:/app/media
- ./docker/nginx/dev/:/etc/nginx/conf.d:ro
links:
- django
depends_on:
- django
postgres:
image: postgres:alpine
container_name: postgres
restart: always
env_file:
- docker/env/.env.dev
volumes:
- pgdata:/var/lib/postgresql/data/
redis:
image: redis:alpine
container_name: redis
env_file:
- docker/env/.env.dev
expose:
- 6379
volumes:
- ./docker/redis/data:/data
celery-worker:
build: .
container_name: celery-worker
restart: always
env_file:
- docker/env/.env.dev
volumes:
- ./:/app
- media:/app/media
command: celery -A backend worker --loglevel=info --logfile=./docker/logs/celery-worker.log
depends_on:
- redis
celery-beat:
build: .
container_name: celery-beat
env_file:
- docker/env/.env.dev
depends_on:
- redis
command: celery -A backend beat --loglevel=info --logfile=./docker/logs/celery-beat.log
volumes:
- media:/app/media
- ./:/app
Код представляет собой описание Docker-композиции для развертывания веб-приложения на базе фреймворка Django.
Первый блок определяет именованные тома для хранения данных приложения, а именно pgdata
для хранения данных PostgreSQL, а также static
и media
для хранения статических файлов и медиа-контента.
Далее определены сервисы, используемые в композиции:
- django - контейнер, который использует Dockerfile в текущей директории для создания образа, на основе которого будет запущен контейнер. В качестве базового образа используется python:alpine. Запускается команда
collectstatic
для сбора статических файлов,makemigrations
иmigrate
для миграции базы данных, а затемrunserver
для запуска приложения на порту8000
. - nginx - контейнер с веб-сервером nginx, который служит для обработки входящих запросов и передачи их в контейнер с приложением Django. Настроен на прослушивание
80
порта, а также на использование статических файлов и медиа-контента из соответствующих именованных томов. - postgres - контейнер с СУБД PostgreSQL, используемый для хранения данных приложения. Запускается с базовым образом
postgres:alpine
и монтирует именованный томpgdata
для хранения данных. - redis - контейнер с базой данных Redis, используемый для кеширования данных и брокер для Celery. Монтирует папку для хранения данных Redis и использует env-файл для определения переменных окружения.
- celery-worker - контейнер, который использует Dockerfile в текущей директории для создания образа, на основе которого будет запущен контейнер. Команда
celery
запускаетworker
, который слушает очередь задач, используя Redis в качестве брокера. - celery-beat - контейнер, использующий тот же Dockerfile, что и celery-worker. Команда
celery
запускаетbeat
, который управляет периодическим выполнением задач, также используя Redis в качестве брокера.
Для каждого сервиса определены настройки, такие как контейнерное имя, используемый образ, порты, env-файлы, монтируемые тома и команды, которые будут выполнены при запуске контейнера. С помощью параметра depends_on
определены зависимости между сервисами, чтобы Docker-композиция могла правильно запускаться в правильном порядке.
Запуск Django проекта в докере
Так как это у нас локальная dev версия проекта, запущу я его из под Windows. Предварительно у меня уже установлен Docker на пк.
Запустим билд проекта в терминале следующей командой: docker compose -f docker-compose.dev.yml build
, создание образа займёт какое-то время. Обычно это 3-5 минут, зависит все от вашего процессора и памяти.
После создания образа с контейнерами билд необходимо запустить, делается это следующей командой: docker compose -f docker-compose.dev.yml up
.
Я использую флаг -f
, так как этот файл содержит имя docker-compose.dev.yml
и является dev версией, если бы имя файла было бы docker-compose.yml
, можно было бы просто запустить его через команду: docker compose build --up
.
Что получилось в самом Docker при билдинге Django
Проверка работы Django
Отлично. Давайте перейдём на наш сайт, который запущен в Docker. Мы можем перейти на адрес: localhost, либо 127.0.0.1, без использования порта.
Как создать учетную запись администратора из консоли
Сайт пустой, но для его заполнения необходимо создать учетную запись, для этого можно ввести следующую команду, чтобы попасть в терминал контейнера с django: docker exec -it django sh
, где django имя любого контейнера, в какой терминал вам необходимо зайти, и уже дальше мы создаем также как обычно суперпользователя: python manage.py createsuperuser
, заполняем необходимые данные.
Далее войдём в админку с учетными данными:
И давайте для теста добавим тестовую статью:
Попробуем пройти регистрацию и проверить работу Celery:
Celery в контейнере отработал:
[2023-04-05 14:34:01,120: INFO/MainProcess] celery@7c3b2a28b0f6 ready.[2023-04-05 14:44:08,645: INFO/MainProcess] Task modules.services.tasks.send_activate_email_message_task[7e5e42d1-3910-4e44-83ab-56f92f23bed5] received
[2023-04-05 14:44:11,334: INFO/ForkPoolWorker-15] Task modules.services.tasks.send_activate_email_message_task[7e5e42d1-3910-4e44-83ab-56f92f23bed5] succeeded in 2.685380100000202s: None
Таким образом мы запустили Django проект в Docker со всеми необходимыми инструментами. На основе этой версии в следующем уроке мы создадим продакшен версию, в которой рассмотрим добавление SSL, работу с .env.prod, другим конфигом nginx под SSL и Gunicorn.