通过 Nginx (Django/React/Nginx/Docker-Compose) 提供 Django 媒体文件
Posted
技术标签:
【中文标题】通过 Nginx (Django/React/Nginx/Docker-Compose) 提供 Django 媒体文件【英文标题】:Serve Django Media Files via Nginx (Django/React/Nginx/Docker-Compose) 【发布时间】:2019-07-21 12:09:27 【问题描述】:上下文
我有一个使用以下堆栈的单页网络应用程序:
为前端做出反应 Django 用于后端 用于网络服务器的 nginxWeb 应用程序使用 docker-compose
进行 docker 化。我的 React 应用程序,从 Django 服务器获取数据(django 是使用 Django Rest Framework 构建为 api 端点)。
问题/问题
我在部署时遇到问题,无法通过 Nginx 提供媒体文件。
到目前为止我尝试了什么
我最初的想法是按照 *** post 所示提供媒体文件 - 这非常简单。不过,由于 Nginx 在它自己的 docker 中运行(我的 django 服务器也是如此),我无法指向我的 django 媒体文件,因为它们位于不同的容器中。
理想情况下,我不想使用 webpack,而是让 Nginx 负责提供媒体文件。
如果您查看下面的 nginx Dockerfile,您会看到我正在将我的静态文件复制到 /usr/share/nginx/html/static/staticfiles
,然后使用 nginx 为它们提供服务(请参阅 nginx.conf
中的 location ^~ /static/
)。我尝试对我的媒体文件(作为测试)做同样的事情并且它有效 - 但是,一旦网站启动,我上传的所有文件都无法访问,因为副本发生在我构建我的容器时。
文件结构
Root Dirc
|__ 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;
nginx Dockerfile
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
Django Dockerfile
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
Django settings.py
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/')
Django urls.py
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
中引用它时,当我尝试访问在 django 后端上传的图像时,我得到一个 404 page not found
。
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;
【问题讨论】:
您可以做的是创建一个共享卷,该卷将包含所有静态资产,并且您可以将其挂载到nginx
和django
容器。在启动django
容器时运行manage.py collectstatic
以复制所有静态资产。
@IgorNikolaev 如果我这样做,我该如何在我的 nginx.conf 文件中指向媒体文件夹。据我了解,一旦我的卷被挂载,它将存在于该挂载中(例如,在我的情况下,我将 Django 应用程序卷挂载在 ./backend 中的主机上)
@IgorNikolaev,这样做允许我访问 /media/ 文件,但是当我从服务器上传图像时,它不会推送到 /media/ 并且我无法可视化此图像.
【参考方案1】:
问题来自我在docker-compose.yml
中安装卷的方式(这是我的错误理解)。
首先,我们创建一个主机挂载卷 (./backend/
),引用我们的 django 服务中存在的 /app/
文件夹。我们创建了这个文件夹,并将所有相关文件添加到位于后端文件夹的Dockerfile
中。这基本上会将我们存在于django
Docker 映像上的/app/
文件夹链接到存在于主机上的./backend
文件夹 - 请参阅 OP 中的文件结构。
一旦我们有了这个卷,每当对我们的/app/
文件夹进行更新(即上传新图像)时,它都会反映在我们主机安装的卷中(即./backend/
) - 反之亦然。
我们最终创建了另外 2 组主机挂载卷(./backend/root/staticfiles/...
和 ./backend/root/project/media/
),我们将使用它们通过 Nginx 为我们的媒体和静态文件提供服务。我们在nginx
和django
服务之间共享这些主机安装卷。从版本 2 开始,docker-compose
会自动在您的 Docker 镜像之间创建一个网络,允许您在服务之间共享卷。
最后,在我们的 nginx.conf 中,我们在 docker-compose.yml
文件中为 static
和 media
url 引用主机安装卷。
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
django:
container_name: django
build:
context: backend
dockerfile: ./root/production/Dockerfile
hostname: django
ports:
- 8000:8000
volumes:
- ./backend:/app/
- ./backend/root/staticfiles/admin:/usr/share/nginx/html/static/admin
- ./backend/root/staticfiles/rest_framework:/usr/share/nginx/html/static/rest_framework
- ./backend/root/staticfiles/summernote:/usr/share/nginx/html/static/summernote
- ./backend/root/project/media/:/usr/share/nginx/html/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:
container_name: nginx
build:
context: .
dockerfile: ./nginx/Dockerfile
image: nginx
restart: always
depends_on:
- django
ports:
- "80:80"
volumes:
- ./backend/root/staticfiles/admin:/usr/share/nginx/html/static/admin
- ./backend/root/staticfiles/rest_framework:/usr/share/nginx/html/static/rest_framework
- ./backend/root/staticfiles/summernote:/usr/share/nginx/html/static/summernote
- ./backend/root/project/media/:/usr/share/nginx/html/media/
nginx.conf
....
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 /usr/share/nginx/html/media/;
location ~ ^/api
proxy_pass http://django:8000;
location ~ ^/admin
proxy_pass http://django:8000;
【讨论】:
我只想补充一点,您可以使用共享卷而不是挂载本地目录。音量可以在***volumes
键下的 docker-compose
内定义:docs.docker.com/compose/compose-file/…。之后,您可以以类似的方式将卷挂载到多个容器。
啊,没有注意到您已经将它用于postgres
数据。所以你可以在那里添加media
卷以保留所有静态资产。
这是我最初尝试的,据我了解,它更适合便携,虽然我无法使其工作。如果您知道我将如何将主机安装的卷替换为命名卷,请不要犹豫。
您能否更具体地说明什么不起作用?通常,您已经为 postgres
容器安装了卷,如下所示:<volume-name>:<mount-path>
。它可能不起作用的唯一原因是最终目录结构可能与 nginx
所期望的不同。
嗨@Teddy,你能提供一个包含代码的回购链接以上是关于通过 Nginx (Django/React/Nginx/Docker-Compose) 提供 Django 媒体文件的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 nginx 通过 https 服务 phpmyadmin
Django-Gunicorn-Nginx 部署没有通过 Nginx