Обслуживание медиафайлов Django через Nginx (Django/React/Nginx/Docker-Compose)

Вопрос или проблема

Контекст

У меня есть одностраничное веб-приложение, использующее следующий стек:

  • 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 требует некоторых корректировок для успешного обслуживания медиафайлов. Начнем с необходимых изменений.

  1. Настройка 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
  1. Конфигурация Nginx для медиафайлов

В вашем файле nginx.conf необходимо настроить правильный путь для медиафайлов:

...
location ^~ /media/ {
    autoindex on;
    alias /app/backend/root/project/media/;
}
...

Убедитесь, что путь в alias указывает на директорию, где Django будет сохранять загруженные медиафайлы.

  1. Настройки Django

Убедитесь, что в вашем settings.py определены правильные настройки для медиафайлов:

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

Эта настройка гарантирует, что все загруженные медиафайлы сохраняются в правильной директории и доступны через предварительно определённый URL.

  1. Проверка и отладка

После изменения конфигурации убедитесь, что вы:

  • Перезапустили все контейнеры для применения новых настроек:
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 является ключом к успешной интеграции всех компонентов системы. Если трудности сохраняются, не стесняйтесь дополнительно проверять логи или обращаться к документации для устранения проблем.

Оцените материал
Добавить комментарий

Капча загружается...