Вопрос или проблема
Контекст
У меня есть одностраничное веб-приложение, использующее следующий стек:
- React для фронтенда
- Django для бэкенда
- Nginx для веб-сервера
Веб-приложение упаковано в контейнеры с помощью docker-compose
. Мое React приложение получает данные с сервера Django (Django построен как конечная точка API, используя Django Rest Framework).
Вопрос/Проблема
У меня возникла проблема при развертывании, когда я не могу обслуживать свои медиафайлы через Nginx.
Что я уже пробовал
Моя первая мысль была обслуживать медиафайлы, как показано в этом посте на stackoverflow посте – что довольно просто. Однако, поскольку Nginx работает в своем собственном контейнере (как и мой сервер Django), я не могу ссылаться на мои медиафайлы Django, так как они находятся в разных контейнерах.
В идеале я бы не хотел использовать webpack и хотел бы, чтобы Nginx занимался обслуживанием медиафайлов.
Если вы посмотрите на Dockerfile nginx ниже, вы увидите, что я копирую свои статические файлы в /usr/share/nginx/html/static/staticfiles
, чтобы затем обслуживать их с помощью nginx (см. location ^~ /static/
в nginx.conf
). Я попробовал сделать то же самое для моего медиафайла (как тест), и это работает – хотя все файлы, которые я загружаю, когда сайт работает, недоступны, так как копирование происходит, когда я строю свой контейнер.
Структура файлов
Корневая Директория
|__ docker-compose.yml
|__ backend
|__ root
|__ Project
|__ api
|__ models.py
|__ ...
|__ media
|__ teddycrepineau
|__ settings.py
|__ ...
|__ production
|__ Dockerfile
|__ nginx
|__ Dockerfile
|__ nginx.conf
|__ frontend
|__ ...
Соответствующий код
docker-compose.yml
version: '3'
volumes:
postgres_data: {}
postgres_backup: {}
services:
postgres:
image: postgres
volumes:
- postgres_data:/var/lib/postgresql/data
- postgres_backup:/backups
env_file: .env
nginx:
container_name: nginx
build:
context: .
dockerfile: ./nginx/Dockerfile
image: nginx
restart: always
depends_on:
- django
ports:
- "80:80"
django:
container_name: django
build:
context: backend
dockerfile: ./root/production/Dockerfile
hostname: django
ports:
- 8000:8000
volumes:
- ./backend:/app/
depends_on:
- postgres
command: >
bash -c '
python3 ./root/manage.py makemigrations &&
python3 ./root/manage.py migrate &&
python3 ./root/manage.py initadmin &&
gunicorn teddycrepineau.wsgi -b 0.0.0.0:8000 --chdir=./root/'
env_file: .env
nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
upstream app {
server django:8000;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name 0.0.0.0;
charset utf-80;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ @proxy_to_app;
}
location @proxy_to_app {
rewrite ^(.+)$ /index.html last;
}
location ^~ /static/ {
autoindex on;
alias /usr/share/nginx/html/static/;
}
location ~ ^/api {
proxy_pass http://django:8000;
}
location ~ ^/admin {
proxy_pass http://django:8000;
}
}
}
Dockerfile nginx
FROM nginx:latest
ADD ./nginx/nginx.conf /etc/nginx/nginx.conf
COPY ./frontend/build /usr/share/nginx/html
COPY ./backend/root/staticfiles /usr/share/nginx/html/static/staticfiles
Dockerfile Django
FROM python:3.7
ENV PYTHONUNBUFFERED 1
RUN export DEBIAN_FRONTEND=noninteractive
RUN mkdir /app
RUN pip install --upgrade pip
ADD /root/requirements.txt /app/
WORKDIR /app/
ADD . /app/
RUN pip install -r requirements.txt
EXPOSE 8000
settings.py Django
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
APPS_DIR = os.path.join(BASE_DIR, 'project')
....
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(APPS_DIR, 'media/')
urls.py Django
urlpatterns = [
path('admin/', admin.site.urls),
re_path(r'^api/', include('project.api.urls')),
path('summernote/', include('django_summernote.urls')),
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Обновление
Когда я монтирую общий том и ссылаюсь на него в своем nginx.conf
, я получаю 404 page not found
, когда пытаюсь получить доступ к изображению, загруженному на бэкенд Django.
docker-compose.yml
version: '3'
volumes:
postgres_data: {}
postgres_backup: {}
services:
postgres:
image: postgres
volumes:
- postgres_data:/var/lib/postgresql/data
- postgres_backup:/backups
env_file: .env
nginx:
container_name: nginx
build:
context: .
dockerfile: ./nginx/Dockerfile
image: nginx
restart: always
depends_on:
- django
ports:
- "80:80"
volumes:
- ./static:/app/backend/root/staticfiles
- ./media:/app/backend/root/project/media
django:
container_name: django
build:
context: backend
dockerfile: ./root/production/Dockerfile
hostname: django
ports:
- 8000:8000
volumes:
- ./backend:/app/
- ./static:/app/backend/root/staticfiles
- ./media:/app/backend/root/project/media
depends_on:
- postgres
command: >
bash -c '
python3 ./root/manage.py collectstatic --no-input &&
python3 ./root/manage.py makemigrations &&
python3 ./root/manage.py migrate &&
python3 ./root/manage.py initadmin &&
gunicorn teddycrepineau.wsgi -b 0.0.0.0:8000 --chdir=./root/'
env_file: .env
nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
upstream app {
server django:8000;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name localhost;
charset utf-80;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ @proxy_to_app;
}
location @proxy_to_app {
rewrite ^(.+)$ /index.html last;
}
location ^~ /static/ {
autoindex on;
alias /usr/share/nginx/html/static/;
}
location ^~ /media/ {
autoindex on;
alias /app/backend/root/project/media/;
}
location ~ ^/api {
proxy_pass http://django:8000;
}
location ~ ^/admin {
proxy_pass http://django:8000;
}
}
}
Если эта проблема возникает только из Django, вы можете попробовать изменить формат статических файлов –
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]
обычно возникают некоторые проблемы с статическими файлами, когда мы работаем в продакшене, так что это может помочь!
этот документ может помочь!
Ответ или решение
Как обслуживать медиафайлы Django через Nginx в структуре Django/React/Nginx/Docker-Compose
Введение
Ваша веб-приложение на стеке Django и React, запущенное с использованием Docker и docker-compose, сталкивается с проблемой обслуживания медиафайлов через Nginx. При этом, поскольку Nginx и сервер Django работают в разных контейнерах, необходимо правильно настроить взаимодействие между контейнерами, чтобы гарантировать доступность медиафайлов для frontend-приложения.
Архитектура проекта
Ваше приложение имеет следующую структуру директорий:
Root Dir
|__ docker-compose.yml
|__ backend
|__ root
|__ Project
|__ api
|__ media
|__ teddycrepineau
|__ settings.py
|__ production
|__ Dockerfile
|__ nginx
|__ Dockerfile
|__ nginx.conf
|__ frontend
|__ ...
Параметры для обслуживания медиафайлов
Ваша конфигурация Nginx и docker-compose требует некоторых корректировок для успешного обслуживания медиафайлов. Начнем с необходимых изменений.
- Настройка volumes в docker-compose
Убедитесь, что вы создаете обмен объемами между контейнером Django и Nginx. Это позволит Nginx получить доступ к медиафайлам, которые загружает Django. Обновите ваш docker-compose.yml
следующим образом:
version: '3'
volumes:
postgres_data: {}
postgres_backup: {}
media_volume: # Добавьте это
static_volume: # Добавьте это
services:
...
nginx:
...
volumes:
- ./media:/app/backend/root/project/media
- ./static:/app/backend/root/staticfiles
django:
...
volumes:
- ./media:/app/backend/root/project/media
- ./static:/app/backend/root/staticfiles
- Конфигурация Nginx для медиафайлов
В вашем файле nginx.conf
необходимо настроить правильный путь для медиафайлов:
...
location ^~ /media/ {
autoindex on;
alias /app/backend/root/project/media/;
}
...
Убедитесь, что путь в alias
указывает на директорию, где Django будет сохранять загруженные медиафайлы.
- Настройки Django
Убедитесь, что в вашем settings.py
определены правильные настройки для медиафайлов:
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
Эта настройка гарантирует, что все загруженные медиафайлы сохраняются в правильной директории и доступны через предварительно определённый URL.
- Проверка и отладка
После изменения конфигурации убедитесь, что вы:
- Перезапустили все контейнеры для применения новых настроек:
docker-compose down
docker-compose up -d --build
- Проверили логи Nginx и Django на наличие ошибок, связанных с обслуживанием файлов:
docker logs <nginx_container_name> docker logs <django_container_name>
Если, несмотря на эти изменения, по-прежнему возникают ошибки 404, проверьте, правильно ли настроены разрешения для доступа к директориям.
Заключение
Следуя описанным шагам, вы сможете успешно настроить обслуживание медиафайлов Django через Nginx в вашей архитектуре Docker с использованием React и Django. Корректная настройка volumes и конфигурации Nginx является ключом к успешной интеграции всех компонентов системы. Если трудности сохраняются, не стесняйтесь дополнительно проверять логи или обращаться к документации для устранения проблем.