使用 Ollama 和 FastAPI 部署 Python AI 應(yīng)用
一個(gè)在本地構(gòu)建的 AI 項(xiàng)目,可能使用了像 Ollama 和 FastAPI 這樣的庫(kù),最終需要部署到服務(wù)器上,以便更廣泛地訪問(wèn)或?qū)崿F(xiàn)可靠的 24/7 運(yùn)行。本文檔詳細(xì)介紹了將這樣一個(gè)基于 Python 的 AI 應(yīng)用部署到 Linux 服務(wù)器上的常用流程。
這些步驟涵蓋了連接到服務(wù)器、設(shè)置環(huán)境、管理 AI 模型、手動(dòng)運(yùn)行應(yīng)用程序進(jìn)行測(cè)試,以及使用 systemd 將其配置為可靠的后臺(tái)服務(wù)運(yùn)行。雖然這些步驟是基于部署使用 FastAPI 和 Ollama 的應(yīng)用程序,但許多步驟展示了適用于各種 Python Web 應(yīng)用程序的標(biāo)準(zhǔn)部署實(shí)踐。
目錄
- 案例研究:使用 Ollama 和 FastAPI 部署 Python AI 應(yīng)用
通過(guò) SSH 連接到服務(wù)器
安全外殼協(xié)議 (SSH) 是安全連接和管理遠(yuǎn)程 Linux 服務(wù)器的標(biāo)準(zhǔn)方法。這通常是部署過(guò)程的第一步。
生成 SSH 密鑰
如果本地計(jì)算機(jī)上還沒(méi)有 SSH 密鑰對(duì),需要生成一個(gè):一個(gè)私鑰(安全地保存在本地)和一個(gè)公鑰(與服務(wù)器共享)。
在本地計(jì)算機(jī)上使用 ssh-keygen 命令。通常遵循提示即可。常見(jiàn)的算法是 ED25519 (推薦) 或 RSA。
# 使用 ED25519 的示例
ssh-keygen -t ed25519 -C "your_email@example.com"
# 遵循提示保存密鑰(默認(rèn)位置通常即可)并可選地設(shè)置密碼。
在 macOS 和 Linux 上,密鑰通常存儲(chǔ)在 ~/.ssh 目錄中。使用 ls -al ~/.ssh 檢查:
-
id_ed25519或id_rsa:私鑰。切勿共享此文件。 -
id_ed25519.pub或id_rsa.pub:公鑰。復(fù)制此文件的內(nèi)容以提供給服務(wù)器管理員,或者如果有權(quán)限,自行添加。
服務(wù)器管理員(或自己)必須將公鑰文件 (id_xxx.pub) 的內(nèi)容添加到服務(wù)器上目標(biāo)用戶主目錄下的 ~/.ssh/authorized_keys 文件中。
連接
一旦公鑰在服務(wù)器上被授權(quán),就可以使用私鑰建立連接:
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:遠(yuǎn)程服務(wù)器上的用戶名。 -
your_server_hostname_or_ip:服務(wù)器的 IP 地址或可解析的主機(jī)名。 -
<ssh_port_number>:服務(wù)器上配置的 SSH 端口號(hào)(默認(rèn)為 22,但為了安全通常會(huì)更改)。
注意: 替換像 <ssh_port_number> 這樣的占位符時(shí),省略尖括號(hào) (<>)。
首次連接到新服務(wù)器時(shí),可能會(huì)看到一個(gè)主機(jī)真實(shí)性警告。這是正常的。輸入 yes 繼續(xù)。
The authenticity of host '...' can't be established.
... key fingerprint is ...
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
克隆項(xiàng)目代碼庫(kù)
成功建立 SSH 連接后,使用 Git 將項(xiàng)目代碼獲取到服務(wù)器上:
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 代碼庫(kù)的實(shí)際 SSH 或 HTTPS URL。進(jìn)入克隆的目錄:cd your_project_directory_name。
環(huán)境配置
在服務(wù)器上設(shè)置必要的軟件和環(huán)境。
1. 安裝 uv (可選但推薦)
uv 是一個(gè)用 Rust 編寫(xiě)的非??焖俚?Python 包安裝器和解析器。與標(biāo)準(zhǔn)的 pip 相比,使用它可以顯著加快依賴項(xiàng)的安裝速度。它是可選的,但推薦使用。
# 使用 pip 安裝 uv (確保 pip 可用)
pip install uv
# 或者,遵循官方說(shuō)明:https://github.com/astral-sh/uv#installation
驗(yàn)證安裝:
uv --version
# 預(yù)期輸出類(lèi)似于:uv x.y.z
2. 安裝 Python
確保安裝了兼容的 Python 版本(例如 3.8-3.11,檢查項(xiàng)目的需求)。
如果安裝了 uv,可以用它來(lái)安裝 Python:
# 將 3.x.y 替換為目標(biāo)版本 (例如 3.10.13)
uv python install 3.x.y
# 遵循任何關(guān)于 PATH 更新的提示。
或者,使用系統(tǒng)的包管理器(在 Linux 服務(wù)器上很常見(jiàn)):
# Debian/Ubuntu 示例
sudo apt update
sudo apt install python3 python3-pip python3-venv
# CentOS/RHEL 示例
sudo yum update
sudo yum install python3 python3-pip
確認(rèn) Python 安裝:
python3 --version
# 或有時(shí)只是 'python --version',取決于 PATH 設(shè)置
- 參考: 使用 uv 安裝 Python
3. 安裝軟件依賴
項(xiàng)目依賴
項(xiàng)目應(yīng)該有一個(gè) requirements.txt 文件,列出了 Python 依賴項(xiàng)。導(dǎo)航到項(xiàng)目目錄并安裝它們。
使用 uv (如果已安裝,推薦):
cd /path/to/your/project
uv pip install -r requirements.txt
或者,使用 pip (通常在虛擬環(huán)境中):
cd /path/to/your/project
# 最佳實(shí)踐:首先創(chuàng)建并激活虛擬環(huán)境
# python3 -m venv venv
# source venv/bin/activate
pip install -r requirements.txt
# 或 pip3 install -r requirements.txt
注意: 將 /path/to/your/project 替換為克隆的代碼庫(kù)的實(shí)際絕對(duì)路徑。
安裝并運(yùn)行 Ollama
Ollama 允許在本地運(yùn)行大型語(yǔ)言模型。使用官方腳本在 Linux 服務(wù)器上安裝它:
curl -fsSL https://ollama.com/install.sh | sh
為了初步測(cè)試,可以在后臺(tái)手動(dòng)啟動(dòng) Ollama 服務(wù)器:
ollama serve & # '&' 符號(hào)使其僅在當(dāng)前會(huì)話的后臺(tái)運(yùn)行。
注意: 對(duì)于生產(chǎn)環(huán)境,Ollama 應(yīng)設(shè)置為 systemd 服務(wù)(安裝腳本通常會(huì)自動(dòng)執(zhí)行此操作,請(qǐng)使用 systemctl status ollama 檢查)。運(yùn)行 ollama serve & 主要用于臨時(shí)測(cè)試。
驗(yàn)證 Ollama 是否正在運(yùn)行且可訪問(wèn)(此命令與正在運(yùn)行的服務(wù)器通信):
ollama list
# 應(yīng)該顯示一個(gè)空列表或任何已經(jīng)拉取/創(chuàng)建的模型。
# 如果命令掛起或出錯(cuò),則表示服務(wù)器未正確運(yùn)行。
4. 準(zhǔn)備 AI 模型
將所需的 AI 模型文件放在服務(wù)器上。選擇一個(gè)合適的位置,例如項(xiàng)目?jī)?nèi)的 models/ 子目錄或中央的 /opt/ai-models/ 目錄。
上傳/下載模型
-
從 Hugging Face: 使用
huggingface-cli(通過(guò)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 -
從本地機(jī)器: 使用
scp(Secure Copy) 從開(kāi)發(fā)機(jī)器上傳模型。替換占位符。# 上傳單個(gè)模型文件 scp /path/on/local/model.gguf your_server_username@your_server_hostname_or_ip:/path/on/server/models/ # 上傳一個(gè) Ollama Modelfile scp /path/on/local/Modelfile your_server_username@your_server_hostname_or_ip:/path/on/server/models/ # 遞歸上傳整個(gè)目錄 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工具來(lái)合并它們。克隆llama.cpp,構(gòu)建它,并使用llama-gguf-split:# 假設(shè) llama.cpp 工具已構(gòu)建并在 PATH 中 # llama-gguf-split --merge input_part_*.gguf output_merged.gguf
使用 Ollama 構(gòu)建模型
要將本地模型文件(如 .gguf)與 Ollama 集成或定義自定義模型參數(shù),請(qǐng)使用 Modelfile。在服務(wù)器上,與基礎(chǔ)模型文件 (.gguf) 相同的目錄中創(chuàng)建這個(gè)文本文件(例如 MyModelModelfile)。
Modelfile 內(nèi)容示例:
# 使用 FROM 指向同一目錄中的模型文件的相對(duì)路徑
FROM ./local_model_filename.gguf
# (可選) 定義提示模板
TEMPLATE """[INST] {{ .Prompt }} [/INST]"""
# (可選) 設(shè)置參數(shù)
PARAMETER temperature 0.7
PARAMETER top_k 40
# 根據(jù)需要添加其他參數(shù) (停止序列等)
使用選定的名稱構(gòu)建模型并將其注冊(cè)到 Ollama:
# 確保 Ollama 服務(wù)器正在運(yùn)行
# 導(dǎo)航到包含 Modelfile 和 .gguf 文件的目錄
cd /path/on/server/models/
# 創(chuàng)建模型 - 使用一個(gè)描述性的名稱
ollama create your_custom_model_name -f MyModelModelfile
驗(yàn)證模型是否可用:ollama list。
5. 設(shè)置環(huán)境變量
應(yīng)用程序通常需要配置,如 API 密鑰或數(shù)據(jù)庫(kù) URL,最好通過(guò)環(huán)境變量來(lái)管理。一種常見(jiàn)的方法是在項(xiàng)目的根目錄中使用 .env 文件。切勿將 .env 文件提交到 Git。 將 .env 添加到.gitignore 文件中。
在項(xiàng)目根目錄 (/path/to/your/project/.env) 創(chuàng)建一個(gè)名為 .env 的文件,內(nèi)容類(lèi)似這樣(替換占位符值):
# 示例 .env 文件內(nèi)容
# 框架密鑰 (生成一個(gè)強(qiáng)隨機(jī)密鑰)
SECRET_KEY=your_strong_random_secret_key
# 外部服務(wù)的 API 密鑰
EXTERNAL_API_KEY=your_external_api_key_value
# 如果應(yīng)用需要顯式連接 Ollama 的配置
OLLAMA_HOST=http://127.0.0.1:11434
# 其他配置變量
DATABASE_URL=your_database_connection_string
Python 應(yīng)用程序代碼需要加載這些變量,通常使用像 python-dotenv (pip install python-dotenv) 這樣的庫(kù)。
6. 手動(dòng)運(yùn)行項(xiàng)目 (測(cè)試)
在設(shè)置服務(wù)之前,從終端手動(dòng)運(yùn)行應(yīng)用程序,以確保它能正確啟動(dòng)和運(yùn)行。
確保 Ollama 正在運(yùn)行: 使用
ollama list或systemctl status ollama(如果作為服務(wù)安裝) 來(lái)驗(yàn)證。激活環(huán)境 (如果使用 venv):
source /path/to/your/project/venv/bin/activate-
啟動(dòng)應(yīng)用程序: 導(dǎo)航到項(xiàng)目根目錄。使用像
uvicorn這樣的 ASGI 服務(wù)器來(lái)運(yùn)行 FastAPI。替換占位符。cd /path/to/your/project # 使用 uvicorn 的示例命令 # 假設(shè) FastAPI 應(yīng)用實(shí)例在 'main.py' 中名為 'app' # 使用應(yīng)用配置監(jiān)聽(tīng)的端口 uvicorn main:app --host 0.0.0.0 --port <your_app_port> --reload-
--host 0.0.0.0:使應(yīng)用可以從網(wǎng)絡(luò)上的其他機(jī)器訪問(wèn)(確保防火墻規(guī)則允許<your_app_port>端口的流量)。 -
--port <your_app_port>:應(yīng)用程序?qū)⒈O(jiān)聽(tīng)的端口(例如 8000)。 -
--reload:僅用于測(cè)試。 在代碼更改時(shí)啟用自動(dòng)重新加載。對(duì)于生產(chǎn)部署,請(qǐng)移除此標(biāo)志。
-
-
測(cè)試:
-
API 端點(diǎn): 使用
curl、Postman 或 Insomnia 等工具發(fā)送請(qǐng)求(例如curl http://your_server_hostname_or_ip:<your_app_port>/api/some_endpoint)。 -
Web UI: 通過(guò)瀏覽器訪問(wèn)
http://your_server_hostname_or_ip:<your_app_port>上的任何 Web 界面。 - 日志: 檢查終端輸出以查找錯(cuò)誤。
-
API 端點(diǎn): 使用
使用 systemd 作為服務(wù)部署
在終端中手動(dòng)運(yùn)行應(yīng)用程序不適合生產(chǎn)環(huán)境。systemd 是標(biāo)準(zhǔn)的 Linux 服務(wù)管理器,用于:
- 在服務(wù)器啟動(dòng)時(shí)自動(dòng)啟動(dòng)應(yīng)用程序。
- 在應(yīng)用程序崩潰時(shí)自動(dòng)重啟。
- 將應(yīng)用程序作為后臺(tái)進(jìn)程進(jìn)行管理,并進(jìn)行適當(dāng)?shù)娜罩居涗洝?/li>
創(chuàng)建服務(wù)文件
使用具有 sudo 權(quán)限的文本編輯器創(chuàng)建一個(gè)服務(wù)定義文件。使用描述性的名稱(例如 your-app-name.service)。
sudo nano /etc/systemd/system/your-app-name.service
將以下模板粘貼到文件中。請(qǐng)仔細(xì)閱讀注釋并替換所有占位符。
[Unit]
Description=My Python AI Application Service # 服務(wù)的描述性名稱
After=network.target
# 如果應(yīng)用嚴(yán)格要求 Ollama 首先運(yùn)行 (并且 Ollama 是一個(gè) systemd 服務(wù)), 取消注釋:
# Wants=ollama.service
# After=network.target ollama.service
[Service]
# !!! 安全最佳實(shí)踐:切勿以 ROOT 用戶身份運(yùn)行 !!!
# 為應(yīng)用程序創(chuàng)建一個(gè)專(zhuān)用的非 root 用戶。
# 將 'your_app_user' 替換為實(shí)際的用戶名。確保此用戶具有
# 讀取項(xiàng)目文件和寫(xiě)入必要目錄 (例如日志、上傳文件) 的權(quán)限。
User=your_app_user
# Group=your_app_group # 通常與用戶相同,如果需要?jiǎng)t取消注釋
# 將工作目錄設(shè)置為項(xiàng)目的根目錄絕對(duì)路徑
WorkingDirectory=/path/to/your/project # !!! 務(wù)必替換此絕對(duì)路徑 !!!
# 啟動(dòng)應(yīng)用程序的命令。使用可執(zhí)行文件 (uvicorn, gunicorn 等) 的絕對(duì)路徑。
# 查找路徑: 'which uvicorn' (以 your_app_user 身份, 可能在激活 venv 后)
# 或者可能是類(lèi)似: /path/to/your/project/venv/bin/uvicorn 或 /home/your_app_user/.local/bin/uvicorn
# 調(diào)整 'main:app', host, port, 和其他參數(shù)。移除 --reload!
ExecStart=/path/to/executable/uvicorn main:app --host 0.0.0.0 --port <your_app_port> --forwarded-allow-ips='*' # !!! 務(wù)必替換路徑, 應(yīng)用, 端口并檢查參數(shù) !!!
# 重啟策略
Restart=always
RestartSec=5 # 重啟前等待 5 秒
# 日志記錄: 將 stdout/stderr 重定向到 systemd journal
StandardOutput=journal
StandardError=journal
# 可選: 從 .env 文件加載環(huán)境變量
# 確保路徑是絕對(duì)路徑。權(quán)限必須允許 'your_app_user' 讀取它。
# EnvironmentFile=/path/to/your/project/.env # !!! 務(wù)必替換此絕對(duì)路徑 !!!
# 注意: 此處的變量可能會(huì)覆蓋系統(tǒng)范圍或用戶特定的環(huán)境設(shè)置。
[Install]
WantedBy=multi-user.target # 在正常系統(tǒng)啟動(dòng)期間啟動(dòng)服務(wù)
需要驗(yàn)證的關(guān)鍵占位符和設(shè)置:
-
Description:一個(gè)清晰的名稱。 -
User/Group:必須更改為一個(gè)專(zhuān)用的非 root 用戶 (your_app_user)。確保此用戶的文件/目錄權(quán)限正確。 -
WorkingDirectory:項(xiàng)目根目錄的絕對(duì)路徑。 -
ExecStart:-
uvicorn(或其他 WSGI/ASGI 服務(wù)器)可執(zhí)行文件的絕對(duì)路徑。如果使用虛擬環(huán)境,通常在venv/bin/內(nèi)。 -
main:app必須與 Python 文件和 FastAPI/Flask 應(yīng)用實(shí)例匹配。 -
<your_app_port>、--host和其他參數(shù)必須適合生產(chǎn)環(huán)境。移除--reload。 - 如果位于反向代理(如 Nginx 或 Apache)之后,可能需要
--forwarded-allow-ips='*'。根據(jù)安全需要進(jìn)行調(diào)整。
-
- (可選)
EnvironmentFile:如果使用,指向.env文件的絕對(duì)路徑。
保存文件并退出編輯器(在 nano 中按 Ctrl+X,然后按 Y,然后按 Enter)。
啟用并啟動(dòng)服務(wù)
使用 systemctl 管理新服務(wù):
-
重新加載
systemd配置: 使其知道新文件。sudo systemctl daemon-reload -
啟用服務(wù): 使其在啟動(dòng)時(shí)自動(dòng)運(yùn)行。使用創(chuàng)建的相同文件名。
sudo systemctl enable your-app-name.service -
啟動(dòng)服務(wù): 立即運(yùn)行它。
sudo systemctl start your-app-name.service -
檢查狀態(tài): 驗(yàn)證它是否正在運(yùn)行。
查找sudo systemctl status your-app-name.serviceactive (running)。如果顯示failed或不是 active,請(qǐng)檢查日志。 -
查看日志: 查看應(yīng)用程序輸出并排查錯(cuò)誤。
sudo journalctl -u your-app-name.service -f-
-u your-app-name.service:過(guò)濾特定服務(wù)的日志。 -
-f:實(shí)時(shí)跟蹤日志(類(lèi)似tail -f)。按Ctrl+C停止跟蹤。 - 要查看較早的日志,移除
-f:sudo journalctl -u your-app-name.service --since "1 hour ago"
-
AI 應(yīng)用程序現(xiàn)在應(yīng)該作為托管的后臺(tái)服務(wù)運(yùn)行了。
常見(jiàn)問(wèn)題 (FAQ)
設(shè)置或運(yùn)行時(shí)遇到的常見(jiàn)問(wèn)題。
環(huán)境配置問(wèn)題
-
SSH 連接/輸入緩慢:
- 原因: 網(wǎng)絡(luò)延遲高。
-
解決方案: 使用
-C標(biāo)志啟用 SSH 壓縮:ssh -C your_server_username@your_server_hostname_or_ip ...
運(yùn)行時(shí)問(wèn)題
-
缺少環(huán)境變量: 出現(xiàn)類(lèi)似
KeyError: 'SECRET_KEY'或ValueError: Required setting X not found的錯(cuò)誤。- 原因: 應(yīng)用程序需要的環(huán)境變量在其運(yùn)行上下文中未定義。
-
解決方案: 確保所需變量已設(shè)置,可以通過(guò)以下方式之一:
- 在
.env文件中,并且應(yīng)用程序使用python-dotenv加載它。 - 或者在
systemd服務(wù)文件中使用Environment="VAR_NAME=value"指令(對(duì)于許多變量不太常見(jiàn))或EnvironmentFile=指令指向.env文件。驗(yàn)證systemd服務(wù)用戶是否有權(quán)限讀取EnvironmentFile。更改后重新加載 (daemon-reload) 并重啟服務(wù)。
- 在
-
Ollama 運(yùn)行時(shí)錯(cuò)誤:
Error: llama runner process has terminated: exit status X.- 原因: 多種多樣??赡苁?Ollama 的 bug、模型所需的 RAM/VRAM 不足、模型文件損壞或不兼容。
-
解決方案:
- 檢查 Ollama 的日志:
journalctl -u ollama.service(如果作為 systemd 服務(wù)運(yùn)行)。 - 在 Ollama GitHub Issues 中搜索具體的錯(cuò)誤消息或退出狀態(tài)碼。
- 確保服務(wù)器滿足模型的資源需求。
- 嘗試不同的(可能更小的)模型來(lái)隔離問(wèn)題。
- 考慮嘗試不同的 Ollama 版本(降級(jí)或升級(jí)),檢查兼容性說(shuō)明。
- 檢查 Ollama 的日志:
-
Ollama 響應(yīng)緩慢/無(wú)響應(yīng)/推理時(shí)間長(zhǎng):
-
原因 1: 網(wǎng)絡(luò)代理干擾。
解決方案 1: 如果手動(dòng)運(yùn)行 Ollama 或配置其環(huán)境,嘗試取消設(shè)置代理變量:unset http_proxy https_proxy NO_PROXY。對(duì)于systemd服務(wù),可能需要在系統(tǒng)范圍或服務(wù)環(huán)境設(shè)置中配置/禁用代理。 - 原因 2: 硬件資源不足 (CPU, RAM, VRAM 如果使用 GPU)。
-
解決方案 2: 驗(yàn)證模型需求與服務(wù)器規(guī)格。監(jiān)控資源使用情況 (
htop,nvidia-smi如果有 GPU)。如果需要,使用更小的模型或升級(jí)硬件。
-
原因 1: 網(wǎng)絡(luò)代理干擾。
-
ollama serve失?。憾丝谝驯徽加? 出現(xiàn)類(lèi)似listen tcp 127.0.0.1:11434: bind: address already in use的錯(cuò)誤。- 原因: 另一個(gè)進(jìn)程(很可能是另一個(gè) Ollama 實(shí)例或配置錯(cuò)誤的應(yīng)用程序)正在使用端口 11434。
-
解決方案: 停止沖突的進(jìn)程。
- 如果 Ollama 是一個(gè)服務(wù):
sudo systemctl stop ollama。 - 手動(dòng)查找并終止:
sudo lsof -i :11434 # 查找使用該端口的 PID sudo kill <PID> # 將 <PID> 替換為找到的進(jìn)程 ID # 或者更強(qiáng)制地 (謹(jǐn)慎使用): # sudo pkill ollama # sudo killall ollama- 停止沖突后,再次嘗試啟動(dòng)服務(wù)/Ollama。
- 如果 Ollama 是一個(gè)服務(wù):
結(jié)論
部署 Python AI 應(yīng)用涉及仔細(xì)的環(huán)境配置、依賴管理、模型處理和健壯的進(jìn)程管理。像 uv (提高速度)、Ollama (本地模型服務(wù))、FastAPI (提供 API) 和 systemd (可靠的服務(wù)操作) 這樣的工具提供了堅(jiān)實(shí)的基礎(chǔ)。請(qǐng)記住,通過(guò)使用非 root 用戶并通過(guò)環(huán)境變量適當(dāng)?shù)毓芾砻荑€來(lái)優(yōu)先考慮安全性。通過(guò)遵循這些步驟并根據(jù)具體項(xiàng)目需求進(jìn)行調(diào)整,可以成功部署應(yīng)用程序以實(shí)現(xiàn)持續(xù)運(yùn)行。