前言
在前面的博客中,我在本地以及在 pythonanywhere 都部署过简单的 web 网站。
本文将做一些梳理、补充。
1. 关于"架构"
部署过程实际上取决于你使用的"技术选型" & "平台类型"。
1.1 理论架构
以 Django 为后端的 web 服务的"理论架构"就是:
- web 服务器
- WSGI/ASGI
- web 应用
1.2 标准部署架构
传统上,严格遵守上述理论框架的部署架构就是
- web 服务器:Nginx
- WSGI:Gunicorn
- web 应用:Django
简单记为:Nginx + WSGI +Django
这套架构的优势来自于 Nginx 作为专业 web服务 带来的性能优势。
这套架构对于初学者来说的缺点在于需要额外的 Nginx 相关知识。
在部署方面,常依托于 IaaS 平台。
1.3 便捷部署架构
作为初学者,比较常用的架构是
- web 服务器(功能分散在以下地方)
PaaS 平台:边缘处理 TLS/安全- Gunicorn:处理 HTTP 解析
- WhiteNoise:处理静态文件
- WSGI 服务:Gunicorn
- web 应用:Django
简单记为:Gunicorn + WhiteNoise + Django
这套架构的优势来自于无需额外的 web 服务器相关知识,专注于项目代码。
这套架构的缺点在于,如果业务发展到大规模并发的阶段,可能会有性能问题。
- WhiteNoise 的瓶颈:高并发静态文件请求会占用 Django 工作进程(因为 WhiteNoise 是 WSGI 中间件,文件读取是同步阻塞的)
- Gunicorn 的瓶颈:没有 Nginx 的 keep-alive 连接池、缓存、压缩等优化
- 但注意:对于小到中型项目,这个瓶颈通常在数据库或业务逻辑,不在静态文件服务。
在部署方面,常依托于 PaaS 平台。
-
注1:
- 实际上,大多数 PaaS 平台在边缘仍有反向代理(如 Nginx),但这对你透明。
- 当然,WhiteNoise 仍是需要的,处理的是应用层静态文件服务。
-
注2:
- 这里将 Gunicorn、WhiteNoise 放在 "web 服务器" 下,只是为了便于初学者理解功能上的对应关系。
- 实际上:
- Gunicorn 属于 WSGI 层(标准架构中的中间层),并非 Web 服务器。
- WhiteNoise 是嵌入 Django 的中间件,让应用层具备了服务静态文件的能力------这在严格的三层理论架构中是不存在的,属于"便捷架构"的特例。
- 因此,"Gunicorn + WhiteNoise + Django" 本质上是用 PaaS 提供的边缘网关 替代了 Nginx 的 Web 服务器角色,同时用 WhiteNoise 弥补了缺少 Nginx 后静态文件服务的空白。
2. 关于数据库
2.1 为什么需要 托管数据库服务
在本地进行学习过程中,我使用过 SQLite、PostgreSQL。
然而在实际部署过程中,或者说在最终需要上线的时候,我们需要 购买/使用托管数据库服务。
实际生产环境中,不应该把数据库和 Django 塞在同一台机器里,更不该用 SQLite。
| 场景 | 存储要求 | 风险 |
|---|---|---|
| SQLite | 几十MB到几GB | 文件损坏=数据丢失,无备份机制 |
| 自建 PostgreSQL | 需要专业 DBA 知识,磁盘要备份、监控、扩容 | 磁盘满=服务崩,误删=数据永久丢失 |
| 托管数据库服务 | 自动备份、自动扩容、自动故障转移 | 贵一点,但 sleep well |
因此,需要把 "托管数据库服务" 作为我们成本的一部分!
2.2 什么时候开始用 托管数据库服务
-
基本流程
首先,在本地开发的过程中,我们仍使用本地数据库。
在本地功能验证完毕,准备部署之前,我们需要购买并验证托管数据库服务。
然后再部署到生产环境。
-
要点
本地验证的一个注意点是:本地测试与生产环境隔离
即:不要让本地调试、测试代码造成的改动污染生产环境数据库
-
本地验证
托管数据库服务的策略策略1:本地临时连接,仅验证连通性,不验证业务逻辑,验证后立即断开
策略2:创建临时测试库,本地连接并验证连通性和业务逻辑,部署时改回生产库
3. 部署(以 PaaS 平台为例)
这里我仅讨论 Gunicorn + WhiteNoise + Django 这套框架下的部署。
3.1 环境变量 & 相关代码
- 我们的思路是:
- 将部署时需要修改的配置专门放在一个配置文件中
- .env 文件
- 在项目根目录下
- 请勿将生产环境的密码、密钥上库到 github
bash
DATABASE_URL=postgres://用户名:密码@localhost:5432/数据库名
DEBUG=False
SECRET_KEY=xxx
ALLOWED_HOSTS=abc.com
CSRF_TRUSTED_ORIGINS=http://abc.com
- settings.py
- 这里的代码通过读取环境变量获取相应的值
python
from environs import Env # new
env = Env() # new
env.read_env() # new
...
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env.str("SECRET_KEY")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env.bool("DEBUG", default=False)
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=["localhost", "127.0.0.1"])
...
DB_URL_STR = env.str("DATABASE_URL", default="")
if "sqlite3" in DB_URL_STR:
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
else:
DATABASES = {
"default": env.dj_db_url("DATABASE_URL"),
}
...
CSRF_TRUSTED_ORIGINS = env.list(
"CSRF_TRUSTED_ORIGINS",
default=["http://localhost", "http://127.0.0.1"]
)
- 解析
-
SECRET_KEY 和 DATABASES 我们不给默认值
- 防止相关密码、密钥 泄露
- 如果不配置就报错,防止在生产环境产生默认行为
-
ALLOWED_HOSTS、CSRF_TRUSTED_ORIGINS
- ALLOWED_HOSTS 防 Host 头攻击
- CSRF_TRUSTED_ORIGINS 防跨站伪造
-
本地开发阶段
- 可以使用 DATABASE_URL=sqlite3,在代码里面会走到本地数据库的配置分支
- DEBUG=True,有助于看到报错信息
- ALLOWED_HOSTS、CSRF_TRUSTED_ORIGINS
- 用 # 注释掉
- 或者进行如下配置
bashALLOWED_HOSTS=localhost,127.0.0.1 CSRF_TRUSTED_ORIGINS=http://localhost,http://127.0.0.1- 这样会准许 localhost 的访问
-
部署阶段
- DATABASE_URL 请使用专门的 托管数据库服务 厂商所提供的相关配置
- DEBUG=False
- SECRET_KEY 通过以下命令生成
pythonpython -c "import secrets; print(secrets.token_urlsafe())"- ALLOWED_HOSTS、CSRF_TRUSTED_ORIGINS 根据 PaaS 平台提供的 URL 进行配置
-
3.2 部署过程
- 部署过程(在 PaaS 平台的终端 或者 前端)
- step 1: 更新代码(git clone)
- step 2: 导入依赖(按照 requirements.txt)
- step 3: 配置 .env 文件(五个参数)
- step 4: 数据库相关操作(migrate、createsuperuser)
- step 5: 静态文件(collectstatic)
- step 6: 重启服务
- step 7: 测试 & 问题修复
- 注意点:
-
step 1: 更新代码(git clone)
- 可能需要在平台做 SSH 相关配置
-
step 2: 导入依赖(按照 requirements.txt)
- 注意使用哪个 python(比如在PaaS平台选择环境时,可能会选择 python版本,那么在终端操作就需要找对 python)
bash# 形如 python3.11 -m pip install -r requirements.txt- 这里的 python3.11 是我学习过程中使用,并在 PaaS 平台选择的
- 实际需要根据部署时的实际情况进行选择(形如:python、python3.12等)
- 你可以使用形如
which python3.11的命令来查看需要的 python 版本是否存在
-
step 3: 配置 .env 文件(五个参数)
- 如前面的 3.1 节所述
-
step 4: 数据库相关操作(migrate、createsuperuser)
- 注意要使用
托管数据库服务 - 以及专门为生产环境创建的数据库
- 由于我们使用的数据库与生产环境不是同一个
- 需要依次执行 migrate、createsuperuser
bash# 刷新数据库 python3.11 manage.py makemigrations posts python3.11 manage.py migrate # 创建管理员 python3.11 manage.py createsuperuser - 注意要使用
-
step 5: 静态文件(collectstatic)
- 执行 collectstatic 自动收集静态文件
bashpython3.11 manage.py collectstatic- 注:由于 staticfiles/ 可以由这个命令自动生成,故此我在 git 上库中将其忽略,所以在部署时需要执行 collectstatic 来生成这个文件夹
-
step 6: 重启服务
-
step 7: 测试 & 问题修复
- 功能测试
- 进行静态文件访问 <主页URL>/static/admin/css/base.css
-
- 补充
- 有些平台可能在前端配置的时候就可以配环境变量
3.3 依赖
- python
- requirements.txt 很重要,在进行了导入包的操作后需要进行更新
- 目前导入过的包如下:
bash
pip install django~=4.2.0 # Django
pip install black # 代码格式化工具,开发用,保持统一代码风格
pip install "environs[django]" # 用于读取环境变量
pip install gunicorn==20.1.0 # WSGI
pip install "psycopg[binary]"==3.1.8 # 访问 PostgreSQL 数据库,注意:不同类型数据库用的驱动包不一样!
pip install whitenoise==6.12.0 # WhiteNoise 中间件