Nginx + Gunicorn 部署 Django + @vue/cli 前后端分离项目
背景知识
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 changesetup_12.x
withsetup_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
上一篇: 总结网站推广中需要避免的12种推广方式
下一篇: 此国用这个称呼侮辱中国,至今再也不敢叫
推荐阅读
-
Nginx + Gunicorn 部署 Django + @vue/cli 前后端分离项目
-
云服务器通过nginx部署springboot+vue前后端分离项目标准配置文件
-
laravel+vue-cli前后端分离部署到nginx
-
私服centos7.5配置nginx 用于前后端分离项目(vue+springboot前后端分离部署)
-
nginx+vue+uwsgi+django的前后端分离项目部署(解决前后端静态资源下载跨域问题)
-
Nginx+uWSGI+Daphne部署前后端分离(django+Vue)项目里面包含(channels(websocket))踩坑篇
-
用uwsgi和nginx 部署 django和vue打造的前后端分离项目
-
django+uwsgi+nginx 部署前后端分离项目的各种坑
-
Vue+Django前后端分离,在Linux下使用Nginx和uWSGI部署
-
spring-boot gradle vue 前后端分离项目在腾讯云上部署到 tomcat nginx