1、項目簡介html
在本文中,將一步一步搭建一個簡單的Flask + Virtualenv + uWSGI + Nginx 架構的Web服務,能夠做爲新手的學習也可做爲記錄備忘。node
若是你安裝好了環境並有必定基礎能夠直接從第五節開始部署。python
項目中只是演示了瀏覽器訪問地址,得到文本返回的過程,本人儘可能把配置解釋的清晰。基於搭建好的架構,後續能夠將業務層(Python)進行擴展,本文不作研究 ,好比:nginx
一、擴展業務代碼:實現json、靜態資源等等的請求響應。git
二、基於業務的數據庫查詢和部署。web
三、服務器端的部署完備(優化):域名、緩存、負載均衡、安全、備份、防火牆、異步IO等。算法
四、項目代碼管理。shell
2、框架介紹數據庫
一、Virtualenvapache
(1)同時擁有兩個python項目,一個是python2.7的,另外一個是python3的,能夠建立兩個虛擬環境。
(2)同時擁有兩個python項目,都依賴一個module(模塊)的不一樣版本,能夠建立兩個不一樣的虛擬環境,分別安裝這個module的不一樣版本。
(3)同時擁有兩個python項目,各自依賴不一樣的module,能夠在兩個不一樣的虛擬環境裏安裝模塊並開發項目。
二、Flask
Flask是一個Python的輕量級web框架,是基於Python開發而且依賴jinja2模板(模板語言)和 Werkzeug WSGI(WSGI工具集)服務的一個微型框架,對於Werkzeug本質是Socket服務端,其用於接收http請求並對請求進行預處理,而後觸發Flask框架,開發人員基於Flask框架提供的功能對請求進行相應的處理,並返回給用戶,若是要返回給用戶複雜的內容時,須要藉助jinja2模板來實現對模板的處理,即:將模板和數據進行渲染,將渲染後的字符串返回給用戶瀏覽器。
Werkzeug的補充:Werkzeug是WSGI工具包,他能夠做爲一個Web框架的底層庫。它不是一個web服務器,也不是一個web框架,而是一個工具包,官方的介紹說是一個 WSGI 工具包,它能夠做爲一個 Web 框架的底層庫,由於它封裝好了不少 Web 框架的東西( python web WSGI 開發相關的功能),例如 :
三、uWSGI
(1)WSGI
全稱 Web Server Gateway Interface ,是爲 Python 語言定義的 Web 服務器和 Web 應用程序或框架之間的一種簡單而通用的接口(協議)。WSGI是Web 服務器(uWSGI)與 Web 應用程序或應用框架(Flask,Django)之間的一種低級別的接口。能夠參考閱讀 PEP3333。
WSGI 分爲兩個部分
(2)uWSGI
uWSGI是一個Web服務器,C語言編寫,它實現了WSGI協議、uwsgi、http等協議。它要作的就是把HTTP協議轉化成語言支持的網絡協議。好比把HTTP協議轉化成WSGI協議,讓Python能夠直接使用。
(3)uwsgi
與WSGI同樣,是uWSGI服務器的獨佔通訊協議,用於定義傳輸信息的類型(type of information)。每個uwsgi packet前4byte爲傳輸信息類型的描述。
四、Nginx
Nginx ("engine x") 是一個高性能的HTTP和反向代理服務器,也是一個IMAP/POP3/SMTP服務器 。能夠做爲一個HTTP服務器進行網站的發佈處理,另外nginx能夠做爲反向代理進行負載均衡的實現。因它的穩定性、豐富的功能集、示例配置文件和低系統資源的消耗而聞名。
3、訪問過程
一、描述一:web客戶端->web服務器->業務代碼的訪問過程 (經過協議通訊)
二、描述二:不一樣用戶場景->服務器->不一樣應用的訪問過程(簡單架構,不包括複雜的部署,緩存等)
(1) 部署多個APP,採用單個Nginx,多個uwsgi+Flask,每一個uWSGI監聽不一樣端口。(標準)
(2) 部署多個APP,採用單個Nginx,單個uWSGI,多個Flask路由,Nginx和uwsg經過一個端口通訊。(很差)
注意:這種模式不是標準方式,「轉發」任務應該是由Nginx實現,經過多個端口和uWSGI通訊,每一個端口對應一個應用,如同描述一。這裏靠路由區分,是很差的。
4、環境準備
一、基本環境
(1)阿里ECS服務器 CentOS7.4 ,如需用戶密鑰部署能夠參考https://www.cnblogs.com/cleven/p/10899171.html
(2)Python環境:Python3.7,pip ,安裝過程省略。
二、虛擬環境:
(1)安裝
sudo pip3 install virtualenv sudo pip3 install virtualenvwrapper
(2)建立虛擬環境
mkvirtualenv -p python3 env1
-p 後面的參數指定了python3(也有可能要換成python3.2/python3.4,具體要看你係統裏面/use/bin/裏面的文件是什麼名字),若是去掉這個參數,就會使用系統默認的python。最後一個參數env1是建立的這個環境的名字。
(3)管理虛擬環境
deactivate # 退出當前虛擬環境
workon env1 # 使用虛擬環境env1
rmvirtualenv env1 # 刪除某個虛擬環境
lsvirtualenv # 列出全部虛擬環境
cdvirtualenv # 進入虛擬環境存儲目錄
(4)在服務器上同步本地的虛擬環境包
在開發機器上執行下面這個命令,來列出全部的包並保存到packages.txt,其中-l參數是隻列出當前虛擬環境的包:
pip3 freeze -l > packages.txt
而後在部署到生產環境的時候,把packages.txt也複製到每一個機器,並在每一個機器上執行:
pip3 install -r packages.txt
(5)注意點
安裝完虛擬環境,重啓shell後,workon等命令就沒法使用了,這是由於沒有添加環境變量:
export WORKON_HOME=/home/.virtualenv #虛擬環境所放置的目錄,能夠自行指定 source /usr/local/bin/virtualenvwrapper.sh
配置完上面,使用 source ~/.bashrc 命令就能夠了
三、web環境
(1)Flask ,虛擬環境下安裝
pip3 install flask
檢驗一下Flask和虛擬環境的安裝狀況:
<1> 建立項目目錄
mkdir webtest
<2> 進入虛擬環境
workon env1
<3> 進入webtest,建立hello.py
from flask import Flask app = Flask(__name__) @app.route("/app/flask/") def hello_flask(): return "Hello Flask!" if __name__ == "__main__": #Flask 開啓監聽全部地址的8888端口 app.run(host='0.0.0.0', port=8888)
<4> 啓動服務,並測試。 Flask是有內置web server的(通常只是測試時使用,不安全,可控性差,線上仍是使用uWSGI部署)
python3 hello.py
在瀏覽器訪問 [服務器的ip地址]:8888/app/flask/ ,瀏覽器中若是顯示「Hello Flask」,則Flask配置正常。
(2)uWSGI,虛擬環境下安裝
pip3 install uwsgi
(3)Nginx:不用在虛擬環境下安裝
sudo yum install nginx
5、部署步驟
一、建立uWSGI項目目錄
在/home/project/mysite目錄下建立以下結構
二、編寫run.py 啓動腳本
from flask import Flask app = Flask(__name__) @app.route("/app/uwsgi/") def hello_flask(): return "Hello uWSGI!"
三、配置uWSGI:
(1)進入項目目錄,編輯uwsgi.ini
vim uwsgi.ini
(2)下面給出配置文件的詳細說明(有些字段註釋掉了,畢竟咱們是簡單的搭建uWSGI服務)
[uwsgi] chdir=/home/project/mysite/ # 項目目錄 home=/home/zhangqi/.virtualenvs/env1 # 虛擬環境的路徑 wsgi-file=%(chdir)/run.py # 項目的啓動腳本文件路徑
#module=run # 項目的啓動腳本名字,不能是路徑,和wsgi-file功能相似
callable=app # 程序內啓用的application變量名,通常而言都是app=Flask(__name__),因此這裏是app master=true # 啓用主進程 processes=2 # worker進程個數 threads=2 # 每一個進程的線程數 procname-prefix-spaced=mysite # uwsgi的進程名稱前綴 ,使用 ps -ef | grep mysite查看
#############———————— 註釋掉的一些配置 ————————##############
#chmod-socket=666 # socket文件的訪問權限(socket字段配置的是文件的狀況) #logfile-chmod=644 #log權限 #uid=zhangqi # 啓動uwsgi的用戶名 #gid=zhangqi # 啓動uwsgi的用戶組 #py-autoreload=1 # py文件修改,自動加載 #vacuum=true # 退出uwsgi是否清理中間文件,包含pid、sock和status文件 #harakiri=30 # 設置自中斷時間 #post-buffering=4096 # 設置緩衝 #touch-reload=%(chdir) # 動態監控文件變化
###########################################################
############———————— 設置uWSGI的socket鏈接 ————————############ # # 方式一: socket文件,配置nginx時候使用。socket文件須要使用socket函數編寫。本文中沒有使用此方式 #socket=%(chdir)/uwsgi/uwsgi.sock # # 方式二:綁定地址+端口 socket=:8001 # # 方式三:監聽http端口,測試時候使用。若是不使用Nginx,瀏覽器是http協議,沒法使用socket直接通訊 #http=0.0.0.0:8001 # ################################################################ ############———————— 設置uWSGI的管理文件 ————————############ # # status文件,能夠查看uwsgi的運行狀態 # 命令:uwsgi --connect-and-read uwsgi/uwsgi.status stats=%(chdir)/uwsgi/uwsgi.status # # pid文件,經過該文件能夠控制uwsgi的重啓和中止 # 命令:uwsgi --reload uwsgi/uwsgi.pid # 命令:uwsgi --stop uwsgi/uwsgi.pid pidfile=%(chdir)/uwsgi/uwsgi.pid # # 日誌文件,經過該文件查看uwsgi的日誌 daemonize=%(chdir)/uwsgi/uwsgi.log # #############################################################
(3)啓動uWSGI,注意是虛擬環境。
uwsgi --ini uwsgi.ini
(4)調試uWSGI
ps -ef | grep mysite #或者 netstat -antp |grep 8001
顯示以下,uWSGI啓動正常
會以json格式顯示出完整內容,包括每一個總的狀態,每一個work是狀態,響應時間等,很是全面。
uwsgi --connect-and-read uwsgi/uwsgi.status
也有一些開源的監控工具可使用:uwsgitop
# pip3 install uwsgitop # uwsgitop uwsgi/uwsgi.status
uwsgi --ini uwsgi.ini # 啓動 uwsgi --reload uwsgi.pid # 重啓 uwsgi --stop uwsgi.pid # 關閉
四、配置Nginx:
(1)編輯配置文件 nginx.conf(主):
vim /etc/nginx/nginx.conf
內容以下
########### 每一個指令必須有分號結束 ################# user nginx; #配置用戶或者組,默認爲nobody nobody worker_processes auto; #容許生成的進程數,默認爲1 pid /run/nginx.pid; #指定nginx進程運行文件存放地址 include /usr/share/nginx/modules/*.conf; #加載動態模塊 #制定日誌路徑和級別。這個設置能夠放入全局塊,http塊,server塊 #級別依次爲:debug|info|notice|warn|error|crit|alert|emerg error_log /var/log/nginx/error.log; events { worker_connections 1024; #最大鏈接數,默認爲512 #use epoll; #事件驅動模型,select|poll|kqueue|epoll|resig|/dev/poll|eventport #accept_mutex on; #設置網路鏈接序列化,防止驚羣現象發生,默認爲on #multi_accept on; #設置一個進程是否同時接受多個網絡鏈接,默認爲off } http { default_type application/octet-stream; #默認文件類型,默認爲text/plain keepalive_timeout 65;#鏈接超時時間,默認爲75s,能夠在http,server,location塊配置 include /etc/nginx/conf.d/*.conf; #虛擬主機配置,引入不一樣server的配置(uWSGI) include /etc/nginx/mime.types; #引入文件類型 #sendfile_max_chunk 100k; #每一個進程每次調用傳輸數量最大值,默認爲0(無上限) ################## 日誌 ######################### #自定義日誌格式 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; #access_log off; #取消服務日誌 # #################################################### #################### SSL證書加密 ################### # #ssl_protocols TLSv1 TLSv1.1 TLSv1.2 SSLv3;#啓動特定的加密協議 #ssl_prefer_server_ciphers on;#設置協商加密算法時,優先使用服務端的加密套件,而不是客戶端瀏覽器 # #################################################### #################### 緩存性能優化 ################### # # 一、open_file_cache :配置能夠存儲的緩存 # max設置緩存中的最大元素數; 在緩存溢出時,刪除最近最少使用(LRU)的元素; # inactive是指通過多長時間文件沒被請求後刪除緩存。 # inactive設置20s,等到至少20s不訪問這個文件,相應緩存的這個文件的更改信息纔會被刪除。 # 例: # open_file_cache max=1000 inactive=20s; # # # 二、open_file_cache_valid:多長時間檢查一次緩存的有效信息。 # 設置爲30s,也就是說即便一直訪問這個文件,30s後會檢查此文件的更改信息是否變化,發現變化就更新 # 例: # open_file_cache_valid 30s; # # # 三、open_file_cache_min_uses : # 在上面open_file_cache 的 inactive時間內文件的最少使用次數。若是超過這個數字,文件更改信息一直是在緩存中打開的。 # 例: # open_file_cache_min_uses 2; # # 四、文件錯誤是否也一樣緩存 # open_file_cache_errors on; # #################################################### ################# 數據傳輸優化 ################### # # 容許sendfile( )方式傳輸文件,提升傳輸性能,默認爲off # 能夠在http塊,server塊,location塊配置 sendfile on; # # 設置調用tcp_cork方法,數據包不會立刻傳送出去,等到數據包最大時,一次性的傳輸出去,這樣有助於解決網絡堵塞。默認on, # tcp_nopush on; # # 打開tcp_nodelay ,禁用Nagle的緩衝算法,並在數據可用時當即發送,優化傳輸效率,默認on # tcp_nodelay on; # # #################################################### ################### 散列表大小 ######################## # Nginx使用散列表來存儲MIME type與文件擴展名。 # types_hash_bucket_size 設置了每一個散列表佔用的內存大小,其影響散列表的衝突率。 # 值越大,就會消耗更多的內存,但散列key的衝突率會下降,檢索速度就更快。 # 值越小,消耗的內存就越小,但散列key的衝突率可能上升。 # 默認1024。 types_hash_max_size 2048; #################################################### ################# Nginx負載均衡 (本文中沒有使用)################ # 負載均衡算法: # 一、熱備 # 二、輪詢 # 三、加權輪詢 # 四、ip_hash(相同的客戶端ip請求相同的服務器) # # 使用服務器列表: # 一、定義服務器列表 mysvr # 二、server的location模塊中將請求轉向mysvr 定義的服務器列表 # # server { #服務器訪問信息的配置 # ..... # location ~*^.+$ { #訪問路由的配置 # proxy_pass http://mysvr; #請求轉向mysvr 定義的服務器列表 # } # # # 寫法舉例: # 一、熱備 # upstream mysvr { //服務器列表 # server 127.0.0.1:7878; # server 192.168.10.121:3333 backup; #熱備 # } # # 二、輪詢 # upstream mysvr { # server 127.0.0.1:7878; # server 192.168.10.121:3333; # } # # 三、加權(參數) # upstream mysvr { # server 127.0.0.1:7878 weight=2 max_fails=2 fail_timeout=2; # server 192.168.10.121:3333 weight=1 max_fails=2 fail_timeout=1; # } # # 四、ip_hash # upstream mysvr { # server 127.0.0.1:7878; # server 192.168.10.121:3333; # ip_hash; # } # # nginx負載調優總結 :https://www.jianshu.com/p/4fa08f2a04ed # ################################################# }
(2)Flask站點對應的server配置:
上面的主配置中,有此句「include /etc/nginx/conf.d/*.conf;」 ,咱們將Flask對應的server模塊,單獨寫在一個文件裏(固然也能夠直接寫在上面的主配置中),一個server中又經過多個location指向不一樣應用。
理論上,一個Nginx對應多個站點,一個站點對應一個server,一個server又能夠對應多個location(應用)。
$ sodu touch /etc/nginx/conf.d/flask.conf
server {
listen 81; #監聽外部端口(瀏覽器)
server_name 39.xxx.xxx.xxx; #服務器地址
charset utf-8;
client_max_body_size 5M;
root /usr/share/nginx/html; #默認根目錄
include /etc/nginx/default.d/*.conf;
#配置路由
location / { #對根目錄的訪問都匹配,此處沒有作正則匹配講解
include uwsgi_params; #引入uwsgi
uwsgi_pass localhost:8001; #經過localhost:8001端口和uWSGI通訊(這是綁定的IP和端口,對應上文中uWSGI.ini裏面的配置)
# uwsgi_pass unix:/home/project/mysite/uwsgi/uwsgi.sock; #本文沒有使用socket文件進行演示。
}
location /static { #靜態文件,直接訪問路徑,不用進入uWSGI進行處理
alias /home/project/mysite/static/;
}
error_page 404 /404.html;
location = /40x.html {
#能夠更改錯誤頁面路徑
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
#能夠更改錯誤頁面路徑
}
}
(2)控制Nginx:
$nginx
或者
$systemctl start nginx
nginx -s reload #熱啓動,配置文件重裝載
kill -HUP 主進程號或進程號文件路徑 #平滑重啓
systemctl restart nginx
system stop nginx nginx -s stop #快速關閉 nginx -s quit #正常關閉
或者經過進程控制
ps -ef | grep nginx
kill -QUIT 主進程號 #從容中止
kill -TERM 主進程號 #快速中止
kill -9 主進程號 #強制中止
(3)查看Nginx狀態:
五、測試服務
(1)方法一:
瀏覽器輸入 39.xxx.xxx.xxx:81/app/uwsgi/ ,若是頁面顯示 「Hello uWSGI!」 ,大功告成!
(2)方法二:
curl http://39.xxx.xxx.xxx:81/app/uwsgi/ ,終端輸出 「Hello uWSGI!」,OK。
6、收尾
整個項目下來並非很複雜,主要是熟悉配置流程,而且根據搭好的基礎框架,升級業務功能。
在調試過程當中若是出現問題,能夠查看各個日誌,根據 上文: 3、訪問過程 進行逐步排查解決。
最後,後端領域還有不少東西須要學習,若有錯誤之處還望你們多多指教,本文多以作技術交流和配置備忘之用。