Nginx + Gunicorn 部署 Django + @vuecli 前后端分离项目

    技术2026-08-13  14

    背景知识

    Django

    Django是一个基于Python的开源网络框架,于2005年公布1.0版本,目前被Django Software Fundation维护。截稿前的Stable版本是3.0.8。Django遵从MVC架构模式,在我们开发的过程中,需要在models.py文件内定义我们的模型,在views.py中定义我们针对每一个HTTP/HTTPS请求所要执行的任务。

    举个例子来说,当前端通过GET HTTP://yoursite.com/login/这个URL访问我们的服务器,Django会自动调用views.py文件中,绑定了/login/这个URL的函数或者是方法,再将返回值返回给前端。如果只是单纯的使用Django进行开发的话,最终返回的一般会是一个由django渲染好的html文件,也就是我们平常所见的网页。

    但是,现在更多的是前后端分离的项目,前端通过调用API与后端进行数据的传输,在这种情况下,我们就不需要Django来渲染页面,而只是需要后端返回一个json格式的文件。

    Django Rest Framework

    对于以上的问题,我们可以使用Django REST Framework来提高我们的开发效率。具体的的使用方法,我会在后面的blog中为大家提供。

    Vue.js

    Vue.js是由华裔工程师尤雨溪开发并维护的渐进式JavaScript前端框架。与2014年发布初始版本。适用于SPA(Single Page Application)开发。Vue.js所关注的,就是MVC架构模式中的View(视图)层。作为前端框架,它可以很方便的通过axios与后端进行数据通信,并根据其遵从的MVVM架构模式,低延迟的将数据渲染到页面上。

    @vue/cli

    @vue/cli脚手架工具使得vue的开发工作变得更加便捷,这也是现在主流前端的开发方法。

    Gunicorn

    Gunicorn (Green Unicorn) 是一个Python 的 WSGI (Web Server Gateway Interface) HTTP server。可以用来代理基于python的Web应用程序。在我们的实例中,我们使用Gunicorn代理Django Web Application到Unix Socket,再使用Nginx将与gunicorn绑定的socket代理到我们的域名或者IP地址上。

    Nginx

    Nginx (Engine X) 是由俄罗斯开发团队开发并维护的异步框架的网站服务器,也可以用作反向代理,负载均衡和HTTP缓存等任务。在我们的例子中,我们主要使用的是其反向代理的功能,将与gunicorn绑定的socket代理到我们的域名或者IP地址上。

    任务前提

    已经有一个可以在localhost运行的 Django project项目前后端分离项目文件是直接从 git clone下来: 前端没有/dist 文件夹后端没有 migrate

    环境搭建

    首先需要更新系统内的包:

    $ sudo apt update $ sudo apt upgrade

    忽略这一步可能会导致环境安装失败。

    安装 Python 环境

    安装 python3 $ sudo apt-get install python3 安装 pip3 $ sudo apt-get install python3-pip 安装 virtualenv $ pip3 install virtualenv 新建虚拟环境 $ cd ~ $ virtualenv --python=python3 myenv 启动我们刚刚新建的虚拟 Python 环境 $ source ~/myenv/bin/activate安装其余我们项目所需要的环境: # 首先安装gunicorn $ pip install gunicorn # 其次安装我们所需要的包,如果有requirement.txt: $ cd /path/to/requirements/txt $ pip install -r requirements/txt # 如果没有requirements.txt,这里我们只前后端项目分离必备的包 $ pip install django # Django 框架 $ pip install djangorestframework # Django REST 框架,使后端只成为API的收发器 $ pip install markdown # Required by djangorestframework $ pip install django-filter # required by djangorestframework

    安装 Nodejs 环境

    Enable the NodeSource repository by runing: $ curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -If you need to install another version, for example 14.x, just change setup_12.x with setup_14.x 安装 Nodejs $ sudo apt-get install nodejs 验证安装 $ node --version v.12.16.3 $ npm --version 6.14.4

    安装 Nginx 环境

    $ sudo apt-get install nginx

    检查 nginx 是否安装成功:

    $ which nginx

    构建项目

    这里我们默认 project/settings.py中的 STATICFILES_DIRS, STATIC_ROOT, MEDIA_URL, 以及MEDIA_ROOT 已经设置好。前两项为必须设置,后两项只有在项目有传输图片或者视频等功能的时候才需要配置。 我们同样默认项目的结构如下:

    project/ ├── db.sqlite3 ├── frontend │   ├── README.md │   ├── axios.js │   ├── babel.config.js │   ├── dist │   ├── node_modules │   ├── package-lock.json │   ├── package.json │   ├── public │   ├── src │   └── vue.config.js ├── manage.py ├── app1 │   ├── __init__.py │   ├── __pycache__ │   ├── admin.py │   ├── apps.py │   ├── models.py │   ├── tests.py │   ├── urls.py │   └── views.py ├── media ├── requirements.txt └── project ├── __init__.py ├── __pycache__ ├── app.yaml ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py

    其中 /project/project/是我们配置项目的APP, /project/app1/是我们的后端程序,/project/frontend/是我们的前端服务器程序。我们默认使用nodejs完成前端工作. 在我们的例子中,/project/project/settings.py里的STATIC_ROOT指向frontend/dist/。

    构建前端项目

    $ cd project/frontend/ $ npm install # 安装前端所需要的插件 $ npm run build # 构建前端项目,输出文件会被放在 dist/ 文件夹下

    构建后端项目

    $ cd project # 项目的根目录 $ python manage.py makemigrations $ python manage.py migrate

    收集静态文件

    由于我们使用Nginx来部署我们的项目,所有的静态文件(static files)都会被Nginx分发。

    $ python manage.py collectstatic You have requested to collect static files at the destination location as specified in your settings: /path/to/your/dist This will overwrite existing files! Are you sure you want to do this? Type 'yes' to continue, or 'no' to cancel: yes x static files copied to '/path/to/your/dist', y unmodified.

    配置 Gunicorn

    创建 gunicorn.socket

    $ sudo vim /etc/systemd/system/gunicorn.socket

    在新建的文件里写入:

    [Unit] Description=gunicorn socket [Socket] ListenStream=/run/gunicorn.sock [Install] WantedBy=sockets.target

    其中 [Unit] section 描述我们新建的socket, [Socket] section 定义了我们未来socket文件要存储的位置,而[Install] section确定了 socket 文件会在正确的时间被创建

    创建 gunicorn.service

    由于我们需要gunicorn以一个service的形式来传递数据,我们需要新建一个gunicorn.service文件

    $ sudo vim /etc/systemd/system/gunicorn.service

    在新建的文件里写入:

    [Unit] Description=gunicorn daemon Requires=gunicorn.socket After=network.target [Service] # 这里的 user 需要被替换成已存在的用户,如果用户不存在的话会导致Gunicorn启动失败 User=user Group=www-data WorkingDirectory=/home/user/path/to/project ExecStart=/home/user/myenv/bin/gunicorn \ --access-logfile - \ --workers 3 \ --bind unix:/run/gunicorn.sock \ myproject.wsgi:application [Install] WantedBy=multi-user.target

    启动 Gunicorn Service

    我们现在可以启动刚刚创建好的 gunicorn.socket,注意是 .socket 而不是 .service。这会自动在/run/文件夹下创建一个gunicorn.sock 文件。当有对该socket连接是,systemd会自动启动 gunicorn.service来处理该连接。

    $ sudo systemctl start gunicorn.socket $ sudo systemctl enable gunicorn.socket

    检查 Gunicorn Socket 文件

    首先检查gunicorn.socket的运行状态

    $ sudo systemctl status gunicorn.socket ● gunicorn.socket - gunicorn socket Loaded: loaded (/etc/systemd/system/gunicorn.socket; enabled; vendor preset: enabled) Active: active (running) since Fri 2020-06-26 01:51:15 UTC; 16h ago Listen: /run/gunicorn.sock (Stream) Tasks: 0 (limit: 4373) Memory: 0B CGroup: /system.slice/gunicorn.socket Jun 26 01:51:15 dev-1 systemd[1]: Listening on gunicorn socket.

    接下来检查 gunicorn.sock文件是否存在

    $ file /run/gunicorn.sock /run/gunicorn.sock: socket

    测试 Socket 是否已激活

    现在我们只是启动了gunicorn.socket,而没有启动gunicorn.service。这是因为gunicorn.socket还没有收到任何连接。我们可以通过以下命令去检查gunicorn.service 的运行状态:

    $ sudo systemctl status gunicorn Output● gunicorn.service - gunicorn daemon Loaded: loaded (/etc/systemd/system/gunicorn.service; disabled; vendor preset: enabled) Active: inactive (dead)

    为了激活gunicorn.service,我们可以调用 curl 命令:

    $ curl --unix-socket /run/gunicorn.sock localhost

    之后,再输入上述命令来检查gunicorn.service是否被激活。 如果在gunicorn.service已经启动之后,修改了/etc/systemd/system/gunicorn.service文件,则需要运行以下命令来重启服务

    $ sudo systemctl daemon-reload $ sudo systemctl restart gunicorn

    配置 Nginx

    现在项目本身和gunicorn已经配置完成,剩下的就是 Nginx了。 首先,我们需要到 /etc/nginx/sites-available/这个文件夹内,并创建一个配置文件

    $ cd /etc/nginx/sites-available $ sudo vim myproject_conf

    在新建的文件内写入:

    upstream your-gunicorn { server unix:/run/gunicorn.sock; } # 这一项只有你需要使用HTTPS only的情况下才需要添加 # 监听80端口,并将其301 permenantly redirect 到https协议的地址 server { listen 80 default_server; server_name _; return 301 https://$host$request_uri; } server { # Nginx 监听的端口,如果是http:// 则监听80,https:// 则监听443 listen 80; # 输入ip地址和/或域名,用空格分开。如果需要用https,则必须要有域名 server_name 0.0.0.0 example.com; # 指向项目的根目录 root /root/of/your/project; # 以下3项配置只有需要用到https协议的时候才是必须的。其中ssl_sertificate和ssl_certificate_key可以由certbot自动填充 ssl on; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot location = /favicon.ico { access_log off; log_not_found off; } # 分发静态文件 location /static/ { autoindex on; alias /your/static/root/in/settings/py; expires 1M; access_log off; add_header Cache-Control "public"; proxy_ignore_headers "Set-Cookie"; } # 分发媒体文件 location /media/ { autoindex on; alias /your/media/root/in/settings/py; } location @proxy_to_app { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://your-gunicorn; } location / { try_files $uri @proxy_to_app; } }

    写入完成后,需要link到sites-enabled/内。保险起见,我们先将sites-enabled/内与新建配置文件重名的文件都删掉:

    $ sudo rm /etc/nginx/sites-enabled/myproject_conf

    之后再link:

    $ sudo ln -s /etc/nginx/sites-available/myproject_conf /etc/nginx/sites-enabled/

    检查一下配置语法是否有误:

    $ sudo nginx -t

    如果没有错误的话直接重启 Nginx 即可:

    $ sudo systemctl restart nginx

    大功告成!

    HTTPS 选项

    我们可以通过 Let’s Encrypt 申请一个免费的证书。但前提是我们需要有一个自己的域名。在我们的例子中,我们的网站运行在Nginx,使用Debian 10(buster)主机。

    安装 Certbot $ sudo apt-get install certbot python-certbot-nginx

    选择如何运行

    2.1 允许certbot修改我们的config文件 $ sudo certbot --nginx

    2.2 只得到证书,之后手动配置 $ sudo certbot certonly --nginx

    测试自动renew

    $ sudo certbot renew --dry-run

    之后如果在我们的config文件内看到有certbot修改过的痕迹,就说明证书安装成功了。

    References

    [1] https://www.digitalocean.com/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-debian-9#checking-for-the-gunicorn-socket-file

    [2] https://uwsgi-docs.readthedocs.io/en/latest/tutorials/Django_and_nginx.html

    [3] https://certbot.eff.org/lets-encrypt/debianbuster-nginx

    Processed: 0.013, SQL: 9