Python 项目部署(Linux)

目录

使用 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/:定义接口的输入输出格式,例如 ContractCreateContractResponse
  • 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 devfastapi run
  • python-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 目录)放入发布目录中,形成待打包或部署的完整内容。

这三行组合起来,完成了一次 "干净的重新发布" 操作:

  1. 删除旧的发布包(如果存在)。
  2. 新建空的发布目录。
  3. 将当前项目的 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.tomluv.lock 发生变化时,才需要额外执行:

bash 复制代码
uv sync --frozen --no-dev --no-install-project