Как мы разворачивали React + Django приложение: пошаговое руководство
В предыдущей статье мы настраивали окружение разработчика для создания приложений с использованием React и Django. В этой статье мы поделимся нашим опытом развертывания Django приложения с помощью Docker контейнеров, описывая ключевые шаги и лучшие практики, чтобы получить продакшн окружение.
В последние годы использование асинхронных приложений на базе Django (ASGI) стало популярным благодаря их высокой производительности и возможности обработки большого количества соединений. Мы будем разворачивать Django ASGI приложение с использованием Nginx и SSL сертификатом от Let’s Encrypt.
Настройка Nginx
Развернем приложение из предыдущей статьи.
Настроим конфигурацию Nginx для вашего приложения. Создайте файл your_domain.conf
в каталоге
frontend/nginx
:
server {
listen 80;
listen [::]:80;
gzip on;
gzip_types text/plain text/css text/js text/xml text/javascript application/javascript application/json
application/xml application/rss+xml image/svg+xml;
server_name your_domain.com;
# Указываем расположение вашего React проекта в этом контейнере. Оно задается в Dockerfile
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# Проксируем все обращения к API сервера в другой контейнер с Django
# Имя контейнера backend задается в docker-compose.yml файле
location /api/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
# Этот каталог нужен для получения сертификата от Let's Encrypt
location ~ /.well-known/acme-challenge {
allow all;
root /var/cert;
}
}
Создание контейнера с React приложением и Nginx
Для развертывания React приложения можно собрать ваш проект командой npm run build
. В результате этого получим каталог
build
, который можно подключить к Nginx контейнеру. Либо сделать новый образ Nginx и включить React приложение в него.
Для этого в каталоге с frontend
приложением создаем следующий Dockerfile
файл:
# Этап сборки
FROM node:current AS build
# Устанавливаем рабочую директорию
WORKDIR /app
# Копируем все файлы приложения
COPY . .
# Устанавливаем зависимости
RUN npm install
# Создаем сборку приложения
RUN npm run build
# Этап развертывания
FROM nginx
# Копируем конфигурацию nginx
COPY ./nginx/* /etc/nginx/conf.d
# Копируем файлы сборки из предыдущего этапа
# Если используете сборщик пакетов Vite, то он создает прилежение в каталоге dist
#COPY --from=build /app/dist /usr/share/nginx/html
COPY --from=build /app/build /usr/share/nginx/html
Создание контейнера с нашим Django приложением
В качестве сервера приложений будем использовать Daphne. Его нет requirements.txt
, поэтому ставим отдельно.
В корне проекта backend
сервера создаем Dockerfile
следующего содержания:
# Используем последний официальный образ Python в качестве базового
FROM python:3
# Устанавливаем рабочую директорию
WORKDIR /app
# Копируем все файлы проекта в контейнер
COPY . .
# Устанавливаем зависимости
RUN pip install --no-cache-dir -r requirements.txt daphne
# Выполняем миграции и собираем статические файлы
RUN python manage.py migrate
RUN python manage.py collectstatic --noinput
# Указываем команду для запуска приложения
CMD ["daphne", "-b", "0.0.0.0", "-p", "8000", "backend.asgi:application"]
Настройка запуска нескольких контейнеров через Docker Compose
В корне нашего проекта создаем файл docker-compose.yml
со следующим содержанием:
services:
web:
build: ./frontend
ports:
- "80:80"
depends_on:
- backend
backend:
build: ./backend
На этом этапе уже можно запустить наши контейнеры и проверить их работу.
docker compose up -d
Если сейчас перейти по вашему доменному имени, должно открыться наше приложение.
Настройка Let’s Encrypt
Для этого запустим отдельный контейнер, который периодически будет проверять сертификаты и запрашивать новые. Для этого
у него должно быть общее хранилище вместе с контейнером nginx. Точнее таких хранилищ должно быть два: для хранения
сертификатов и для прохождения проверки для выдачи сертификата. Приведем наш docker-compose.yml
файл к следующему виду:
services:
web:
build: ./frontend
volumes:
- certbot-etc:/etc/letsencrypt
- certbot-var:/var/cert
ports:
- "80:80"
- "443:443"
command: "/bin/sh -c 'while :; do sleep 12h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
backend:
build: ./backend
volumes:
- .:/app
certbot:
image: certbot/certbot
volumes:
- certbot-etc:/etc/letsencrypt
- certbot-var:/var/cert
depends_on:
- web
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
volumes:
certbot-etc:
certbot-var:
Перезапускаем контейнеры и пробуем получить новый сертификат. Не забываем подставить свое доменное имя и почтовый ящик.
Также, для тестирования, запускайте с опций --staging
, т.е. у Let’s Encrypt очень маленькие лимиты на попытки
и в случае неудач, придется по часу ожидать. А поле того как все начало работать без ошибок, просто уберите эту опцию
или поменяйте на --force
чтобы обновить еще не истекший сертификат.
docker compose up -d
docker compose run --entrypoint "certbot certonly --webroot --webroot-path=/var/cert \
--email you@email --agree-tos --no-eff-email --staging -d your_domain.com \
-d www.your_domain.com" certbot
Теперь нам нужно перенастроить Nginx на работу с полученным сертификатом.
Приведем файл your_domain.conf
в каталоге frontend/nginx
к такому виду:
# Делаем переадресацию на https
server {
listen 80;
listen [::]:80;
access_log off;
server_name your_domain.com www.your_domain.com;
return 301 https://your_domain.com$request_uri;
}
# Делаем переадресацию с поддомена www на без него. Либо наоборот, в зависимости от ваших предпочтений.
server {
listen 443 ssl;
listen [::]:443 ssl;
access_log off;
server_name www.your_domain.com;
ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
include conf.d/common_params;
return 301 https://your_domain.com$request_uri;
}
# Основной блок обработки запросов
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name your_domain.com;
ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
include conf.d/common_params;
# Указываем расположение вашего React проекта в этом контейнере. Оно задается в Dockerfile
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# Проксируем все обращения к API сервера в другой контейнер с Django
# Имя контейнера backend задается в docker-compose.yml файле
location /api/ {
proxy_pass http://backend:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
# Этот каталог нужен для получения сертификата от Let's Encrypt
location ~ /.well-known/acme-challenge {
allow all;
root /var/cert;
}
}
Так же добавим в этот каталог файл common_params
с общими параметрами для большинства сайтов
gzip on;
gzip_types text/plain text/css text/js text/xml text/javascript application/javascript application/json
application/xml application/rss+xml image/svg+xml;
server_tokens off;
ssl_buffer_size 4k;
ssl_session_cache shared:TLS:2m;
# Сгенерировать случайный dhparam файл можно с помощью следующей команды:
# openssl dhparam 4096 -out /etc/nginx/conf.d/dhparam.pem
ssl_dhparam /etc/nginx/conf.d/dhparam.pem;
ssl_protocols TLSv1.3 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ecdh_curve secp521r1:secp384r1;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001]; # Cloudflare
# Set HSTS to 365 days
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload' always;
Пересобираем наш контейнер docker compose build web
и запускам docker compose up -d
. Если все сделано правильно,
то мы увидим работающий сервер через https соединение с автоматическим обновлением сертификатов.
Похожее
Если вас заинтересовала данная тема, пожалуйста свяжитесь с нами.