Django + uWSGI + Docker пример настройки
Дата выхода: 1 февраля 2024 г.
Редактировался: 13 апреля 2024 г.
Интернет полнится статьями по настройке Django с Gunicorn, но что если вам нужно больше функциональности от WSGI сервиса, тогда вам больше подойдёт uWSGI возможности которого гораздо шире.
Представим, что у нас уже есть проект (ссылка на подобный проект) простое django приложение отображающее страницу с какими-то данными.
Структура проекта:
Папки:
- app - папка с django проектом, а так же с статическими и медиа файлами;
- data - хранит базу данных postgres;
- nginx - папка с конфигом nginx-а;
Запапускается проект с помощью docker-compose, yaml файл которого имеет следующий вид:
version: '3.9'
services:
db:
container_name: Database
image: postgres
volumes:
- ./data:/var/lib/pgsql/data
environment:
- POSTGRES_DB=post
- POSTGRES_USER=test
- POSTGRES_PASSWORD=verystrongpasswd
- PGDATA=/var/lib/pgsql/data/pgdata
app:
container_name: Web-app
build: ./app
command: bash -c "python manage.py makemigrations &&
python manage.py migrate && python manage.py collectstatic --noinput &&
gunicorn mysite.wsgi:application --bind 0.0.0.0:8001"
volumes:
- ./app:/app
environment:
- POSTGRES_DB=post
- POSTGRES_USER=test
- POSTGRES_PASSWORD=verystrongpasswd
expose:
- "8001"
depends_on:
- db
nginx:
container_name: NGINX
build: ./nginx
volumes:
- ./app/static:/mysite/static
- ./app/media:/mysite/media
ports:
- "8001:80"
depends_on:
- db
- app
Как видно из yaml файла у нас есть контейнер с базой данных, nginx-ом и приложением django, которое работает через gunicorn, от последнего мы сейчас и будем избавляться.
Для начала создадим в папке django приложения (app) файл конфигурации uWSGI (uwsgi.ini) чтобы не писать параметры в консоли, выглядеть он будет сделующим образом:
[uwsgi]
# Путь к django проекту
chdir = /app
# Путь к wsgi django
module = mysite.wsgi:application
master = true
# Количество процессов
processes = 4
# Путь к сокету
socket = /socket/pntlv.sock
# Права доступа к файлу сокета в ОС
chmod-socket = 664
# UID пользователя (web) от имени которого будет запущен процесс
uid = 1000
# GUID группы (web)
gid = 1000
# Очистка всех сгенерированных файлов/сокетов по завершению работы uWSGI
vacuum = true
enable-threads = true
thunder-lock = true
single-interpreter = true
need-app = true
die-on-term = true
disable-logging = true
py-call-osafterfork = true
# Принцип жизни воркеров
max-requests = 1000 # Перезапуск workers после указанного количества запросов
max-worker-lifetime = 3600 # Перезапуск workers после указанного времени в сек
reload-on-rss = 1024 # Перезапуск workers после потребления указанного количества памяти
worker-reload-mercy = 60 # Время ожидания перед уничтожение процессов workers
Большей части параметров я дал краткое описание, но если вам этого недостаточно то воспользуйтесь официальной документацией.
Приложение gunicorn работало с nginx через порт 8001, с uWSGI мы поступим по другому, будем использовать socket, для этого создадим в корне проекта папку socket, её мы будем пробрасывать в контейнеры с помощью volumes, внесём соответствующие изменения в docker-compose.yaml:
version: '3.9'
services:
db:
container_name: Database
image: postgres:alpine
volumes:
- ./data:/var/lib/pgsql/data
environment:
- POSTGRES_DB=post
- POSTGRES_USER=test
- POSTGRES_PASSWORD=verystrongpasswd
- PGDATA=/var/lib/pgsql/data/pgdata
app:
container_name: Web-app
build: ./app
# 0 Заменяем gunicorn на uWSGI
command: bash -c "python manage.py makemigrations &&
python manage.py migrate && python manage.py collectstatic --noinput &&
uwsgi --ini uwsgi.ini"
volumes:
- ./app:/app
# 1 Прокидываем (расшариваем) папку socket
- ./socket:/socket
environment:
- POSTGRES_DB=post
- POSTGRES_USER=test
- POSTGRES_PASSWORD=verystrongpasswd
expose:
- "8001"
depends_on:
- db
nginx:
container_name: NGINX
build: ./nginx
volumes:
- ./app/static:/mysite/static
- ./app/media:/mysite/media
# 2 Прокидываем (расшариваем) папку socket
- ./socket:/socket
ports:
- "8001:80"
depends_on:
- db
- app
Не забываем что uWSGI должен быть установлен в контейнере django приложения, для этого добавим его в файл requirements.txt (и удалим от туда gunicorn).
Теперь необходимо изменить Dockerfile нашего приложения следующим образом:
FROM python:slim
# Определяем переменные среды
# Чтобы python не создавал файлы .pyc
ENV PYTHONDONTWRITEBYTECODE=1
# Чтобы видеть выходные данные приложения в реальном времени
ENV PYTHONUNBUFFERED=1
# Устанавливаем рабочую директорию
WORKDIR /app
# Копируем в рабочую директорию файл зависимостей
COPY requirements.txt /app/
# Установливаем gcc необходимый для сборки uWSGI
RUN apt-get update && apt-get install gcc -y
# Обновляем pip устанавливаем зависимости
RUN pip install --upgrade pip
# Устанавливаем все зависимости без использования кеша
RUN pip install --no-cache-dir -r requirements.txt
# Копируем содержимое локальной директории web в рабочую директорию
COPY . /app/
# Создаём группу и пользователя UID и GUID которых мы указали в uwsgi.ini
RUN addgroup --gid 1000 web && adduser -ingroup web --uid 1000 web
# Используем только что созданого пользователя
USER web
Далее нам остаётся только изменить файл конфигурации nginx-а:
upstream mysite {
# Старый способ работы через порты
# server app:8001;
# Новый способ работы через сокет
server unix:///socket/mysite.sock;
}
server {
# Прослушивается 80 порт
listen 80;
# Перенаправляем все запросы в django приложение
location / {
# Старый способ работы через порты
# proxy_pass http://mysite;
# Новый способ работы через сокет
uwsgi_pass mysite;
include uwsgi_params;
# Устанавливаем заголовки
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
# Отключаем перенаправление
proxy_redirect off;
}
# подключаем статические файлы
location /static/ {
alias /app/static/;
}
# подключаем медиа файлы
location /media/ {
alias /app/media/;
}
}
Готово, теперь после пересборки образа нашего django приложения (docker-compose up --build) оно будет работать через uWSGI.
Весь код используемый в статье можно найти по ссылке.