5.5 部署上线
本章目标:了解把 FastAPI 项目部署到服务器的完整流程和主流方案。本章偏实操指引,第一次部署建议按「方案 A」一步步来。
1. 开发环境 vs 生产环境
| 开发 | 生产 | |
|---|---|---|
| 启动命令 | fastapi dev(热重载) | fastapi run / uvicorn(多进程,无重载) |
| 数据库 | SQLite | MySQL / PostgreSQL |
| 密钥配置 | 写死在代码里 | 环境变量 |
| 建表 | create_all | Alembic 迁移 |
| 入口 | 直接暴露 8000 端口 | Nginx 反向代理 + HTTPS |
| 文档 | /docs 开放 | 视情况关闭 |
2. 第一步:把配置改成环境变量
密钥、数据库地址绝不能写死在代码里(更不能提交 git)。用 pydantic-settings 管理:
pip install pydantic-settings
新建 app/config.py:
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
# 有环境变量用环境变量,没有用默认值(默认值仅供本地开发)
database_url: str = "sqlite:///./blog.db"
secret_key: str = "dev-secret-do-not-use-in-prod"
access_token_expire_minutes: int = 60 * 24
cors_origins: list[str] = ["http://localhost:5173"]
# 也支持从 .env 文件读取
model_config = SettingsConfigDict(env_file=".env")
settings = Settings()
然后把 database.py、security.py、main.py 里写死的值改为从 settings 读取,例如:
from app.config import settings
engine = create_engine(settings.database_url, ...)
SECRET_KEY = settings.secret_key
本地开发建一个 .env 文件(加进 .gitignore):
DATABASE_URL=sqlite:///./blog.db
SECRET_KEY=本地随便
生产服务器上则通过真正的环境变量注入:
export DATABASE_URL="mysql+pymysql://bloguser:强密码@localhost:3306/blog"
export SECRET_KEY="$(python -c 'import secrets; print(secrets.token_hex(32))')"
3. 方案 A:Linux 服务器直接部署(最经典)
假设你买了一台 Ubuntu 云服务器:
① 装环境、拉代码
sudo apt update && sudo apt install -y python3.12-venv nginx
git clone https://github.com/you/blog-api.git /opt/blog-api
cd /opt/blog-api
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
② 准备数据库(以 MySQL 为例)
sudo apt install -y mysql-server
sudo mysql -e "CREATE DATABASE blog CHARACTER SET utf8mb4;
CREATE USER 'bloguser'@'localhost' IDENTIFIED BY '强密码';
GRANT ALL ON blog.* TO 'bloguser'@'localhost';"
pip install pymysql
# 设置好 DATABASE_URL 环境变量后执行迁移
alembic upgrade head
③ 用 systemd 常驻运行
生产启动命令(多进程):
fastapi run app/main.py --workers 4 --port 8000
# 等价于 uvicorn app.main:app --workers 4 --host 0.0.0.0 --port 8000
写成 systemd 服务让它开机自启、崩溃自动拉起。创建 /etc/systemd/system/blog.service:
[Unit]
Description=Blog API
After=network.target mysql.service
[Service]
User=www-data
WorkingDirectory=/opt/blog-api
Environment=DATABASE_URL=mysql+pymysql://bloguser:强密码@localhost:3306/blog
Environment=SECRET_KEY=生成的随机密钥
ExecStart=/opt/blog-api/venv/bin/fastapi run app/main.py --workers 4 --port 8000
Restart=always
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now blog
sudo systemctl status blog # 查看状态
journalctl -u blog -f # 看日志
④ Nginx 反向代理 + HTTPS
外网不直接暴露 8000 端口,由 Nginx 转发。创建 /etc/nginx/sites-available/blog:
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
sudo ln -s /etc/nginx/sites-available/blog /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
# 免费 HTTPS 证书(域名解析到服务器后执行)
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d api.example.com
至此 https://api.example.com/docs 就能访问了。
4. 方案 B:Docker 部署(团队/多环境推荐)
项目根目录建 Dockerfile:
FROM python:3.12-slim
WORKDIR /code
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY ./app ./app
COPY ./alembic ./alembic
COPY alembic.ini .
EXPOSE 8000
CMD ["fastapi", "run", "app/main.py", "--port", "8000"]
配合 docker-compose.yml 一键起「应用 + 数据库」:
services:
db:
image: mysql:8
environment:
MYSQL_DATABASE: blog
MYSQL_USER: bloguser
MYSQL_PASSWORD: 强密码
MYSQL_ROOT_PASSWORD: root强密码
volumes:
- db_data:/var/lib/mysql
api:
build: .
environment:
DATABASE_URL: mysql+pymysql://bloguser:强密码@db:3306/blog
SECRET_KEY: 生成的随机密钥
ports:
- "8000:8000"
depends_on:
- db
volumes:
db_data:
docker compose up -d --build
docker compose exec api alembic upgrade head # 首次跑迁移
5. 方案 C:PaaS 平台(最省事)
不想管服务器,可以用托管平台:Railway、Render、Fly.io 等,连上 GitHub 仓库即可自动部署(通常需要提供 Dockerfile 或启动命令)。国内可看各云厂商的容器/函数服务。适合个人项目和演示。
6. 上线检查清单
- SECRET_KEY 已换成随机值且来自环境变量
- 数据库换成 MySQL/PostgreSQL,密码不在代码里
-
echo=True已关闭;create_all 已移除,用 Alembic - CORS
allow_origins只写前端正式域名 - Nginx + HTTPS 配置完成,8000 端口不对外网开放
- systemd/Docker 保证进程崩溃自动重启
-
pytest全绿再部署 - 兜底异常处理器 + 日志已配置(见错误处理章)
本章小结
- 配置走环境变量(pydantic-settings),密钥绝不进代码库
- 经典部署链路:systemd 守护 fastapi run 多进程 → Nginx 反代 → certbot HTTPS
- Docker Compose 把应用和数据库打包,一条命令起全套
- 上线前过一遍检查清单
至此教程主线完结!遇到问题时查阅附录:附录 A:常见错误排查