目录
使用 UV 进行环境隔离,使用 pyc 编译文件发布运行
项目构建
UV安装
忽略安装过程,Windows可参考: Windows 安装 UV
初始项目
D:\OpenSource\Python\DeployDemo>uv --version
uv 0.9.18 (0cee76417 2025-12-16)
D:\OpenSource\Python\DeployDemo>uv python install 3.12
Python 3.12 is already installed
D:\OpenSource\Python\DeployDemo>uv init -p 3.12
Initialized project `deploydemo`
D:\OpenSource\Python\DeployDemo>uv run main.py
Using CPython 3.12.12
Creating virtual environment at: .venv
Hello from deploydemo!
D:\OpenSource\Python\DeployDemo>
uv init -p 3.12 会生成4个文件
deploydemo
├── .python-version //项目使用的 Python 的版本
├── README.md //空的项目说明文件
├── main.py //包含一个简单的示例代码。
└── pyproject.toml //定义项目的元数据,如名称、版本和依赖项。
uv run main.py 后会生成 .venv 目录和 uv.lock文件
deploydemo
├── .venv //虚拟环境,为项目提供了一个与操作系统和其他项目完全隔离的独立Python运行空间
└── uv.lock //跨平台的锁文件,记录了项目所有直接和间接依赖的精确版本号及校验哈希值。确保了无论在哪台机器上安装,得到的依赖树都完全一致,从而实现可重复构建。
uv.lock 需要提交到git,它是确保团队协作和部署时环境一致性的关键文件

构建 FastAPI
FastAPI 没有强制目录规范。小项目可以只有一个 main.py,但稍微长期维护的项目通常按"接口、业务、数据、配置"分层。
text
my-fastapi-project/
├── pyproject.toml # uv / Python 项目依赖与配置
├── uv.lock # uv 锁定的准确依赖版本
├── .env # 本地环境变量,不提交到 Git
├── .env.example # 环境变量模板,可提交
├── README.md
├── app/
│ ├── __init__.py
│ ├── main.py # 应用入口,创建 FastAPI 实例、注册路由、中间件
│ │
│ ├── api/ # HTTP 接口层
│ │ ├── __init__.py
│ │ ├── deps.py # 路由依赖注入,例如当前用户、数据库会话
│ │ └── v1/ # API 版本划分
│ │ ├── router.py # 汇总 v1 下的所有路由
│ │ ├── users.py # 用户相关接口
│ │ └── contracts.py # 合同相关接口
│ │
│ ├── core/ # 全局基础能力与配置
│ │ ├── config.py # 从 .env 读取配置
│ │ ├── security.py # 密码哈希、JWT、权限等
│ │ └── exceptions.py # 自定义异常和统一异常处理
│ │
│ ├── schemas/ # 请求和响应的 Pydantic 模型
│ │ ├── user.py
│ │ └── contract.py
│ │
│ ├── models/ # 数据库 ORM 模型,如 SQLAlchemy 表定义
│ │ ├── user.py
│ │ └── contract.py
│ │
│ ├── services/ # 业务逻辑,例如合同分析、用户注册
│ │ ├── user_service.py
│ │ └── contract_service.py
│ │
│ ├── repositories/ # 数据库查询和持久化逻辑,可选
│ │ └── contract_repository.py
│ │
│ ├── db/ # 数据库连接、会话、初始化
│ │ ├── session.py
│ │ └── base.py
│ │
│ └── utils/ # 无业务归属的通用工具函数
│
├── tests/ # 自动化测试,结构通常对应 app/
│ ├── api/
│ └── services/
│
├── alembic/ # 数据库迁移文件,使用 Alembic 时生成
├── scripts/ # 一次性或运维脚本
└── static/ # 静态文件;仅在服务页面、上传文件等场景需要
职责上建议保持:
api/:接收 HTTP 请求、参数校验、返回响应;尽量少放业务逻辑。schemas/:定义接口的输入输出格式,例如ContractCreate、ContractResponse。services/:处理业务规则,适合合同解析、权限判断、流程编排。models/:定义数据库表,不直接等同于 API 返回的数据。repositories/:隔离数据库访问;小项目也可以直接省略,查询放在services/。core/:配置、安全、日志、异常等跨模块能力。
最小可用版本其实只需要:
text
app/
├── main.py
├── routers/
│ └── contracts.py
├── schemas/
│ └── contract.py
└── services/
└── contract_service.py
项目增长后再引入 models/、db/、repositories/ 和 API 版本目录即可。对于合同分析项目,建议从这个最小结构开始,并优先把"解析/分析逻辑"放在 services/,避免堆进路由函数。
D:\OpenSource\Python\DeployDemo>uv add fastapi --extra standard
--extra standard 表示:安装 FastAPI 定义的名为 standard 的一组可选依赖。
powershell
uv add fastapi --extra standard
基本等价于:
powershell
uv add "fastapi[standard]"
除了 FastAPI 核心包,还会安装常用功能所需的依赖,例如:
uvicorn[standard]:运行 ASGI 服务fastapi-cli:提供fastapi dev、fastapi runpython-multipart:处理表单和文件上传jinja2:HTML 模板httpx:测试客户端等email-validator:邮箱字段校验
不加 --extra standard:
powershell
uv add fastapi
只安装 FastAPI 的核心依赖,体积更小,但运行服务等功能可能需要你自行添加依赖。
注意命令中应使用英文空格,不要包含中文逗号 ,。通常开发 FastAPI 项目推荐:
powershell
uv add fastapi --extra standard
添加应用入口
应用入口,创建 FastAPI 实例、注册路由、中间件
创建 app 目录,在app目录增加 main.py,代码如下
python
from fastapi import FastAPI
import time
app = FastAPI()
@app.get("/")
def read_root() -> dict:
return {"message": f"Hello, FastAPI with UV!{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}"}
外层 main.py加入下列代码,(可有可无,后面可以直接用命令运行)
python
import uvicorn
def main() -> None:
uvicorn.run("app.main:app", host="0.0.0.0", port=8000, reload=True)
if __name__ == "__main__":
main()

本地运行
# 切换到代码目录下,项目中的 \.venv\Lib\site-packages\uvicorn 运行
# 运行命令等同于 外层 main.py 中的代码
D:\OpenSource\Python\DeployDemo>uv run uvicorn app.main:app --reload --host 0.0.0.0 --port 8001
INFO: Will watch for changes in these directories: ['D:\\OpenSource\\Python\\DeployDemo']
INFO: Uvicorn running on http://0.0.0.0:8001 (Press CTRL+C to quit)
INFO: Started reloader process [78068] using WatchFiles
INFO: Started server process [76700]
INFO: Waiting for application startup.
INFO: Application startup complete.
-
uvicorn : ASGI(Asynchronous Server Gateway Interface)服务器,用于构建异步 Web 服务,可以理解成 Tomcat、IIS 类似的产品
-
app.main:app : 前面是文件路径,app.main app目录下的main 文件 , 后面的 app是变量名, FastAPI 的实例,
app = FastAPI() -
--reload : 选项的作用是在代码发生变化时自动重新加载应用,以便进行开发和调试。不建议在生产环境中使用,因为它会导致应用在每次请求时重新加载代码,降低性能。
-
--host 0.0.0.0 : 监听本机上的所有网络接,比如多个网卡,如果改成 host 172.16.27.88,那只能通过这个IP地址访问,不能用别的地址访问
-
--port 8001 : 指定端口运行

项目部署
服务器环境安装
安装 uv:
bash
curl -LsSf https://astral.sh/uv/install.sh | sh
source ~/.local/bin/env
uv --version
创建目录:
bash
# APP 放程序文件
# data 放数据库文件如 SQLite
# config 放配置文件,如:.env
sudo mkdir -p /opt/deploydemo/{app,data,config}
sudo chown -R $USER:$USER /opt/deploydemo
cd /opt/deploydemo
把以下2个文件上传到 /opt/deploydemo:
text
pyproject.toml
uv.lock
安装 Python 3.12 和依赖:
bash
uv python install 3.12
# 同步操作确保所有项目依赖项已安装并与锁文件(lockfile)保持一致:https://uv.oaix.tech/reference/cli/sync/
# 只有 `pyproject.toml` 或 `uv.lock` 发生变化时,才需要额外执行
uv sync --frozen --no-dev --no-install-project
本地生成发布包
思路: 建一个目录,将源码复制进去,进行编译,编译完成后,删除源代码文件(*.py) ,保留编译后的文件(*.pyc)
下面这个是AI生成的方式,感觉有点繁琐应该还有其它更方便的方式
在 Windows 项目目录执行(要使用 Windows PowerShell 执行):
Copy 源代码
cd D:\OpenSource\Python\DeployDemo
powershell
Remove-Item -Recurse -Force .\dist\release -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force .\dist\release
Copy-Item .\app .\dist\release\app -Recurse
第1行
powershell
Remove-Item -Recurse -Force .\dist\release -ErrorAction SilentlyContinue
- 作用 :删除
.\dist\release这个文件夹及其所有子内容(递归删除)。 - 参数 :
-Recurse:递归删除所有子文件夹和文件。-Force:强制删除,包括只读文件或隐藏文件,不会因为权限问询而中断。-ErrorAction SilentlyContinue:静默忽略错误------如果该目录本来就不存在,不会报错,脚本继续执行。
- 意图:确保该目录被彻底清除,避免旧文件残留。
第2行
powershell
New-Item -ItemType Directory -Force .\dist\release
- 作用 :创建一个新的目录
.\dist\release。 - 参数 :
-ItemType Directory:指明要创建的是目录,而非文件。-Force:即使父目录(dist)不存在,也会自动创建;如果目标目录已存在(虽然第1步已删除,但保险),也不会报错。
- 意图:准备好一个干净的、空的目标文件夹,用于存放新版本的发布文件。
第3行
powershell
Copy-Item .\app .\dist\release\app -Recurse
- 作用 :将
.\app文件夹(及其所有子内容)完整复制到.\dist\release\app下。 - 参数 :
-Recurse:递归复制所有子文件夹和文件。
- 意图 :把最新的应用程序代码(位于
app目录)放入发布目录中,形成待打包或部署的完整内容。
这三行组合起来,完成了一次 "干净的重新发布" 操作:
- 删除旧的发布包(如果存在)。
- 新建空的发布目录。
- 将当前项目的
app文件夹完整复制进去。

删除旧缓存并编译:
powershell
# 删除 __pycache__
Get-ChildItem .\dist\release -Directory -Recurse -Filter __pycache__ | Remove-Item -Recurse -Force
# 编译
# -q(quiet):静默模式。执行时不输出成功编译的文件名,只在发生错误时才报错,保持终端输出整洁
# -b(bytes):不使用 __pycache__ 子目录。默认情况下编译后的 .pyc 文件会放在 __pycache__ 文件夹里,加上 -b 后,.pyc 文件会直接生成在和 .py 源文件相同的目录下(即 app 文件夹内)
# .\dist\release\app:目标路径,即当前目录下的 dist/release/app 文件夹。compileall 会递归遍历该文件夹下的所有子目录,编译其中所有的 .py 文件。
uv run python -m compileall -q -b .\dist\release\app
# 删除 .py 源代码文件
Get-ChildItem .\dist\release -File -Recurse -Filter *.py | Remove-Item -Force
检查发布包中没有 .py:
powershell
Get-ChildItem .\dist\release -Recurse

服务器部署
将编译后的文件传到服务器的 app 目录下

服务器运行
bash
cd /opt/deploydemo
PYTHONPATH=/opt/deploydemo/app \
.venv/bin/uvicorn app.main:app \
--host 0.0.0.0 \
--port 8001
访问:
text
[vipsoft@host ~]$ curl http://127.0.0.1:8001
{"message":"Hello, FastAPI with UV!2026-07-22 15:44:48"}
配置系统服务
创建 sudo vi /etc/systemd/system/deploydemo.service:
ini
[Unit]
Description=DeployDemo FastAPI Service
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/deploydemo
#EnvironmentFile=/opt/deploydemo/config/.env
Environment="PYTHONPATH=/opt/deploydemo/app"
Environment="PATH=/opt/deploydemo/.venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
ExecStart=/opt/deploydemo/.venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8001
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
启动并设置开机启动:
bash
# 重新加载 systemd 的守护进程配置。
sudo systemctl daemon-reload
# 设为开机自启 并且 立即启动服务
sudo systemctl enable --now deploydemo
# 查看服务状态
sudo systemctl status deploydemo
# 重启服务
sudo systemctl restart deploydemo
# 停止服务
sudo systemctl stop deploydemo
# 立即停止当前运行的服务
sudo systemctl stop deploydemo
# 取消开机自动启动
sudo systemctl disable deploydemo
# 删除服务配置文件(彻底删除)删除文件后,必须让 systemd 刷新一下,让它"忘记"这个服务的存在
sudo rm /etc/systemd/system/deploydemo.service
查看日志:
bash
# 显示最近 100 行并持续跟踪
sudo journalctl -u deploydemo -n 100 -f
以后更新
只需要重新生成并上传 .pyc 压缩包,然后:
bash
# 文件不多,直接 WinSCP 工具上传,比较舒服
cd /opt/deploydemo
rm -rf app/deploydemo
tar -xzf deploydemo-app-0.1.1.tar.gz -C app
sudo systemctl restart deploydemo
只有 pyproject.toml 或 uv.lock 发生变化时,才需要额外执行:
bash
uv sync --frozen --no-dev --no-install-project