欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Nginx + Gunicorn 部署 Django + @vue/cli 前后端分离项目

程序员文章站 2022-06-13 20:37:41
...

背景知识

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)主机。

  1. 安装 Certbot
$ sudo apt-get install certbot python-certbot-nginx
  1. 选择如何运行

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

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

  2. 测试自动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

相关标签: Django