本文記錄如何利用Python、Flask、Nginx、Supervisor、Gunicorn、Git搭建Website

架構

我們使用Nginx作爲前端反向代理服務器,一些靜態資源可以直接通過Nginx訪問。對於動態資源,Nginx將請求轉發到後端的Gunicorn服務器,Gunicorn負責調用後端的Python代碼,進行動態處理。

租VPS

可以選擇以下服務器提供商

买域名

(域名解析到國內主機需要備案)

搭建環境

選擇Ubuntu作爲VPS的操作系統,下面開始安裝必要的軟件

安装nginx, git, mysql, python-pip, virtualenv, python-dev,libmysqlclient-dev

  • git 版本控制,同時也是程序源碼來源
  • python-pip 安裝Python module
  • virtualenv Python虛擬執行環境,可以避免依賴衝突
  • mysql 數據庫
  • nginx 前端代理服務器
$ sudo apt-get install git
$ sudo apt-get install python-pip
$ sudo apt-get install mysql-server
$ sudo apt-get install nginx
$ sudo apt-get install python-dev
$ sudo apt-get install libmysqlclient-dev

配置好mysql的密碼後,可以嘗試登陸

$ mysql -u root -p

安裝好nginx後,輸入以下命令,可以查看其運行狀態

$ sudo service nginx status

裝好後nginx服務已經啓動,此時,可以在瀏覽器中鍵入服務器ip或者域名,會顯示歡迎界面(圖爲在Raspberry Pi上面搭建的結果)

我們需要更改nginx的配置文件,使其能將請求解析到我們編寫的網頁。

Nginx配置

我們將項目部署到/usr/www/目錄下,首先創建以下目錄:

  • /usr/www/demo/app Python代碼
  • /usr/www/demo/app/static css樣式、js等靜態資源
  • /usr/www/demo/venv python virtualenv
  • /usr/www/demo/log 日誌文件
  • /usr/www/demo/files 圖片等附件
  • /usr/git/demo git服務器

接着,爲項目添加nginx配置文件。在/etc/nginx/sites-available/目錄下創建文件demo,文件內容爲

server {
    listen      80;

    root  /usr/www/demo/app;
    access_log /usr/www/demo/log/access_log;
    error_log  /usr/www/demo/log/error_log;

    server_name $demo.hejunjie.net;

    location ~ ^\/static\/.*$ {
        root /usr/www/demo/app;
    }

    location ~ ^\/files\/.*$ {
        root /usr/www/demo/files;
    }

    location / {
        proxy_pass       http://127.0.0.1:9000;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header Host \$host;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
    }
}

nginx會直接處理/static/*以及/files/*目錄下的請求,將其他請求轉發到本地的9000端口。9000端口由Gunicorn監聽,稍後會講解其配置。

接下來,我們在/etc/nginx/sites-enabled/下創建符號連接

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

sites-available目錄下的配置文件表示所有可能使用的配置,sites-enabled中的文件是sites-available中文件的符號鏈接,表示實際上正在使用的項目。通過更改符號鏈接,我們可以隨時更改正在使用的項目。

Gunicorn配置

上面,我們說道,nginx將動態請求轉發到本地的9000端口,Gunicorn正是監聽此端口。Nginx可以直接作爲服務進程啓動,但是Gunicorn還不行,因此我們使用SupervisorSupervisor是一个管理进程的工具,可以随系统启动而启动服务,它还时刻监控服务进程,如果服务进程意外退出,Supervisor可以自动重启服务。同時,我們安裝gevent,它是一個把Python同步代码变成异步协程的库;

首先,我們在/usr/www/demo/下創建Python虛擬環境,然後安裝Gunicorn,gevent

$ virtualenv venv
$ source venv/bin/active
(venv)$ pip install gunicorn
(venv)$ pip install gevent
$ deactive

然後,我們創建Supervisor配置文件。在/etc/supervisor/conf.d/目錄下,創建demo.conf文件。內容如下

[program:demo]
command     = /usr/www/demo/venv/bin/gunicorn --bind 127.0.0.1:9000 --workers 1 --worker-class gevent index:app
directory   = /usr/www/demo/app
user        = www-data
startsecs   = 3
redirect_stderr         = true
stdout_logfile_maxbytes = 50MB
stdout_logfile_backups  = 10
stdout_logfile          = /usr/www/demo/log/supervisor.log

配置文件通过[program:demo]指定服务名为demo,command指定启动gunicorn的命令行,设定gunicorn的启动端口为9000WSGI处理函数入口为index:app。即/usr/www/demo/app/目錄下的index.py文件,該文件中有app對象。

我們只需要將項目源碼放入/usr/www/demo/app/目錄下即可

Git配置

爲了方便項目部署,我們使用git作爲版本控制系統。並可以利用githook機制,當我們將源碼提交到版本庫時,同時將最新版的源碼拷貝到/usr/www/demo/app/目錄下,實現更新。

首先,按照git官方教程創建git server。然後在/usr/git/下建立一個裸倉庫

$ sudo git init --bare demo

接着,在hooks目錄下創建post-receive,當我們提交新源碼時,會更新項目代碼以及安裝新增的依賴項。內容如下

#!/bin/bash
GIT_WORK_TREE=/usr/www/demo/app git checkout -f
source /usr/www/demo/venv/bin/activate
pip install -r /usr/www/demo/app/requirements.txt
deactivate
supervisorctl reload demo

然後,我們需要將裸倉庫中的內容拷貝到/usr/www/demo/app

git clone [email protected]_ip:/usr/git/$project.git /usr/www/demo/app

之後,需要將git 添加到www-data用戶組,並將Supervisor配置文件中的user=www-data改爲user=git

還要更改Supervisor的權限配置,不然無法在提交後更新項目

[unix_http_server]
file=/var/run/supervisor.sock   ; (the path to the socket file)
chmod=0770                       ; sockef file mode (default 0700)
chown=root:www-data ;
gpasswd -a git www-data

就可以利用git提交代碼啦

啓動

$ sudo supervisorctl reload
$ sudo service nginx restart

集成

以上命令可以集成爲一個shell腳本,參數爲 項目名 端口號

#!/bin/bash

project=$1
port=$2
directory=/usr/www/$1
sudo mkdir -p $directory/app
sudo mkdir -p $directory/log
sudo mkdir -p $directory/files

# nginx
nginx_file="/etc/nginx/sites-available/$project"
nginx_conf="server {
    listen      80;

    root  $directory/app;
    access_log $directory/log/access_log;
    error_log  $directory/log/error_log;

    server_name $project.hejunjie.net;

    location ~ ^\/static\/.*$ {
        root $directory/app;
    }

    location ~ ^\/files\/.*$ {
        root $directory/files;
    }

    location / {
        proxy_pass       http://127.0.0.1:$port;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header Host \$host;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
    }
}"

echo $nginx_conf | sudo tee $nginx_file
sudo chmod 644 $nginx_file
sudo ln -s $nginx_file /etc/nginx/sites-enabled/$project

# supervisor 
supervisor_file="/etc/supervisor/conf.d/$project.conf"
supervisor_conf="[program:$project]\r\ncommand     = $directory/venv/bin/gunicorn --bind 127.0.0.1:$port --workers 1 --worker-class gevent index:app\r\ndirectory   = $directory/app\r\nuser        = git\r\nstartsecs   = 3\r\nredirect_stderr         = true\r\nstdout_logfile_maxbytes = 50MB\r\nstdout_logfile_backups  = 10\r\nstdout_logfile          = $directory/log/supervisor.log"

echo -e $supervisor_conf | sudo tee $supervisor_file
sudo virtualenv $directory/venv
sudo chmod -R 777 $directory/venv
source $directory/venv/bin/activate
pip install gunicorn gevent
deactivate
sudo chmod -R 755 $directory/venv 
sudo chown -R www-data:www-data $directory
sudo chown -R git:git $directory/app
sudo chown -R git:git $directory/venv

# git 
git_dir="/usr/git/$project.git"
hook_file="$git_dir/hooks/post-receive"
hook_shell="#!/bin/bash\n
GIT_WORK_TREE=$directory/app git checkout -f\n
source $directory/venv/bin/activate\n
pip install -r $directory/app/requirements.txt\n
deactivate\n
supervisorctl reload $project"

sudo mkdir -p /usr/git
sudo git init --bare $git_dir
echo -e $hook_shell | sudo tee $hook_file
sudo chmod 755 $hook_file
sudo chown -R git:git $git_dir
sudo chown -R ubuntu:ubuntu $directory/app
git clone [email protected]_ip:/usr/git/$project.git $directory/app
sudo chown -R git:git $directory/app

sudo service nginx restart
sudo supervisorctl reload