LEEHPC 集群中部署 Open-WebUI + Ollama 大模型推理服务的完整记录
作者: LEEHPC 一粒海
一句话总结: 在 LEEHPC 集群 GPU 节点部署 Ollama + Open-WebUI,通过管理节点 Nginx 反向代理实现多用户统一入口访问大模型,含完整 systemd 配置、并发调优和踩坑记录。
核心结论:这套方案把 Ollama 推理服务和 Open-WebUI 都跑在 GPU 节点 gpu02 上,用户只通过管理节点 mgmt01 的 8088 端口访问,GPU 节点的真实地址不需要暴露。实测 4 张 RTX 4090D 的配置足够支撑 4 个用户的并发推理,下面记录具体配置步骤和踩过的坑。
测试环境硬件平台:GPU 节点 gpu02,4 × RTX 4090D操作系统:Rocky Linux(管理节点 mgmt01 + GPU 节点 gpu02)软件版本:Ollama(最新稳定版)、Open-WebUI(pip 安装)、Nginx本文由 LEEHPC 一粒海团队基于实际项目经验整理
整体架构是什么样的?
先说一下最终效果 —— 用户访问的链路是这样:
用户浏览器
↓
http://管理节点IP:8088
↓
管理节点 mgmt01 Nginx 反向代理
↓
GPU 节点 gpu02 Open-WebUI:8088
↓
gpu02 Ollama:11434
↓
GPU 本地大模型推理
用户最终只需要记住一个地址:
http://mgmt01_ip:8088
环境关键信息:
| 项目 | 配置 |
|---|---|
| 管理节点 | mgmt01 |
| GPU 节点 | gpu02 |
| Web 入口 | mgmt01:8088 |
| Open-WebUI 节点 | gpu02 |
| Open-WebUI 端口 | 8088 |
| Ollama 节点 | gpu02 |
| Ollama 端口 | 11434 |
| Python 环境 | /public/softwares/anaconda3/envs/python311 |
| Open-WebUI 数据目录 | /LLM/bin |
| Ollama 模型目录 | /LLM/models |
| GPU | 4 × RTX 4090D |
| 推荐访问方式 | http://mgmt01_ip:8088 |
基础目录怎么规划?
在 gpu02 上创建三个目录就够了:
mkdir -p /LLM/bin
mkdir -p /LLM/models
mkdir -p /LLM/src
用途分配如下:
| 目录 | 用途 |
|---|---|
/LLM/bin |
Open-WebUI 工作目录、数据库、日志 |
/LLM/models |
Ollama 模型存放目录 |
/LLM/src |
安装包、源码、临时文件目录 |
验证一下:
ls -ld /LLM/bin /LLM/models /LLM/src
Ollama 怎么安装?
以下操作都在 GPU 节点 gpu02 上执行。
能连外网的话,在线装最快
curl -fsSL https://ollama.com/install.sh | sh
装完确认路径和版本:
which ollama # 正常应该是 /usr/local/bin/ollama
ollama --version
外网太慢或者不能联网,离线装
如果服务器访问不了外网,可以在能联网的机器上下载 Ollama Linux 安装包再传上去。
下载地址:https://github.com/ollama/ollama/releases
可以用迅雷拖下来,文件名一般是 ollama-linux-amd64.tar.zst。把包丢到 /LLM/src:
cd /LLM/src
zstd -d ollama-linux-amd64.tar.zst && tar -xvf ollama-linux-amd64.tar
确认二进制文件:
ls -l /LLM/bin/ollama
没执行权限的话加上:
chmod +x /LLM/bin/ollama
建个软链接到系统目录,后面用着方便:
ln -s /LLM/bin/ollama /usr/local/bin/ollama
不同版本 Ollama 离线包结构可能略有不同,以实际下载的包为准。
Ollama 的 systemd 服务和关键参数怎么配?
在 gpu02 上创建或编辑服务文件:
vim /etc/systemd/system/ollama.service
我们实际用的配置:
[Unit]
Description=Ollama LLM Service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
Group=root
ExecStart=/LLM/bin/ollama serve
Restart=always
RestartSec=5
# 监听所有接口,允许主节点访问
Environment="OLLAMA_HOST=0.0.0.0:11434"
# 模型别放默认目录,独立挂一个大容量分区
Environment="OLLAMA_MODELS=/LLM/models"
# 日志重定向(可选)
StandardOutput=append:/LLM/bin/nohup.out
StandardError=append:/LLM/bin/nohup.out
# 并发和性能相关 —— 下面这几个参数后面会细说
Environment="OLLAMA_NUM_PARALLEL=4"
Environment="OLLAMA_MAX_LOADED_MODELS=1"
Environment="OLLAMA_FLASH_ATTENTION=1"
[Install]
WantedBy=multi-user.target
加载并启动:
systemctl daemon-reload
systemctl enable --now ollama
systemctl status ollama
怎么确认 Ollama 跑起来了?
看端口
ss -lntp | grep 11434
正常应该输出类似:
LISTEN 0 4096 0.0.0.0:11434 users:(("ollama",pid=xxxx,fd=xx))
API 测试一把
curl http://127.0.0.1:11434/api/tags
服务正常的话会返回 JSON。
看日志
journalctl -u ollama -f
模型怎么下载和验证?
在线拉模型
gpu02 能访问外网的话,直接:
ollama pull nomic-embed-text
大语言模型,比如 gemma4:
ollama pull gemma4:31b-it-bf16
具体模型名以实际使用的为准,可查看:https://ollama.com/library/gemma4
确认已安装的模型:
ollama list
测一下推理能不能跑
ollama run gemma4:31b-it-bf16
进去后输入:
你好,请简单介绍一下你自己。
能正常回复就说明推理服务没问题。
Embedding 模型也要装一下
如果 Open-WebUI 打算用 RAG 或文档知识库功能,建议装上 embedding 模型:
ollama pull nomic-embed-text
Open-WebUI 里对应的环境变量:
Environment="RAG_EMBEDDING_ENGINE=ollama"
Environment="RAG_EMBEDDING_MODEL=nomic-embed-text"
Python 环境怎么准备?
Open-WebUI 要求 Python 3.11 或 3.12。我们用 Conda 创建环境:
source /public/softwares/anaconda3/bin/activate
conda env create -n python311 python=3.11
激活并升级 pip:
conda activate python311
python -m pip install --upgrade pip
Open-WebUI 怎么安装?
以下都在 gpu02 上执行,确保已经进入 python311 环境。
在线安装
默认源慢的话走国内镜像:
pip install open-webui -i https://pypi.tuna.tsinghua.edu.cn/simple
或者:
pip install open-webui -i https://mirrors.aliyun.com/pypi/simple/
确认一下装好了
which open-webui
# 正常输出:/public/softwares/anaconda3/envs/python311/bin/open-webui
open-webui --help
配 systemd 之前,手动启动测一下
建议先手动跑一遍,确认没问题再写成 systemd 服务。在 gpu02 上:
cd /LLM/bin
设环境变量:
export OLLAMA_BASE_URL=http://localhost:11434
export DATA_DIR=/LLM/bin
export HF_DATASETS_OFFLINE=1
export TRANSFORMERS_OFFLINE=1
export HF_HUB_OFFLINE=1
export ENABLE_SIGNUP=true
启动,指定 8088 端口:
/public/softwares/anaconda3/envs/python311/bin/open-webui serve --host 0.0.0.0 --port 8088
另开一个终端测试:
curl -I http://127.0.0.1:8088
返回 HTTP/1.1 200 OK 或 HTTP/1.1 307 Temporary Redirect 都算正常。
手动测试没问题后 Ctrl + C 停掉,开始配 systemd。
Open-WebUI 的 systemd 服务怎么配?
在 gpu02 上创建服务文件:
vim /etc/systemd/system/open-webui.service
当前推荐配置(做了不少参数调优):
[Unit]
Description=Open WebUI for Ollama
After=network-online.target ollama.service
Wants=network-online.target
[Service]
Type=simple
User=root
Group=root
WorkingDirectory=/LLM/bin
Environment="PATH=/public/softwares/anaconda3/envs/python311/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
Environment="OLLAMA_BASE_URL=http://localhost:11434"
Environment="DATA_DIR=/LLM/bin"
# 用户注册控制 —— 初始开一下,注册完记得关
Environment="ENABLE_SIGNUP=true"
Environment="DEFAULT_USER_ROLE=user"
# 离线模式,避免启动时去拉 HuggingFace 的东西
Environment="HF_DATASETS_OFFLINE=1"
Environment="TRANSFORMERS_OFFLINE=1"
Environment="HF_HUB_OFFLINE=1"
# 性能
Environment="WEBUI_WORKERS=4"
# 联网搜索配置(需要搭 searxng 或购买 API 才能用)
Environment="ENABLE_RAG_WEB_SEARCH=true"
Environment="RAG_WEB_SEARCH_ENGINE=searxng"
Environment="SEARXNG_QUERY_URL=http://localhost:8080/search?q=<query>&format=json&language=zh"
Environment="RAG_WEB_SEARCH_RESULT_COUNT=5"
Environment="RAG_WEB_SEARCH_CONCURRENT_REQUESTS=10"
# 文档解析与 RAG
Environment="RAG_EMBEDDING_ENGINE=ollama"
Environment="RAG_EMBEDDING_MODEL=nomic-embed-text"
Environment="CONTENT_EXTRACTION_ENGINE=default"
Environment="MAX_UPLOAD_SIZE=100"
Environment="ENABLE_RAG_LOCAL_WEB_FETCH=true"
# 如果要启用 Apache Tika 做文档解析,取消下面两行注释
# Environment="CONTENT_EXTRACTION_ENGINE=tika"
# Environment="TIKA_SERVER_URL=http://localhost:9998"
ExecStart=/public/softwares/anaconda3/envs/python311/bin/open-webui serve --host 0.0.0.0 --port 8088
Restart=always
RestartSec=10
StandardOutput=append:/LLM/bin/webui.log
StandardError=append:/LLM/bin/webui.log
[Install]
WantedBy=multi-user.target
加载并启动:
systemctl daemon-reload
systemctl enable --now open-webui
systemctl status open-webui
重启:
systemctl restart open-webui
怎么验证 Open-WebUI 服务?
在 gpu02 上:
ss -lntp | grep 8088
# 正常:LISTEN 0 2048 0.0.0.0:8088
curl -I http://127.0.0.1:8088
# 或
curl -I http://gpu02:8088
返回 200 或 307 都算正常。看日志:
tail -f /LLM/bin/webui.log
管理节点 Nginx 反向代理怎么配?
Open-WebUI 部署在 gpu02,但用户要从管理节点 mgmt01 的 8088 端口访问。需要在 mgmt01 上配 Nginx 反代。
装 Nginx
Rocky Linux 8/9:
dnf install -y nginx
反代配置文件
在 mgmt01 上创建:
vim /etc/nginx/conf.d/open-webui.conf
server {
listen 8088;
server_name _;
client_max_body_size 10G;
access_log /var/log/nginx/open-webui.access.log;
error_log /var/log/nginx/open-webui.error.log;
location / {
proxy_pass http://gpu02:8088;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket 支持,Open-WebUI 的流式对话依赖这个
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 关闭缓冲,大模型流式输出不能缓冲,不然用户看到的是卡住不动的
proxy_buffering off;
proxy_request_buffering off;
proxy_cache off;
# 超时设长一点,防止长回答时断连
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
send_timeout 3600s;
}
}
如果管理节点解析不了 gpu02 主机名,把 proxy_pass 里的 gpu02 换成实际 IP:
proxy_pass http://10.10.10.xx:8088;
检查配置并启动
nginx -t
# 正常输出:syntax is ok 和 test is successful
systemctl restart nginx && systemctl enable nginx
Nginx 启动报 80 端口占用怎么处理?
这个坑挺常见。管理节点上往往已经有别的服务占了 80 端口,而 Nginx 默认配置会尝试监听 80,启动就报:
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
nginx: [emerg] still could not bind()
排查步骤:
# 看谁在占 80
ss -lntp | grep ':80'
# 找 Nginx 里哪些配置监听了 80
grep -R "listen .*80" /etc/nginx/nginx.conf /etc/nginx/conf.d/*.conf
如果 /etc/nginx/nginx.conf 里有默认的 80 监听,改成:
listen 127.0.0.1:18080 default_server;
# listen [::]:80 default_server;
或者如果是 /etc/nginx/conf.d/default.conf 在监听 80,直接禁用它:
mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.disabled
改完检查并重启:
nginx -t
systemctl restart nginx
防火墙要开哪些端口?
管理节点开放 8088
firewall-cmd --permanent --add-port=8088/tcp
firewall-cmd --reload
firewall-cmd --list-ports
GPU 节点也需要开 8088(如果节点间有防火墙)
firewall-cmd --permanent --add-port=8088/tcp
firewall-cmd --reload
firewall-cmd --list-ports
集群内部节点间如果防火墙没限制,GPU 节点这一步可以跳过。
多用户怎么管理?
Open-WebUI 支持多用户。我们用的账号结构:
| 账号 | 邮箱 | 角色 | 用途 |
|---|---|---|---|
| leehpc | admin@qq.com | 管理员 | 管理系统、用户、模型 |
| user1 | user1@leehpc.com | 普通用户 | 用户 1 使用 |
| user2 | user2@leehpc.com | 普通用户 | 用户 2 使用 |
| user3 | user3@leehpc.com | 普通用户 | 用户 3 使用 |
第一个注册用户
Open-WebUI 第一个注册的用户自动成为管理员,管理员可以在后台管其他用户。
开启用户注册
管理员登录后:管理员面板 > 设置 > 允许新用户注册
建议短暂开放,注册完立即关闭,防止无限增加用户。
管理员手动创建用户
管理员登录 > 管理员面板 > 用户 > 新建用户(界面右侧 + 号)
模型权限设置
管理员登录 > 管理员面板 > 设置 > 模型 > 模型编辑(名称后面的笔符号)> 访问 > 公共 > 添加访问 > 刷新
并发和性能怎么调?
当前是 4 个用户用的场景。
Open-WebUI 并发
通过环境变量设置:
Environment="WEBUI_WORKERS=4"
注意:open-webui serve 命令本身不接受 --workers 参数,只能用 WEBUI_WORKERS 环境变量。
Ollama 并发
Environment="OLLAMA_NUM_PARALLEL=2"
如果模型比较小、显存充裕,可以试:
Environment="OLLAMA_NUM_PARALLEL=4"
出现显存不够或推理失败就退回 2。
Flash Attention
支持的 GPU 和模型建议开:
Environment="OLLAMA_FLASH_ATTENTION=1"
能降低显存占用,加载和推理都会快一些。在 4090D 上实测有效。
常用检查命令
管理节点 mgmt01
systemctl status nginx # Nginx 状态
ss -lntp | grep 8088 # 是否监听 8088
curl -I http://127.0.0.1:8088 # 本地测试
curl -I http://gpu02:8088 # 测试到 GPU 节点连通性
tail -f /var/log/nginx/open-webui.access.log # 访问日志
tail -f /var/log/nginx/open-webui.error.log # 错误日志
GPU 节点 gpu02
systemctl status open-webui # Open-WebUI 状态
ss -lntp | grep 8088 # Open-WebUI 端口
tail -f /LLM/bin/webui.log # Open-WebUI 日志
systemctl status ollama # Ollama 状态
journalctl -u ollama -f # Ollama 日志
ollama list # 已安装模型
nvidia-smi # GPU 状态
开机自启确认
gpu02:
systemctl is-enabled ollama # 应为 enabled
systemctl is-enabled open-webui # 应为 enabled
mgmt01:
systemctl is-enabled nginx # 应为 enabled
最终的安全建议
管理员账号和所有普通用户都创建好之后,建议关掉注册入口:
Environment="ENABLE_SIGNUP=false"
然后:
systemctl daemon-reload
systemctl restart open-webui
运行建议:
1. 管理员账号只由系统管理员使用;
2. 每个用户使用独立账号,不要多人共用一个;
3. 创建完用户后关闭注册入口;
4. Nginx 只对外开放 8088;
5. Ollama 仅在内部节点访问,不暴露到公网;
6. 定期检查 /LLM/bin/webui.log 和 journalctl -u ollama 的日志。
踩坑记录
下面这几个坑当时排查得比较久,记录一下。
| 报错现象 | 原因分析 | 解决方案 |
|---|---|---|
bind() to 0.0.0.0:80 failed (98: Address already in use) |
管理节点已有服务占用 80 端口,Nginx 默认配置尝试监听 80 | ss -lntp | grep ':80' 查占用,禁用或修改默认 80 监听后重启 Nginx |
| Open-WebUI 启动后立即退出,日志显示 HuggingFace 下载失败 | 没有开启离线模式,启动时尝试连接 HuggingFace Hub 拉模型文件 | 确保设置了 HF_DATASETS_OFFLINE=1、TRANSFORMERS_OFFLINE=1、HF_HUB_OFFLINE=1 |
| 用户访问 mgmt01:8088 无响应 | 可能是 Nginx 没跑、防火墙没开、或 Open-WebUI 服务挂了 | 分别在 mgmt01 和 gpu02 检查监听端口和防火墙,在管理节点 curl 测试到 gpu02 的连通性 |
| Open-WebUI 连接不上 Ollama | OLLAMA_BASE_URL 配置错误或 Ollama 没监听正确地址 |
检查 Ollama 是否绑定 0.0.0.0:11434,确认 OLLAMA_BASE_URL 地址正确 |
| 多个用户同时推理时显存溢出 | OLLAMA_NUM_PARALLEL 设太大,单张卡同时加载模型太多 |
建议默认设为 2,显存充裕再尝试调到 4 |
FAQ
Q:Ollama 默认把模型下载到系统盘,怎么改到数据盘上?
A:在 Ollama 的 systemd 服务文件里加一行 Environment="OLLAMA_MODELS=/LLM/models",然后 systemctl daemon-reload && systemctl restart ollama。模型目录需要提前创建好,Ollama 不会自己建。配完之后 ollama list 能看到路径变化。
Q:Nginx 反代时为什么要关 buffering?
A:因为大模型的输出是逐 token 流式返回的,如果 Nginx 开了缓冲,会把数据攒到一定量才发给客户端,用户看到的就是页面卡住、然后突然刷出一大段,体验很差。proxy_buffering off 之后每个 token 都能实时推到浏览器。这个在长回答场景下区别特别明显。
Q:Open-WebUI 启动就退出,日志里全是 HuggingFace 相关的下载报错,怎么解决?
A:一般都是服务器访问不了 HuggingFace Hub。在 Open-WebUI 的环境变量里加上这三行就行:
Environment="HF_DATASETS_OFFLINE=1"
Environment="TRANSFORMERS_OFFLINE=1"
Environment="HF_HUB_OFFLINE=1"
加上之后重启服务,应该就不会再去尝试联网下载了。
Q:4 张 RTX 4090D,OLLAMA_NUM_PARALLEL 设多少比较合适?
A:这个得看模型大小。当前测下来,如果模型显存占用在一张卡以内,设 4 问题不大;如果模型比较大、单卡显存紧张,建议设 2。显存不够的时候 OLLAMA_NUM_PARALLEL 设大了反而会导致推理失败,可以先从 2 开始,跑稳了再往上调。
Q:第一个注册 Open-WebUI 的用户就是管理员吗?后面怎么添加更多用户?
A:是的,第一个注册的自动成为管理员。之后管理员登录后可以在"管理员面板 > 用户"里手动创建新用户,也可以在设置里短暂开启"允许新用户注册",让别人自己注册完再关掉。根据 LEEHPC 团队的实操经验,建议走管理员手动创建的方式,可以严格控制用户数量。
其他
实时联网检索功能需要额外购买相关搜索 API 才能生效,文中配置的 searxng 也需要自行部署。
关于 LEEHPC 一粒海
LEEHPC 一粒海专注于高性能计算、AI 服务器与科学计算软件编译优化,提供 GPU/CPU 服务器定制、Slurm 集群调度、InfiniBand 网络部署、Lustre/BeeGFS 并行存储,以及 VASP、LAMMPS、CP2K、QE、ABACUS、GROMACS 等科研软件的编译与性能调优服务。
更多技术文章,欢迎关注公众号「服务器与科学计算」官网:www.leehpc.cn
标签:#HPC 集群 #Ollama 部署 #OpenWebUI #Nginx 反代 #大模型推理 #多用户管理 #LEEHPC 一粒海 #科学计算

浙公网安备 33010602011771号