初衷
在探索了DRF之后(兩篇:Django REST Framework Quickstart 項目解讀 - 簡書 和 Django REST Framework 實現業務 api 并自動文檔化 - 簡書 ) ,是時候上線到一個正式的服務上了,也為了后面更好地實現一些想法。
準備服務器
一開始想到的還是阿里云主機,之前也用過一段時間,但是說實話,價格貴了點。因為現在只是實現一些自己的小想法,所以選擇了性價比稍微高一些的國外vps。根據大家的推薦,選擇了vultr的5刀版本,已經足夠使用了,支持支付寶付款,所以特別方便。
但是這個過程還是有些問題,比如說節點的IP可能會被墻,解決的方法是,不斷地試著換實例,一般能換到訪問正常的節點。但是這種墻法還是比較暴力的,使用了tcp截斷,你會發現能ping通,但是不能建立tcp連接。
VPS啟動ok之后,建立一個新用戶,避免總是使用root賬號去登錄或者去操作,并在新用戶上加入sshkey,方便今后ssh連接。之后就可以綁定域名了,使用vps的另一個好處是,不需要備案。
安裝nginx
直接安裝便可,我看版本也不低。
apt-get install nginx
主配置文件:/etc/nginx/nginx.conf
啟動nginx服務:
sudo service nginx start
默認情況下,使用域名測試訪問nginx是否啟動正常便可。
安裝python3+virtualenv環境
因為我選擇了ubuntu18.04的系統,好像默認就預裝了python3.6,所以這個可以跳過了。
安裝virtualenv:
sudo apt-get install python3-pip
pip3 install virtualenv
高版本的ubuntu可能會把virtualenv安裝到$HOME/.local/bin/下面,所以需要把路徑添加到PATH里。
建立django運行的虛擬環境并安裝django等
virtualenv -p /usr/bin/python3 env3
pip install djangorestframework django pymysql coreapi pygments markdown
git clone https://github.com/roubo/rouboApi.git
安裝mysql
sudo apt-get install mysql-server
## 查看默認用戶名密碼
sudo cat /etc/mysql/debian.cnf
## 登錄
mysql -u xxxx -p yyyy
修改密碼:
mysql> use mysql;
Database changed
mysql> update user set plugin='mysql_native_password' where user='root';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> update user set authentication_string=password('xxx') where user='root';
Query OK, 1 row affected, 1 warning (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 1
mysql> flush privileges;
Query OK, 0 rows affected (0.01 sec)
建立業務相關的數據庫
create database rouboinfo default charset utf8 collate utf8_general_ci;
django項目數據庫初始化
python manage.py makemigrations
python manage.py migrate
測試Django端服務
啟動django調試服務
python manage.py runserver
測試接口,本地訪問ok(django服務本身不需要對外網提供訪問服務,最終通過nginx分發)
? ~ curl -i 'http://127.0.0.1:8000/rouboapi/v1/report/'
HTTP/1.1 400 Bad Request
Date: Thu, 06 Sep 2018 03:42:23 GMT
Server: WSGIServer/0.2 CPython/3.6.5
Content-Type: application/json
Vary: Accept
Allow: GET, HEAD, OPTIONS
X-Frame-Options: SAMEORIGIN
Content-Length: 124
{"report_type":["This field is required."],"device_id":["This field is required."],"ip_address":["This field is required."]}%
橋接nginx和django
現在nginx和django的服務都已經ok,接下來就是怎么打通兩者,我們之前提到過uWSGI。這里我們來了解下WSGI協議的事情。
WSGI協議
WSGI(Web Server GateWay Interface),這個是一套協議,用于連接web server 和 web application。他的出現也是人們對Web的server和application解耦的需求。舉個例子,比如我們要完成兩個api接口,我們可以很作地選擇兩個web application框架,比如Django和Flask,一個api分別使用一種框架,而對客戶端來說,可以完全無感。WSGI協議在這里就起到了橋接作用,可以實現多Server對單application,或者當server對多application。
uWSGI應用
uWSGI是一個應用,他可做為兩種角色:
- 當在 Nginx + uWSGI + Django 場景下,他是中間件。
- 當在 uWSGI + Django 的場景下, 他是Web Server。
當為中間件角色時,需要用到其uwsgi協議,Nginx一般通過該協議與uWSGI連接。這里的uwsgi協議的作用,我們可以簡單地理解為,如何高效地傳遞request和response便好。而uWSGI中間件的作用,我們也可以簡單地理解為,作為一個網關屏蔽各web application之間的協議、開發語言等差異,并且高效地管理著各種并發、負載均衡、日志等。
那為什么要選擇 Nginx + uWSGI + Django 這種方式呢? 其實是一個物盡其用的理由。nginx有如下優點:
1、安全,客戶端對Web服務器的訪問需要先經過反向代理服務器。這樣可以防止外部程序對Web服務器的直接攻擊。
2、負載均衡,反向代理服務器可以根據Web服務器的負載情況,動態地把HTTP請求交給不同的Web服務器來處理,前提是要有多個Web服務器。
3、提升Web服務器的IO性能。一個HTTP請求的數據,從客戶端傳輸給服務器,是需要時間的,例如N秒,如果直接傳給Web服務器,Web服務器就需要讓一個進程阻塞N秒,來接收IO,這樣會降低Web服務器的性能。如果使用反向代理服務器,先讓反向代理服務器接收完整個HTTP請求,再把請求發給Web服務器,就能提升Web服務器的性能。還有一些靜態文件的請求,可以直接交給反向代理來處理,不需要經過Web服務器。
安裝和配置uWSGI和Nginx
注意我還是將應用安裝在虛擬環境中。
首先在非虛擬環境中安裝全局依賴的包:
sudo apt-get install python3.6-dev build-essential
在虛擬環境中安裝uWSGI
pip install uwsgi
測試uWSGI + django是否運行正常,在項目路徑下運行并測試8080端口是否正常:
uwsgi --http :8080 --module rouboinfo.wsgi
如果這一切正常,那就可以開始配置nginx接入uWSGI了。
增加nginx配置,rouboapi.conf:
# the upstream component nginx needs to connect to
upstream django {
server unix:///data/django/rouboApi/rouboapi.scok; # for a file socket
#server 127.0.0.1:8001; # for a web port socket (we'll use this first)
}
# configuration of the server
server {
# the port your site will be served on
listen 80;
# the domain name it will serve for
server_name app.airoubo.com; # substitute your machine's IP address or FQDN
charset utf-8;
# max upload size
client_max_body_size 75M; # adjust to taste
# Django media
#location /media {
# alias /path/to/your/mysite/media; # your Django project's media files - amend as required
#}
#location /static {
# alias /path/to/your/mysite/static; # your Django project's static files - amend as required
#}
# Finally, send all non-media requests to the Django server.
location /roubo {
uwsgi_pass django;
include /etc/nginx/uwsgi_params; # the uwsgi_params file you installed
}
}
使用配置文件運行uWSGI:
# mysite_uwsgi.ini file
[uwsgi]
# Django-related settings
# the base directory (full path)
chdir = /data/django/rouboApi
# Django's wsgi file
module = rouboinfo.wsgi
# the virtualenv (full path)
home = /data/django/env3
# process-related settings
# master
master = true
# maximum number of worker processes
processes = 10
# the socket (use the full path to be safe
socket = /data/django/rouboApi/rouboapi.scok
# ... with appropriate permissions - may be needed
chmod-socket = 666
# clear environment on exit
vacuum = true
# 在虛擬環境下執行
uwsgi --ini rouboapi.ini
至此,我們發現接口訪問已經正常,nginx <-> uWSGI <-> django 的通路已經ok了。但是我們來看下Django REST Framework 的文檔頁面的效果:
發現所有樣式都丟失了。。這是因為我們的nginx配置里并沒有將靜態文件暴露給客戶端:
#location /static {
# alias /path/to/your/mysite/static; # your Django project's static files - amend as required
#}
Django的靜態文件收集和Nginx的靜態文件配置
上面說的文檔頁面的樣式丟失問題,我們可以看下nginx的error log:
2018/09/06 07:41:27 [error] 14754#14754: *23 open() "/usr/share/nginx/html/static/rest_framework/js/default.js" failed (2: No such file or directory), client: 116.231.148.176, server: app.airoubo.com, request: "GET /static/rest_framework/js/default.js HTTP/1.1", host: "app.airoubo.com", referrer: "http://app.airoubo.com/roubo/rouboapi/v1/report/"
也就是rest下的js文件是404。
收集Django的靜態文件到指定目錄
Settings.py中加入收集路徑:
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
在項目下執行:
python manage.py collectstatic
執行后在項目目錄下可以看到static文件夾:
static
|-- admin
| |-- css
| |-- fonts
| |-- img
| `-- js
`-- rest_framework
|-- css
|-- docs
|-- fonts
|-- img
`-- js
配置nginx,放開靜態文件,修改nginx配置文件中的static如下:
location /static {
alias /data/django/rouboApi/static;
}
重啟nginx,查看效果,完工:
完成
后端部署完成,后面可以愉快地添加新接口新服務了。