部署
项目的所有功能都开发完成,并且经过测试没有 bug 后,就可以把项目部署到服务器上了,这里以虚拟机 Ubuntu 20.04 LTS Server 版的操作系统为例进行讲解。读者可以自行购买云服务器,如阿里云、腾讯云、华为云等进行部署,操作方式大同小异。
导出依赖包
在项目开发完成后,我们把项目使用的虚拟环境的依赖包导出,以方便在服务器上进行安装。在 PyCharm 的 Terminal 中输入以下命令完成依赖包的导出。
pip freeze > requirements.txt
执行完上述命令后,会在项目的根路径下生成一个 requirements.txt
文件,这个文件记录了当前项目所依赖的包。当项目上传到服务器后,通过以下命令即可一次性完成所有依赖包的安装。
pip install -r requirements.txt
使用Git上传代码
本地开发好的项目代码,可以通过多种方式上传到服务器,如 scp、Git 等。Git 更新代码更加方便,而且有版本管理功能,可以随时切换到之前的版本,本书选择 Git 来提交代码。首先在开发机上安装 Git,只要从 https://git-scm.com/downloads 根据自己的操作系统下载最新的 Git,然后安装即可。
Git 安装完成后,还需要用到 Git 服务器。可以自己搭建 Git 服务器,也可以使用第三方 Git 服务。如国内的有 Gitee(官网 www.gitee.com)、Coding(官网 www.coding.net),国外的有 Github(官网 www.github.com)、Gitlab(官网 www.gitlab.com)等。Github 是全球最大的代码托管网站,并且可以免费创建任意数量的私有项目,我们以 Github 为例来讲解。首先在 Github 上注册账号,然后在 Github 网站上创建一个名叫 pythonbbs 的仓库,为了使项目不被其他用户看到,选中 Private 单选按钮,如图 9-52 所示。

在图 9-52 中单击 Create repository 按钮后,出现如图 9-53 所示界面。
在图 9-53 中,可以看到新创建的 pythonbbs 项目的仓库地址,后续需要将本地的仓库地址和远程的仓库地址映射起来。
接下来,在本地 pythonbbs 项目的根路径下右击,在弹出的快捷菜单中选择 Git Bash Here 命令,就会打开 Git 的操作终端,如图 9-54 所示。


执行以下命令,将本地仓库的代码推送到 Github 服务器。
-
初始化仓库
$ git init
执行以上命令后,会将
pythonbbs
项目初始化成 git 仓库,在项目的根路径下就会多出一个.git
文件夹。 -
添加远程仓库地址
$ git remote add [远程仓库地址]
以上命令的作用是在本地仓库添加远程仓库地址,后期可以把代码推送到这个地址。读者在操作时应该将“[远程仓库地址]”修改为自己的仓库地址。仓库地址可以通过图 9-53 中所示方式获取。
-
添加所有代码到缓存区
$ git add .
将
pythonbbs
项目下的所有代码添加到缓存区。 -
将代码提交到本地仓库
$ git commit -m "first commit"
将缓存区的代码添加到本地仓库中。
-
将本地仓库代码推送到 Github 远程仓库
$ git push origin main
|
完成以上 5 个步骤,即可实现将本地仓库的代码推送到 Github 服务器上,以后我们在自己的服务器上,通过 pull
命令即可完成代码下载。现在机器有 3 种角色,分别是本地的开发机器、Git 服务器以及运行网站的网站服务器,三者之间的角色关系和工作流程如图 9-55 所示。
我们在本地开发机器上开发的代码,经测试没有 bug 后推送到 Git 服务器上(现在用的是 Github),然后在网站服务器上拉取代码,即可完成网站服务器代码的更新。

生产环境的配置
在 Git 中,分支是一个非常有用的功能。使用 Git 有一个基本的工作流程,就是尚未完成测试的代码,一般先放到 dev
或者 development
分支下。在测试没有 bug 后,再把代码合并到 main
分支下,并修改为生产环境下的配置信息,最后再把代码推送到网站服务器上,完成代码的更新。
按照 Git 的工作流程,在 main
分支下将 pythonbbs/config.py
中的 ProductionConfig
类根据实际情况修改配置信息,如数据库的域名和端口号等,并且设置 DEBUG=False
。然后再在 pythonbbs/app.py
中加载配置类,将之前的 DevelopmentConfig
修改为 ProductionConfig
,示例代码如下:
app.config.from_object(config.ProductionConfig)
完成以上操作后,在 Git 终端使用以下命令将代码推送到 Git 服务器:
$ git add .
$ git commit -m "production config"
$ git push origin main
安装常用软件
-
安装 OpenSSH
-
安装 Vim
Vim 是在 Linux 系统上使用的一款非常好用的文本编辑软件,几乎是 Linux 系统必备的软件,通过以下命令即可完成安装:
$ sudo apt install vim
-
安装 MySQL
这里我们是为了演示项目,把 MySQL 安装在了网站服务器上。在公司中如果有条件,建议购买单独的 MySQL 服务器,或者搭建独立的 MySQL 服务器,最大限度地保证数据的安全。在网站服务器上安装 MySQL 的命令如下:
$ sudo apt install mysql-server mysql-client $ sudo apt install libmysqld-dev
执行上述两条命令可以安装 MySQL 服务器和客户端。
-
安装 Redis
我们的
pythonbbs
项目使用 Redis,用来缓存邮箱验证码和 Celery 数据,通过以下命令可安装 Redis:$ sudo apt install redis
-
安装 Python3
有的服务器包比较旧,可能没有 Python3,可以通过以下命令安装:
# 安装 Python3 软件 $ sudo apt install python3 # 安装 Python3 下的 pip 工具 $ sudo apt install python3-pip # 安装 Python3 依赖的头文件等的开发包 $ sudo apt install python3-dev
-
安装 Git
网站服务器需要通过 Git 工具从 Git 服务器上拉取代码,通过以下命令可以安装 Git:
$ sudo apt install git
配置网站
在 9.12.4 节中的软件都安装完成后,我们就可以配置网站了,步骤如下。
-
创建新用户
默认的 root 账号权限过大,不建议使用 root 用户部署网站。接下来使用以下命令创建一个新的用户:
$ adduser zhiliao $ usermod -aG sudo zhiliao $ su zhiliao
上述命令首先创建了一个名叫
zhiliao
的用户,并且赋予了 root 权限,然后切换到了zhiliao
用户。 -
使用 Git 拉取代码
现在代码还是在 Git 服务器上,我们在
/home/zhiliao
下通过以下命令拉取代码到网站服务器:$ cd /home/zhiliao $ mkdir pythonbbs $ git remote add origin https://github.com/NunchakusHuang/pythonbbs.git $ git pull origin main
在执行
git pull
命令时,会提示输入 Github 网站的用户名和密码,输入之后,就可以将pythonbbs
项目的代码拉取到网站服务器上了。 -
安装 Python 依赖包
我们从开发机上导出了 Python 依赖包到
requirements.txt
中。在网站服务器上,可以通过以下命令完成依赖包的安装:$ pip install -r requirements.txt
-
创建数据库
登录 MySQL 后,使用以下命令创建名叫
pythonbbs
的数据库:> create database pythonbbs charset utf8mb4;
-
同步 ORM 模型到数据库中
我们在开发机上使用 migrate 生成的迁移脚本,可以直接在网站服务器上进行映射。通过以下命令即可完成:
$ flask db upgrade
以上命令会执行
pythonbbs/migrations/versions
下的所有迁移脚本,将 ORM 模型同步到数据库中生成表。 -
创建初始数据
网站运营前应该把初始数据创建好,包括角色、权限、板块以及初始的管理员用户,相关命令格式如下:
-
创建角色:
flask create-role
-
创建权限:
flask create-permission
-
创建板块:
flask create-board
-
创建管理员:
flask create-admin --username 张三 --email xx@qq.com --password 111111
-
-
运行 Celery
pythonbbs
项目的运行依赖于 Celery,而 Celery 又依赖于 Redis,因此在运行 Celery 之前,先运行 Redis。命令如下:$ sudo service redis start
使用Gunicorn部署网站
在学习使用 Gunicorn(是 Green Unicorn 的简称)部署网站之前,先来厘清 Web 服务器、应用服务器和 Web 应用框架 3 个概念。
-
Web 服务器:负责处理 HTTP 请求,并响应静态文件。常见的有 Apache、Nginx 以及微软的 IIS 等。
-
应用服务器:负责处理逻辑的服务器。如 Java、Python 的代码是不能直接通过 Nginx 这种 Web 服务器来处理的,只能通过应用服务器来处理,常见的应用服务器有 uWSGI、Gunicorn 以及 Tomcat 等。
-
Web 应用框架:使用某种编程语言、封装了常用的 Web 功能的框架就是 Web 应用框架。Flask、Django 以及 Java 中的 SSH(Structs+Spring+Hibernate)等都是 Web 应用框架。
WSGI(Web Server Gateway Interface,Web 服务网关接口)是 Python 中定义的 Web 服务器和 Web 应用框架之间的一种简单而通用的接口。我们在开发阶段使用 werkzeug
作为 WSGI 应用服务器,但是 werkzeug
仅用于开发,不能用于生产环境。在生产环境中应该选择性能强悍、稳定性高的 WSGI 应用服务器,如 uWSGI 和 Gunicorn。因为 Gunicorn 性能强、配置简单,所以选择用 Gunicorn 作为 pythonbbs 项目的应用服务器。
首先通过以下命令安装 Gunicorn:
$ sudo pip install gunicorn
接下来在项目的根路径下,创建 gunicorn.conf.py
文件,作为 Gunicorn 的运行配置文件,代码如下:
import multiprocessing
bind = "127.0.0.1:8000"
workers = multiprocessing.cpu_count() * 2 + 1
accesslog = "/var/log/pythonbbs/access.log"
daemon = True
上述代码中每个配置项的意义如下:
-
bind:监听的 IP 地址和端口号,语法为 IP:PORT。如果想让其他机器能够访问,则设置成
0.0.0.0
或者留空,然后再让 Gunicorn 监听 8000 端口即可。后面会用 Nginx 做 Web 服务器,Nginx 和 Gunicorn 之间要通过 8000 端口进行通信,所以 IP 设置为127.0.0.1
。 -
workers:workers 的数量。Gunicorn 官方推荐的配置是 CPU 数量 × 2 + 1。
-
accesslog:连接 Gunicorn 的日志文件。
-
daemon:是否作为守护进程执行,设置为
True
。
最后在 pythonbbs 项目的根路径下,执行以下命令启动 pythonbbs 项目:
$ gunicorn app:app
上述命令会自动在当前路径下寻找 gunicorn.config.py
文件,然后执行 app.py
模块下的 app
对象。至此,Gunicorn 就完成了对 pythonbbs 项目的部署。
读者将 bind 的 IP 地址修改为 0.0.0.0
,执行 gunicorn reload
命令重新加载配置文件,然后就可以通过 http://[机器的IP地址]:8000
访问该网站了。
使用Nginx部署网站
虽然 Gunicorn 可以让网站正常运行,但这不是最佳选择。下面使用 Gunicorn 推荐的 Nginx 来作为 Web 服务器。Nginx、Gunicorn 和 pythonbbs 三者之间的关系如图 9-56 所示。

通过图 9-56 可以发现,在浏览器向服务器发送请求后,先是由 Nginx 来处理,在遇到以 /static
开头的静态文件 URL 后,则直接返回静态文件;在遇到非静态路由后,则通过反向代理给 Gunicorn 服务器,Gunicorn 再传递给 pythonbbs 项目进行逻辑处理。使用 Nginx 作为 Web 服务器有以下好处:
-
Gunicorn 对静态文件处理效率并不好,包括响应速度和缓存等,Nginx 则不然。
-
Nginx 作为专业的 Web 服务器,可以对外界隐藏应用服务器,更加安全。
-
Nginx 运维起来更加方便。如设置负载均衡、配置 IP 黑名单等都非常方便,如果用 Gunicorn 实现会很麻烦。
要使用 Nginx,首先需要通过以下命令安装 Nginx:
$ sudo apt install nginx
Nginx 的常用命令如下:
-
启动:
sudo service nginx start
-
关闭:
sudo service nginx stop
-
重启:
sudo service nginx restart
-
测试配置文件:
sudo service nginx configtest
启动 Nginx 后,在浏览器中访问 http://[服务器IP]
,即可看到如图 9-57 所示的界面。

图9-57 是 Nginx 的默认界面,看到此界面说明 Nignx 已经运行成功。接下来再添加 Nginx 配置文件,在 /etc/nginx/conf.d 目录下创建 pythonbbs.conf 文件,然后添加以下代码。
upstream pythonbbs {
server 127.0.0.1:8000; # 配置服务器
}
server {
listen 80; # 监听的端口号
server_name 192.168.0.2; # 域名
charset utf-8;
access_log /var/log/nginx/pythonbbs_access.log;
error_log /var/log/nginx/pythonbbs_error.log;
# 最大的文件上传尺寸
client_max_body_size 75M;
# 静态文件访问的URL
location /static {
# 静态文件地址
alias /home/zhiliao/pythonbbs/static;
# 静态文件过期时间
expires 60d;
}
location /media {
# 文件上传地址
alias /home/zhiliao/pythonbbs/media;
expires 100d;
}
# 最后,发送所有非静态文件请求到 Gunicorn
location / {
proxy_pass http://pythonbbs;
# uwsgi_params 文件地址
include /etc/nginx/uwsgi_params;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
上述配置代码中,upstream 是用来设置负载均衡的,里面可以设置多个 server, Nginx 会根据策略选择不同的后端服务器来实现负载均衡。下面的 server 部分用来配置 Nginx 服务器,这里监听 80 端口,并且指定服务器名称为 192.168.0.2,以后通过这个服务器名称即可访问到 Nginx 服务器。读者在这里可以修改为自己真实服务器的 IP 地址,或者如果有域名,并且做好了域名和服务器的映射,则可以修改为域名。access_log 和 error_log 是配置客户端连接 Nginx 服务器的日志。location/static 用于指定以 /static 开头的静态文件的处理逻辑,使用 alias 指定静态文件寻找的路径,并且过期时间为 60 天。另外,我们还设置了 /media,用来获取用户上传后的图片。最后是非静态文件和非 media 文件,通过反向代理传给 Gunicorn 服务器,下面设置的 proxy_set_header 是为了能准确获取客户端的信息。
现在通过 80 端口和 8000 端口都可以访问到网站,原因是 Nginx 监听 80 端口,Gunicorn 监听 8000 端口。我们的预想是客户端仅通过 80 端口访问,8000 端口用于 Nginx 和 Gunicorn 通信。可以通过防火墙关闭 8000 端口来实现这个需求,执行命令如下。
$ sudo ufw enable
$ sudo ufw allow 80
$ sudo ufw deny 8000
$ sudo ufw allow 22
上述命令的作用分别是开启防火墙、打开 80 端口、关闭 8000 端口,打开 22 端口并且为了能在开发机上通过远程连接服务器,我们开启了 22 端口,也就是 SSH 服务。
压力测试
在项目部署完成后,我们可以进行压力测试,来评估 Nginx+Gunicorn 部署的网站的效率。使用的是 apache2-utils 中的 ab
命令。
-
安装 apache2-utils
通过以下命令安装
apache2-utils
:$ sudo apt install apache2-utils
-
执行压力测试命令
执行以下命令进行压力测试:
$ ab -n 50000 -c 1000 http://127.0.0.1/
其中,命令参数的含义如下:
-
-n
:总共发起请求的数量。这里设置为总共发起 50000 个请求。 -
-c
:并发的数量。这里设置并发数为 1000 个请求。 -
http://127.0.0.1/
:压力测试的 URL。
-
-
输出结果
执行完上述命令后,您可以看到类似如下的输出结果。
以上每项指标代表的意义如下。
-
Completed xxx requests:请求完成的数量。
-
Server Software:服务器软件名称。
-
Server Hostname:服务器主机名。
-
Server Port:服务器端口号。
-
Document Path:请求的 URL PATH。
-
Document Length:HTTP 响应数据的正文长度。
-
Concurrency Level:并发量。
-
Time taken for tests:总共耗费的时间。
-
Complete requests:完成的请求数。
-
Failed requests:失败的请求数。
-
Total transferred:所有请求响应数据的长度。
-
HTML transferred:所有请求响应数据中 HTML 内容的长度。
-
Requests per second:每秒完成的请求数,也叫 RPS(吞吐率)。
-
Time per request (mean):一个请求用户平均等待的时间。
-
Time per request (mean, across all concurrent requests):服务器完成一个请求平均的时间。
-
Transfer rate:网络传输速率。
-
Connection Times (ms):从请求到响应的整个过程消耗的时间。包括以下几个部分:
-
Connect(网络连接)
-
Processing(程序处理)
-
Waiting(等待)
其中:
-
min
:最小值 -
mean
:平均值 -
[+/-sd]
:标准差(反映数据的离散程度,数值越大表示越不稳定) -
median
:中位数 -
max
:最大值
-
-
Percentage of the requests served within a certain time (ms):在 50000 个请求中的不同完成率阶段进行采样,请求所消耗的时间。例如:
-
50%,也就是在第 25000 个请求时,花费了 70ms 的时间。
-
在第 50000 个请求消耗了 1107ms 的时间。
-
以上压力测试的硬件环境配置为 2GB内存+单核CPU 3600Hz+机械硬盘。其中QPS(queriesper second,每秒查询率)达到了 13000 以上,说明网站运行的效率还是不错的。