使用 Ollama 和 FastAPI 部署 Python AI 应用
一个在本地构建的 AI 项目,可能使用了像 Ollama 和 FastAPI 这样的库,最终需要部署到服务器上,以便更广泛地访问或实现可靠的 24/7 运行。本文档详细介绍了将这样一个基于 Python 的 AI 应用部署到 Linux 服务器上的常用流程。
这些步骤涵盖了连接到服务器、设置环境、管理 AI 模型、手动运行应用程序进行测试,以及使用 systemd
将其配置为可靠的后台服务运行。虽然这些步骤是基于部署使用 FastAPI 和 Ollama 的应用程序,但许多步骤展示了适用于各种 Python Web 应用程序的标准部署实践。
目录
通过 SSH 连接到服务器
安全外壳协议 (SSH) 是安全连接和管理远程 Linux 服务器的标准方法。这通常是部署过程的第一步。
生成 SSH 密钥
如果本地计算机上还没有 SSH 密钥对,需要生成一个:一个私钥(安全地保存在本地)和一个公钥(与服务器共享)。
在本地计算机上使用 ssh-keygen
命令。通常遵循提示即可。常见的算法是 ED25519 (推荐) 或 RSA。
# 使用 ED25519 的示例
ssh-keygen -t ed25519 -C "your_email@example.com"
# 遵循提示保存密钥(默认位置通常即可)并可选地设置密码。
在 macOS 和 Linux 上,密钥通常存储在 ~/.ssh
目录中。使用 ls -al ~/.ssh
检查:
id_ed25519
或id_rsa
:私钥。切勿共享此文件。id_ed25519.pub
或id_rsa.pub
:公钥。复制此文件的内容以提供给服务器管理员,或者如果有权限,自行添加。
服务器管理员(或自己)必须将公钥文件 (id_xxx.pub
) 的内容添加到服务器上目标用户主目录下的 ~/.ssh/authorized_keys
文件中。
连接
一旦公钥在服务器上被授权,就可以使用私钥建立连接:
ssh -i /path/to/your/private_key your_server_username@your_server_hostname_or_ip -p <ssh_port_number>
替换占位符:
/path/to/your/private_key
:私钥文件的路径(例如~/.ssh/id_ed25519
)。your_server_username
:远程服务器上的用户名。your_server_hostname_or_ip
:服务器的 IP 地址或可解析的主机名。<ssh_port_number>
:服务器上配置的 SSH 端口号(默认为 22,但为了安全通常会更改)。
注意: 替换像 <ssh_port_number>
这样的占位符时,省略尖括号 (<>
)。
首次连接到新服务器时,可能会看到一个主机真实性警告。这是正常的。输入 yes
继续。
The authenticity of host '...' can't be established.
... key fingerprint is ...
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
克隆项目代码库
成功建立 SSH 连接后,使用 Git 将项目代码获取到服务器上:
git clone your_repository_link
# 示例: git clone git@github.com:your_username/your_project.git
# 或: git clone https://github.com/your_username/your_project.git
将 your_repository_link
替换为 Git 代码库的实际 SSH 或 HTTPS URL。进入克隆的目录:cd your_project_directory_name
。
环境配置
在服务器上设置必要的软件和环境。
1. 安装 uv
(可选但推荐)
uv
是一个用 Rust 编写的非常快速的 Python 包安装器和解析器。与标准的 pip
相比,使用它可以显著加快依赖项的安装速度。它是可选的,但推荐使用。
# 使用 pip 安装 uv (确保 pip 可用)
pip install uv
# 或者,遵循官方说明:https://github.com/astral-sh/uv#installation
验证安装:
uv --version
# 预期输出类似于:uv x.y.z
2. 安装 Python
确保安装了兼容的 Python 版本(例如 3.8-3.11,检查项目的需求)。
如果安装了 uv
,可以用它来安装 Python:
# 将 3.x.y 替换为目标版本 (例如 3.10.13)
uv python install 3.x.y
# 遵循任何关于 PATH 更新的提示。
或者,使用系统的包管理器(在 Linux 服务器上很常见):
# Debian/Ubuntu 示例
sudo apt update
sudo apt install python3 python3-pip python3-venv
# CentOS/RHEL 示例
sudo yum update
sudo yum install python3 python3-pip
确认 Python 安装:
python3 --version
# 或有时只是 'python --version',取决于 PATH 设置
- 参考: 使用 uv 安装 Python
3. 安装软件依赖
项目依赖
项目应该有一个 requirements.txt
文件,列出了 Python 依赖项。导航到项目目录并安装它们。
使用 uv
(如果已安装,推荐):
cd /path/to/your/project
uv pip install -r requirements.txt
或者,使用 pip
(通常在虚拟环境中):
cd /path/to/your/project
# 最佳实践:首先创建并激活虚拟环境
# python3 -m venv venv
# source venv/bin/activate
pip install -r requirements.txt
# 或 pip3 install -r requirements.txt
注意: 将 /path/to/your/project
替换为克隆的代码库的实际绝对路径。
安装并运行 Ollama
Ollama 允许在本地运行大型语言模型。使用官方脚本在 Linux 服务器上安装它:
curl -fsSL https://ollama.com/install.sh | sh
为了初步测试,可以在后台手动启动 Ollama 服务器:
ollama serve & # '&' 符号使其仅在当前会话的后台运行。
注意: 对于生产环境,Ollama 应设置为 systemd
服务(安装脚本通常会自动执行此操作,请使用 systemctl status ollama
检查)。运行 ollama serve &
主要用于临时测试。
验证 Ollama 是否正在运行且可访问(此命令与正在运行的服务器通信):
ollama list
# 应该显示一个空列表或任何已经拉取/创建的模型。
# 如果命令挂起或出错,则表示服务器未正确运行。
4. 准备 AI 模型
将所需的 AI 模型文件放在服务器上。选择一个合适的位置,例如项目内的 models/
子目录或中央的 /opt/ai-models/
目录。
上传/下载模型
从 Hugging Face: 使用
huggingface-cli
(通过pip install huggingface_hub
安装):huggingface-cli download repo_id path/to/model.gguf --local-dir /path/on/server/models --local-dir-use-symlinks False # 替换 repo_id, 模型文件名, 和 /path/on/server/models
从本地机器: 使用
scp
(Secure Copy) 从开发机器上传模型。替换占位符。# 上传单个模型文件 scp /path/on/local/model.gguf your_server_username@your_server_hostname_or_ip:/path/on/server/models/ # 上传一个 Ollama Modelfile scp /path/on/local/Modelfile your_server_username@your_server_hostname_or_ip:/path/on/server/models/ # 递归上传整个目录 scp -r /path/on/local/model_directory your_server_username@your_server_hostname_or_ip:/path/on/server/models/
GGUF 合并: 如果模型被分割(例如
model-part-1.gguf
,model-part-2.gguf
),可能需要llama.cpp
工具来合并它们。克隆llama.cpp
,构建它,并使用llama-gguf-split
:# 假设 llama.cpp 工具已构建并在 PATH 中 # llama-gguf-split --merge input_part_*.gguf output_merged.gguf
使用 Ollama 构建模型
要将本地模型文件(如 .gguf
)与 Ollama 集成或定义自定义模型参数,请使用 Modelfile
。在服务器上,与基础模型文件 (.gguf
) 相同的目录中创建这个文本文件(例如 MyModelModelfile
)。
Modelfile
内容示例:
# 使用 FROM 指向同一目录中的模型文件的相对路径
FROM ./local_model_filename.gguf
# (可选) 定义提示模板
TEMPLATE """[INST] {{ .Prompt }} [/INST]"""
# (可选) 设置参数
PARAMETER temperature 0.7
PARAMETER top_k 40
# 根据需要添加其他参数 (停止序列等)
使用选定的名称构建模型并将其注册到 Ollama:
# 确保 Ollama 服务器正在运行
# 导航到包含 Modelfile 和 .gguf 文件的目录
cd /path/on/server/models/
# 创建模型 - 使用一个描述性的名称
ollama create your_custom_model_name -f MyModelModelfile
验证模型是否可用:ollama list
。
5. 设置环境变量
应用程序通常需要配置,如 API 密钥或数据库 URL,最好通过环境变量来管理。一种常见的方法是在项目的根目录中使用 .env
文件。切勿将 .env
文件提交到 Git。 将 .env
添加到.gitignore
文件中。
在项目根目录 (/path/to/your/project/.env
) 创建一个名为 .env
的文件,内容类似这样(替换占位符值):
# 示例 .env 文件内容
# 框架密钥 (生成一个强随机密钥)
SECRET_KEY=your_strong_random_secret_key
# 外部服务的 API 密钥
EXTERNAL_API_KEY=your_external_api_key_value
# 如果应用需要显式连接 Ollama 的配置
OLLAMA_HOST=http://127.0.0.1:11434
# 其他配置变量
DATABASE_URL=your_database_connection_string
Python 应用程序代码需要加载这些变量,通常使用像 python-dotenv
(pip install python-dotenv
) 这样的库。
6. 手动运行项目 (测试)
在设置服务之前,从终端手动运行应用程序,以确保它能正确启动和运行。
- 确保 Ollama 正在运行: 使用
ollama list
或systemctl status ollama
(如果作为服务安装) 来验证。 - 激活环境 (如果使用 venv):
source /path/to/your/project/venv/bin/activate
启动应用程序: 导航到项目根目录。使用像
uvicorn
这样的 ASGI 服务器来运行 FastAPI。替换占位符。cd /path/to/your/project # 使用 uvicorn 的示例命令 # 假设 FastAPI 应用实例在 'main.py' 中名为 'app' # 使用应用配置监听的端口 uvicorn main:app --host 0.0.0.0 --port <your_app_port> --reload
--host 0.0.0.0
:使应用可以从网络上的其他机器访问(确保防火墙规则允许<your_app_port>
端口的流量)。--port <your_app_port>
:应用程序将监听的端口(例如 8000)。--reload
:仅用于测试。 在代码更改时启用自动重新加载。对于生产部署,请移除此标志。
测试:
- API 端点: 使用
curl
、Postman 或 Insomnia 等工具发送请求(例如curl http://your_server_hostname_or_ip:<your_app_port>/api/some_endpoint
)。 - Web UI: 通过浏览器访问
http://your_server_hostname_or_ip:<your_app_port>
上的任何 Web 界面。 - 日志: 检查终端输出以查找错误。
- API 端点: 使用
使用 systemd
作为服务部署
在终端中手动运行应用程序不适合生产环境。systemd
是标准的 Linux 服务管理器,用于:
- 在服务器启动时自动启动应用程序。
- 在应用程序崩溃时自动重启。
- 将应用程序作为后台进程进行管理,并进行适当的日志记录。
创建服务文件
使用具有 sudo
权限的文本编辑器创建一个服务定义文件。使用描述性的名称(例如 your-app-name.service
)。
sudo nano /etc/systemd/system/your-app-name.service
将以下模板粘贴到文件中。请仔细阅读注释并替换所有占位符。
[Unit]
Description=My Python AI Application Service # 服务的描述性名称
After=network.target
# 如果应用严格要求 Ollama 首先运行 (并且 Ollama 是一个 systemd 服务), 取消注释:
# Wants=ollama.service
# After=network.target ollama.service
[Service]
# !!! 安全最佳实践:切勿以 ROOT 用户身份运行 !!!
# 为应用程序创建一个专用的非 root 用户。
# 将 'your_app_user' 替换为实际的用户名。确保此用户具有
# 读取项目文件和写入必要目录 (例如日志、上传文件) 的权限。
User=your_app_user
# Group=your_app_group # 通常与用户相同,如果需要则取消注释
# 将工作目录设置为项目的根目录绝对路径
WorkingDirectory=/path/to/your/project # !!! 务必替换此绝对路径 !!!
# 启动应用程序的命令。使用可执行文件 (uvicorn, gunicorn 等) 的绝对路径。
# 查找路径: 'which uvicorn' (以 your_app_user 身份, 可能在激活 venv 后)
# 或者可能是类似: /path/to/your/project/venv/bin/uvicorn 或 /home/your_app_user/.local/bin/uvicorn
# 调整 'main:app', host, port, 和其他参数。移除 --reload!
ExecStart=/path/to/executable/uvicorn main:app --host 0.0.0.0 --port <your_app_port> --forwarded-allow-ips='*' # !!! 务必替换路径, 应用, 端口并检查参数 !!!
# 重启策略
Restart=always
RestartSec=5 # 重启前等待 5 秒
# 日志记录: 将 stdout/stderr 重定向到 systemd journal
StandardOutput=journal
StandardError=journal
# 可选: 从 .env 文件加载环境变量
# 确保路径是绝对路径。权限必须允许 'your_app_user' 读取它。
# EnvironmentFile=/path/to/your/project/.env # !!! 务必替换此绝对路径 !!!
# 注意: 此处的变量可能会覆盖系统范围或用户特定的环境设置。
[Install]
WantedBy=multi-user.target # 在正常系统启动期间启动服务
需要验证的关键占位符和设置:
Description
:一个清晰的名称。User
/Group
:必须更改为一个专用的非 root 用户 (your_app_user
)。确保此用户的文件/目录权限正确。WorkingDirectory
:项目根目录的绝对路径。ExecStart
:uvicorn
(或其他 WSGI/ASGI 服务器)可执行文件的绝对路径。如果使用虚拟环境,通常在venv/bin/
内。main:app
必须与 Python 文件和 FastAPI/Flask 应用实例匹配。<your_app_port>
、--host
和其他参数必须适合生产环境。移除--reload
。- 如果位于反向代理(如 Nginx 或 Apache)之后,可能需要
--forwarded-allow-ips='*'
。根据安全需要进行调整。
- (可选)
EnvironmentFile
:如果使用,指向.env
文件的绝对路径。
保存文件并退出编辑器(在 nano
中按 Ctrl+X
,然后按 Y
,然后按 Enter
)。
启用并启动服务
使用 systemctl
管理新服务:
重新加载
systemd
配置: 使其知道新文件。sudo systemctl daemon-reload
启用服务: 使其在启动时自动运行。使用创建的相同文件名。
sudo systemctl enable your-app-name.service
启动服务: 立即运行它。
sudo systemctl start your-app-name.service
检查状态: 验证它是否正在运行。
sudo systemctl status your-app-name.service
查找
active (running)
。如果显示failed
或不是 active,请检查日志。查看日志: 查看应用程序输出并排查错误。
sudo journalctl -u your-app-name.service -f
-u your-app-name.service
:过滤特定服务的日志。-f
:实时跟踪日志(类似tail -f
)。按Ctrl+C
停止跟踪。要查看较早的日志,移除
-f
:sudo journalctl -u your-app-name.service --since "1 hour ago"
AI 应用程序现在应该作为托管的后台服务运行了。
常见问题 (FAQ)
设置或运行时遇到的常见问题。
环境配置问题
SSH 连接/输入缓慢:
- 原因: 网络延迟高。
解决方案: 使用
-C
标志启用 SSH 压缩:ssh -C your_server_username@your_server_hostname_or_ip ...
运行时问题
缺少环境变量: 出现类似
KeyError: 'SECRET_KEY'
或ValueError: Required setting X not found
的错误。- 原因: 应用程序需要的环境变量在其运行上下文中未定义。
解决方案: 确保所需变量已设置,可以通过以下方式之一:
- 在
.env
文件中,并且应用程序使用python-dotenv
加载它。 - 或者在
systemd
服务文件中使用Environment="VAR_NAME=value"
指令(对于许多变量不太常见)或EnvironmentFile=
指令指向.env
文件。验证systemd
服务用户是否有权限读取EnvironmentFile
。更改后重新加载 (daemon-reload
) 并重启服务。
- 在
Ollama 运行时错误:
Error: llama runner process has terminated: exit status X
.- 原因: 多种多样。可能是 Ollama 的 bug、模型所需的 RAM/VRAM 不足、模型文件损坏或不兼容。
解决方案:
- 检查 Ollama 的日志:
journalctl -u ollama.service
(如果作为 systemd 服务运行)。 - 在 Ollama GitHub Issues 中搜索具体的错误消息或退出状态码。
- 确保服务器满足模型的资源需求。
- 尝试不同的(可能更小的)模型来隔离问题。
- 考虑尝试不同的 Ollama 版本(降级或升级),检查兼容性说明。
- 检查 Ollama 的日志:
Ollama 响应缓慢/无响应/推理时间长:
- 原因 1: 网络代理干扰。
解决方案 1: 如果手动运行 Ollama 或配置其环境,尝试取消设置代理变量:unset http_proxy https_proxy NO_PROXY
。对于systemd
服务,可能需要在系统范围或服务环境设置中配置/禁用代理。 - 原因 2: 硬件资源不足 (CPU, RAM, VRAM 如果使用 GPU)。
- 解决方案 2: 验证模型需求与服务器规格。监控资源使用情况 (
htop
,nvidia-smi
如果有 GPU)。如果需要,使用更小的模型或升级硬件。
- 原因 1: 网络代理干扰。
ollama serve
失败:端口已被占用: 出现类似listen tcp 127.0.0.1:11434: bind: address already in use
的错误。- 原因: 另一个进程(很可能是另一个 Ollama 实例或配置错误的应用程序)正在使用端口 11434。
解决方案: 停止冲突的进程。
- 如果 Ollama 是一个服务:
sudo systemctl stop ollama
。 手动查找并终止:
sudo lsof -i :11434 # 查找使用该端口的 PID sudo kill <PID> # 将 <PID> 替换为找到的进程 ID # 或者更强制地 (谨慎使用): # sudo pkill ollama # sudo killall ollama
- 停止冲突后,再次尝试启动服务/Ollama。
- 如果 Ollama 是一个服务:
结论
部署 Python AI 应用涉及仔细的环境配置、依赖管理、模型处理和健壮的进程管理。像 uv
(提高速度)、Ollama (本地模型服务)、FastAPI (提供 API) 和 systemd
(可靠的服务操作) 这样的工具提供了坚实的基础。请记住,通过使用非 root 用户并通过环境变量适当地管理密钥来优先考虑安全性。通过遵循这些步骤并根据具体项目需求进行调整,可以成功部署应用程序以实现持续运行。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。