Pandora ResearchPandora
Research
RUEN
DevOps

Dockerfile для проекта на Django

Pandora ResearchPandora Research
11 июля 20266 мин чтения

В данной статье рассмотрим пример создания Docker-файла для проекта на Django.

Базовый образ и зависимости

Начнём с выбора образа. Если нет ограничений по объёму жёсткого диска, я предпочитаю использовать образ slim-buster. Если ограничения есть, лучше взять alpine, но в этом случае, возможно, потребуется установка дополнительных пакетов в процессе сборки контейнера. Итак, выбираем нужную версию Python и добавляем образ в Dockerfile:

Dockerfile
FROM python:3.10.7-slim-buster

Создаём рабочий каталог для приложения и делаем его активным:

Dockerfile
WORKDIR /app

Затем приступаем к установке Python-библиотек и зависимостей. Я использую для этих целей pip, а зависимости храню в файле requirements.txt. Копируем файл в контейнер и устанавливаем зависимости:

Dockerfile
COPY ./requirements.txt requirements.txt
RUN pip3 install -r requirements.txt --no-cache-dir --no-warn-script-location

Затем копируем остальные файлы проекта:

Dockerfile
COPY . .

Раздача статики через nginx

Раздача статических файлов. Если статические и медиафайлы хранятся вместе с Django, необходимо настроить их раздачу. При запуске development-сервера Django командой python manage.py runserver раздача осуществляется автоматически, но в продакшене он обычно не используется. Рассмотрим вариант с раздачей статики через nginx.

Добавим nginx в контейнер, произведём настройку и соберём статику:

Dockerfile
RUN apt-get update
RUN apt-get -y install nginx
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf
COPY ./nginx/site.conf /etc/nginx/conf.d/site.conf
RUN python manage.py collectstatic --noinput

Настройки nginx в файле site.conf следующие:

site.conf
server {
    listen       80 default_server;
    listen       [::]:80 default_server;
    server_name  _;

    root /data;
    location @backend {
        proxy_pass http://localhost:8000;
    }

    location / {
        try_files $uri $uri/ @backend;
    }
}

Настройки в файле nginx.conf следующие:

nginx.conf
worker_processes  1;

events {
  worker_connections 1024;
}

http {
  include mime.types;

  log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

  sendfile        on;
  tcp_nodelay     on;
  tcp_nopush      on;

  client_max_body_size 200m;

  gzip on;
  gzip_comp_level 3;
  gzip_min_length 1000;
  gzip_types
        text/plain
        text/css
        application/json
        application/x-javascript
        text/xml
        text/javascript;

  proxy_redirect     off;
  proxy_set_header   Host             $host;

  include conf.d/*.conf;

  server_tokens off;
}

Здесь мы указываем, что все запросы на 80-й порт проксируются на http://localhost:8000. Также проверяется наличие файлов по URL http://localhost:8000. Если файл есть, он отдаётся nginx'ом без дальнейшего запроса к бэкенду.

Данную операцию производить необязательно, если файлы хранятся в S3-хранилище.

Одна из общих рекомендаций при использовании Docker-контейнеров — запускать команду контейнера под пользователем, отличным от root. Но поскольку нам нужно запустить nginx под root, придётся оставить root.

Порт и скрипт запуска

Укажем контейнеру, какой порт должен быть открыт наружу, и добавим скрипт запуска.

Dockerfile
EXPOSE 80

CMD ["./entrypoint.sh"]

Запуск производим отдельным скриптом, чтобы запустить nginx, выполнить миграции, а также запустить Django-сервер под gunicorn. Состав скрипта следующий:

entrypoint.sh
#!/bin/bash
echo "Starting service..."
/usr/sbin/nginx
python manage.py migrate
gunicorn --bind=0.0.0.0:8000 pandora_research_backend.wsgi

Сборка и запуск

На этом всё. Пробуем собрать образ командой:

Terminal
docker build -t django-img-demo .

И затем стартуем его:

Terminal
docker run -p 80:80 --name django-demo django-img-demo

Посмотреть результат можно, введя в браузере путь http://localhost:80 или http://localhost.

Недостатки и решения

В результате мы получили контейнер, в котором запущен веб-сервер на Django, и доступ к нему можно получить по стандартному 80-му порту. Несмотря на простоту, этот вариант имеет ряд недостатков, из-за которых в продакшене его всё же лучше не использовать. Недостатки и возможные решения перечислены ниже.

Запуск контейнера под root. Для решения этой проблемы можно развести Django-сервер и nginx по разным контейнерам, а затем соркестрировать их через docker-compose. Для обмена статикой можно примонтировать общий каталог на сервере к каждому из контейнеров и использовать его для передачи статики между Django и nginx.

Можно подключить S3-хранилище и использовать его для раздачи статики. Существует немало библиотек для Django, которые позволяют это сделать. В этом случае потребность в раздаче статики отпадёт, и веб-сервер Django уже можно будет запустить под специально созданным пользователем. Код для добавления пользователя следующий:

Dockerfile
RUN groupadd -r api_group
RUN useradd -d /app -r -g api_group api_user
RUN chown api_user:api_group -R /app
USER api_user
DockerDjangoPython

Есть задача для нашей команды?

Расскажите о проекте — оценим и предложим решение в течение одного рабочего дня.

Обсудить проект