一.导出依赖清单,将所有的包打到requirement.txt文件中
两种方式
1.整个环境的包都写进去,可能包含很多与项目无关的依赖
pip freeze > requirements.txt
只想记录项目真正用到的包,可以配合虚拟环境或使用 pipreqs 等工具
pip install pipreqs
# 默认生成 requirements.txt
pipreqs .
# 如文件已存在,强制覆盖
pipreqs . --force
二、为 Python 3.11 下载兼容的离线包(关键步骤!)
下载离线包注意你生产环境的系统和python的版本号
pip download -r requirements.txt -d ./packages --only-binary=:all: --platform manylinux_2_17_x86_64 --python-version 311 --abi cp311
这条命令会把 requirements.txt 里所有包 的「manylinux_2_17_x86_64 + CPython-3.11 ABI 」二进制 wheel 预先下载到
./packages目录,不做安装,留给后续离线/容器化部署使用
|----------------------------------|-------------------------------------------------------------------------------------------------|
| 命令 | 含义 |
| pip download | 仅拉包,不装包 |
| -r requirements.txt | 按文件批量下载 |
| -d ./packages | 保存目录(自动生成) |
| --only-binary=:all: | |---------------------------------------------| | 只要 wheel ,不要源码 tar.gz;缺 wheel 就直接报错 | |
| --platform manylinux_2_17_x86_64 | wheel 标签:glibc≥2.17 的 64-bit Linux;不会下 win32/macOS 版本 |
| --python-version 311 | 3.11.x |
| --abi cp311 | CPython 3.11 ABI(与 PyPy/abi3 区分) |
执行后 ./packages 里会出现类似文件
numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.whl
uvloop-0.21.0-cp311-cp311-manylinux_2_17_x86_64.whl
...
没有 .tar.gz,因此后续离线安装时无需编译,直接:
三、文件打包,上传服务器
将源代码+package目录打包上传到服务器,注意不要打包本地的环境目录/venv 目录
your_project/ ├── main.py # 你的应用主文件 ├── requirements.txt # 依赖清单 ├── packages/ # 存放所有.whl离线包的目录 └── ... # 其他项目文件
四、在服务器离线安装:
4.1 安装python环境,可以直接安装python3.11,也可以安装虚拟环境
4.2 使用本地包安装依赖
pip install --no-index --find-links=./packages -r requirements.txt
注意如果某个包没有打包到packages目录,类似以下出错:
出错ERROR: Could not find a version that satisfies the requirement uvloop>=0.15.1; (sys_platform != "win32" and (sys_platform != "cygwin" and platform_python_implementation != "PyPy")) and extra == "standard" (from uvicorn[standard]) (from versions: none)
ERROR: No matching distribution found for uvloop>=0.15.1; (sys_platform != "win32" and (sys_platform != "cygwin" and platform_python_implementation != "PyPy")) and extra == "standard"
如果服务器可以联网,可以直接下到packages目录中
pip3.11 download uvloop==0.21.0 -d ./packages
然后重新运行安装命令:
sudo pip3.11 install --no-index --find-links=./packages -r requirements.txt
4.3 运行程序
python3.11 -m uvicorn main:app --host 0.0.0.0 --port 8000
五、加一个系统启动脚本
#!/bin/bash
# ------------------------------------------------------------
# Uvicorn 一键启停脚本(CentOS + Python3.11)
# 放到项目根目录(/www/service)即可
# ------------------------------------------------------------
set -e
APP_DIR="/www/service"
PID_FILE="$APP_DIR/tool-service-uvicorn.pid"
LOG_FILE="$APP_DIR/run.log"
PYTHON="/usr/bin/python" # 改成你的 3.11 路径
HOST="0.0.0.0"
PORT="9099"
# 确保在项目根下
cd "$APP_DIR"
# 获取 pid(若文件存在且进程存活)
get_pid(){
[ -s "$PID_FILE" ] && echo $(cat "$PID_FILE") && return 0
return 1
}
# 启动
start(){
if get_pid >/dev/null; then
echo "uvicorn is already running ($(get_pid))"
return 0
fi
echo "Starting uvicorn ..."
nohup $PYTHON -m uvicorn app:app --host $HOST --port $PORT \
--access-log --use-colors >> "$LOG_FILE" 2>&1 &
echo $! > "$PID_FILE"
echo "started, pid=$(get_pid)"
}
# 停止
stop(){
PID=$(get_pid) || { echo "uvicorn not running"; return 0; }
echo "Stopping uvicorn ($PID) ..."
kill -TERM "$PID"
for i in {1..10}; do
kill -0 "$PID" >/dev/null 2>&1 || { rm -f "$PID_FILE"; echo "stopped"; return 0; }
sleep 1
done
echo "force kill ..."
kill -9 "$PID" && rm -f "$PID_FILE"
}
# 重启
restart(){
stop
sleep 1
start
}
# 状态
status(){
PID=$(get_pid) && echo "uvicorn is running, pid=$PID" || echo "uvicorn is stopped"
}
# 路由
case "$1" in
start) start ;;
stop) stop ;;
restart) restart ;;
status) status ;;
*) echo "Usage: $0 {start|stop|restart|status}"; exit 1 ;;
esac
操作命令
./uvicorn.sh start # 后台启动
./uvicorn.sh stop # 优雅停止
./uvicorn.sh restart # 先停后启
./uvicorn.sh status # 查看运行状态
tail -f run.log # 实时日志