Обслуживание статических файлов с помощью Nginx и Django в Docker

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

Вот мои две статические переменные в моем settings.py:

STATIC_URL = '/static/'
STATIC_ROOT='/opt/django/portfolio/collectstatic'

Вот мой dockerfile для сборки образа проекта:

FROM python:3.11-slim

WORKDIR opt/django/
COPY pyproject.toml .
RUN python -m pip install .

COPY ./portfolio/ ./portfolio/

WORKDIR portfolio/

RUN python manage.py collectstatic --noinput
RUN python manage.py makemigrations
RUN python manage.py migrate

EXPOSE 8000

CMD ["gunicorn", "portfolio.wsgi:application", "--bind", "0.0.0.0:8000"]

Вот docker-compose.yml:

services:
  web:
    build:
      context: .
      dockerfile: ./docker/Dockerfile_django
    container_name: webserver
    volumes:
      - static_data:/opt/django/portfolio/collectstatic
    expose:
      - "8000"
    ports:
      - "8000:8000"
    depends_on:
      - db
    networks:
      - docker_network

  db:
    image: postgres:15
    container_name: db_postgres
    expose:
      - "5432"
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: postgres
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    networks:
      - docker_network

  nginx:
    image: nginx:latest
    container_name: nginx
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - static_data:/opt/django/portfolio/collectstatic
    depends_on:
      - web
    networks:
      - docker_network

volumes:
  postgres_data:
  static_data:

networks:
  docker_network:
    driver: bridge
    name: docker_network

И, наконец, вот мой nginx.conf:

events {}

http {
    server {
        listen 80;
        server_name localhost;

        location /static {
            alias /opt/django/portfolio/collectstatic;
            autoindex on;
        }

        # skip favicon.ico
        location /favicon.ico {
            access_log off;
            return 204;
        }

        location / {
            proxy_pass http://web:8000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

Я не вижу ошибок в логах, и nginx сообщает о 200, когда я делаю GET к url в проекте. Когда я посещаю http://127.0.0.1/static/website/style.css, я вижу css (и js файлы). Я очистил свой кэш. Когда я перехожу по адресу /opt/django/portfolio/collectstatic в моем веб-контейнере, я вижу статические файлы. Структура моего проекта выглядит следующим образом:

 - my_project/
   - nginx/
     - nginx.conf
   - my_project/
     - my_project/
       - settings.py
       ...
     - web/
       - js/
         - some_js.js
         - some_other_js.js
       - website/
         - style.css
       admin.py
       apps.py
       forms.py
       models.py
       tests.py
       ...

Что я упускаю? Буду признателен за любые рекомендации / отзывы о возможных неправильных конфигурациях, которые я, возможно, упускаю из виду.

Скорее всего, причина в том, что процесс nginx не имеет необходимых прав доступа к папке /opt/django/portfolio/collectstatic.

Сначала добавьте несколько отладочных операторов в ваш nginx conf, например, так:

location /static {
    alias /opt/django/portfolio/collectstatic;
    autoindex on;
    access_log /var/log/nginx/static_access.log;
    error_log /var/log/nginx/static_error.log debug;
}

Очевидно, что затем нужно проверить журналы...

В качестве первой попытки вы можете запустить свой проект, затем войти в веб-контейнер и выполнить chmod -R 755 /opt/django/portfolio/collectstatic. Это должно передать правильные права на папку. Проверьте, помогает ли это, если нет, снова проверьте журналы ошибок.

Это удобная команда для постоянного мониторинга журналов ошибок:
docker-compose exec nginx tail -f /var/log/nginx/static_error.log

Не связанное с вашим вопросом замечание:
Очень необычно выполнять команду makemigrations в режиме производства. Теоретически ваша dev-DB имеет ту же структуру, что и prod-DB. Тогда вы вносите изменения во время разработки. Здесь, на dev-DB вы запускаете makemigrations, применяете их с помощью migrate и тестируете. Когда все работает, вы фиксируете файлы миграции в вашем git-репо. Затем для prod-DB вы больше не запускаете makemigrations. Для prod-DB вы применяете только миграции, полученные из вашей разработки/вашего git-репо.
TL/DR: Разработайте миграции на dev-DB с помощью makemigrations и migrate. Применяйте миграции к prod-DB только с помощью migrate и не используйте makemigrations снова.

Я смог решить эту проблему, добавив include mime.types; в блок location /static:

location /static {
    alias /opt/django/portfolio/static;
    autoindex on;
    include mime.types;
}
Вернуться на верх