☰
Docker+Ollama+OpenWebUI私有化部署大模型与显存调优
2026/10/26 20:07:00 网站建设 项目流程

私有化部署大模型这件事,真正动手做过一遍的人会发现它没那么神秘,但也没那么随便。比较常见的组合是 Docker 加 Ollama 再加 OpenWebUI:Ollama 负责把模型权重跑起来、对外提供 API,OpenWebUI 提供浏览器里的对话界面,Docker 把这两样东西装进容器,一条命令起停。标题里提到的 gemma、llama2、qwen 三款模型,正好覆盖了轻量试水、生态成熟、中文友好三种不同的选型诉求。我这次把这套东西从零跑通、跑了几个月、踩了一圈坑之后,回头把整条链路重新梳理一遍,包括硬件怎么算账、compose 文件为什么这么写、参数在哪里调、出了问题从哪儿查。适合手上有台带显卡的机器、想把模型留在自己内网、又不想花几天时间折腾环境的人参考;没用过 Docker 也能跟下来,但基础的命令行操作要熟。

1. 这套组合到底解决了什么问题

1.1 三个组件各管一段,别混着理解

很多人第一次接触会把它当成"一个软件",其实这是三层很清晰的职责划分。Ollama 是推理运行时,它的核心工作是把 GGUF 格式的模型权重加载进内存或显存,接收 HTTP 请求,吐 token 流。它自己带了一个模型仓库客户端,能直接按名字拉模型,省掉了自己找权重、转格式、写加载脚本这一大坨事。OpenWebUI 是纯前端加一层后端代理,负责会话管理、多用户、模型切换、知识库、提示词模板这些东西,它本身不含任何推理能力,所有生成请求都转发给 Ollama 的/api/chat或/api/generate接口。Docker 在最底下,解决的是依赖地狱:Python 版本、CUDA 运行时、系统库冲突,这些在裸机上装一次能折腾半天,容器化之后变成拉镜像、起容器两个动作。

把这三层拆明白,后面所有问题都好定位。界面打不开,去查 OpenWebUI 容器;界面能开但发消息转圈,去查 OpenWebUI 到 Ollama 的网络连通;能连上但报模型不存在,去查 Ollama 里到底 pull 了哪些模型;模型在但回答奇慢,去查是不是掉到 CPU 上跑了。这四个层次基本覆盖了 90% 的故障。我见过不少人一上来就重装,其实日志里一行model not found就说明白了。

1.2 为什么用容器而不是本机裸装

裸装当然也能跑,Ollama 官方提供了一键安装脚本,装完ollama serve就起来了。但一旦你想要"能随时删干净重来",容器优势就出来了。模型权重动辄几个 GB 到几十个 GB,装在本机时模型文件散落在用户目录下,配置在/etc/systemd,日志在 journal 里,想彻底清理得挨个找。用容器加 bind mount 之后,模型目录就是宿主机上一个普通文件夹,删掉即清零,迁移时打包带走即可。

另一个容易被忽略的点是版本锁定。Ollama 和 OpenWebUI 迭代都很快,镜像 tag 天天在变,某次latest拉下来可能行为就跟上周不一样。标题里那个日期标记其实就是在提醒这件事——这套组合的默认参数、接口字段、模型标签都在变。用容器的话,你在 compose 里写死一个具体的 tag,什么时候升级由你决定,而不是被动接受。升级出问题,改回上一个 tag 重启,两分钟回滚。裸装想做到这个,得自己维护安装包。

还有一个实际好处是 GPU 资源的隔离声明。容器里通过设备声明把显卡暴露进去,你能明确看到哪个容器占了多少显存,编排文件里写清楚,换机器时不用重新回忆当初装了什么驱动组件。

1.3 先把预期摆正:它不适合干哪些事

这套方案的定位是"个人或小团队的私有推理入口",不是生产级推理平台。差距体现在几个地方:Ollama 的调度是单机单实例的,没有跨节点的负载均衡,也没有请求队列的优先级管理,几个并发打满之后后面的请求就排队;OpenWebUI 的多用户是轻量级的账号体系,权限模型不细,做不了复杂的企业组织架构映射;整个链路没有审计日志、没有细粒度配额。

所以如果你的诉求是"给几十人同时用、要计费、要审计",那这套东西只能当原型验证,验证完该换成带调度层的方案。但如果诉求是"我自己或者三五个同事,在内网里有个能随时问的模型,数据不出门",那它的性价比高得离谱。先把边界说清楚,能省掉后面大量"为什么它不支持 XX"的无效纠结。

2. 部署前的硬件与模型选型账

2.1 显存怎么算:权重、KV Cache、系统开销三笔账

这是整个部署里最值钱的一节,算明白了能省下买错卡的钱。显存占用大致分成三块:模型权重、KV Cache、运行时开销。

权重这块好算,看量化格式。FP16 是每参数 2 字节,Q8 约 1 字节,Q4_K_M 大约 0.55 到 0.6 字节。所以 7B 模型 Q4 量化后权重约 4GB 出头,13B 约 7.5GB,70B 约 40GB。这个数字在模型页面上通常直接标了文件大小,直接看就行,不用自己估。

KV Cache 是新手最容易漏掉的一块,也是"明明权重装得下却爆显存"的元凶。它有明确的公式:

KV Cache 字节数 = 2 × 层数 × KV头数 × 每头维度 × 序列长度 × 精度字节数 × 并发数

开头的 2 是因为 K 和 V 各存一份。拿 llama2 7B 举例,32 层、32 个注意力头、每头 128 维、FP16 精度,单 token 的 KV 占用是2 × 32 × 32 × 128 × 2 = 524288字节,约 0.5MB。上下文开到 4096 token,就是 2GB。再叠加 4GB 权重和大约 0.5 到 1GB 的运行时开销,8GB 显存的卡刚好卡在边缘,实际上跑起来会有一部分被挤出到内存,速度立刻掉一个数量级。

注意公式里用的是"KV 头数"而不是"注意力头数"。用了 GQA(分组查询注意力)的模型,KV 头数远小于注意力头数,KV Cache 会小一个数量级。llama2 70B 就是 8 个 KV 头,单 token 只有2 × 80 × 8 × 128 × 2 ≈ 0.31MB,4096 上下文的 KV 只有 1.3GB 左右,配合 40GB 权重,48GB 显存的卡能跑。如果不区分这两个概念,你会算出 70B 需要 80GB 以上显存,直接把可行方案否掉。

我整理了一张常见组合的估算表,上下文按 4096 算,单并发:

模型量化权重KV Cache 估算建议显存下限
gemma:2bQ4约 1.5GB约 0.6GB4GB
gemma:7bQ4约 4.8GB约 1.8GB8GB
llama2:7bQ4约 3.8GB约 2.1GB8GB
qwen:7bQ4约 4.4GB约 2.1GB8GB
llama2:13bQ4约 7.4GB约 3.2GB12GB
qwen:14bQ4约 8.9GB约 3.5GB16GB
llama2:70bQ4约 40GB约 1.4GB(GQA)48GB

表里的"建议下限"是能跑起来且不掉到 CPU 的值,不是舒服的值。想开更大上下文、想跑两个并发,往上加就行,KV Cache 是随上下文和并发线性涨的,这部分没有魔法。

提示:估算只是估算,实际的显存峰值还受推理框架的临时缓冲区、CUDA 上下文初始化占用影响,通常要留出 1GB 左右的余量。按表里的下限刚好配卡,大概率会遇到偶发的 OOM。

2.2 gemma、llama2、qwen 三款模型怎么选

这三款放在一起对比挺有意思,它们代表了三个不同时期、不同设计取向。

llama2 是这批里资历最老的,7B 和 13B 版本在消费级显卡上跑得很稳,生态兼容性最好,几乎所有的工具链都对它有优化。它的短板也很明显:上下文只有 4096,中文能力基本靠"能说几句",做中文问答会明显感觉到词不达意、术语翻译生硬。如果主要场景是英文的技术问答、代码补全,它依然是个稳妥的基线选择;如果主力是中文,它只能当对照组。

gemma 系列走的是小体积路线,2B 这个规格特别适合拿来做流程验证。它能在很一般的机器上跑起来,回答质量对付简单的分类、摘要、格式化输出够用。7B 版本比 2B 强不少,但相比同尺寸的其他模型,它的中文表现中规中矩。gemma 有个好处是对话风格比较克制,不太容易胡编,做需要稳定输出的场景(比如把一段文本抽成 JSON)反而省心。

qwen 系列在中文场景下的表现是这三款里最贴合国内使用习惯的,术语、成语、语感都更自然,指令跟随也稳。7B 和 14B 是个人机器上最实用的两个档位,7B 一张 8GB 卡能跑,14B 建议 16GB 起。它的问题在于生态版本多、标签杂,不同时间拉下来的同名标签可能对应不同版本,需要留意。

我的实际用法是三个都留着,按任务切换:结构化抽取和小任务走 gemma 2b(快、省资源),英文技术问答走 llama2 13b,中文长文处理和知识库问答走 qwen 14b。模型不用只留一个,Ollama 支持同时存多个,加载哪个看请求里的模型名。当然同时加载多个会一起吃显存,这个要配合后面的OLLAMA_MAX_LOADED_MODELS控制。

2.3 磁盘、内存、CPU 这些容易被忽略的连带项

显卡之外还有三样东西要提前看。磁盘,每个模型几 GB,三个模型加上 OpenWebUI 的数据目录,50GB 起步比较稳妥,别忘了 Docker 自己的镜像层也占地方,docker system df隔段时间看一眼,镜像残留能吃掉几十 GB。内存,如果显存不够需要 CPU 卸载(offload),内存就是第二战场,一个 13B Q4 模型整段卸载到内存大约要 8GB 常驻,加上系统和容器,32GB 内存是比较舒服的配置,16GB 会很紧张。CPU,纯 CPU 推理的速度大致是每秒几个 token,能用的门槛是核心数够多、支持向量指令集,但说实话体验不好,超过 7B 就别指望交互式对话了。

3. 一键部署实操:从零到能对话

3.1 环境准备与目录规划

先确认基础环境。宿主机上装好 Docker 引擎和 Compose 插件,docker version和docker compose version都能输出版本号即可。如果要用显卡,还得装好显卡驱动和容器 GPU 运行时组件,验证方式是nvidia-smi能看到卡,再跑一个带 GPU 声明的临时容器确认设备能透传进去。

目录规划我建议一开始就分好,别等数据长起来再挪:

mkdir -p /opt/ai-stack/{ollama,open-webui} cd /opt/ai-stack

ollama目录挂给容器的/root/.ollama,模型权重都在里面;open-webui目录挂给/app/backend/data,账号、会话、上传的文档、向量库都在里面。两个目录分开的好处是升级 OpenWebUI 时不用担心动到模型,备份也可以分开做策略——模型可以重下,会话数据丢了就真没了。

注意:这两个目录一定要用 bind mount 挂到宿主机,不要在容器里裸存。容器删除重建是家常便饭,数据在容器可写层里就等于随时会丢。

3.2 compose 编排文件逐行拆解

下面这份是我实际在用的,去掉了一些环境特定的东西,可以直接抄:

services: ollama: image: ollama/ollama:latest container_name: ollama restart: unless-stopped ports: - "127.0.0.1:11434:11434" volumes: - ./ollama:/root/.ollama environment: - OLLAMA_KEEP_ALIVE=30m - OLLAMA_NUM_PARALLEL=2 - OLLAMA_MAX_LOADED_MODELS=2 deploy: resources: reservations: devices: - driver: nvidia count: all capabilities: [gpu] open-webui: image: ghcr.io/open-webui/open-webui:main container_name: open-webui restart: unless-stopped ports: - "3000:8080" volumes: - ./open-webui:/app/backend/data environment: - OLLAMA_BASE_URL=http://ollama:11434 - WEBUI_SECRET_KEY=换成你自己的随机长字符串 depends_on: - ollama

几个地方值得单独说。

端口映射写127.0.0.1:11434:11434而不是11434:11434,这是有意的。Ollama 的 API 不带任何鉴权,谁连上谁就能调模型、能拉模型、能删模型。绑到回环地址之后,外部只能通过 OpenWebUI 那一层进来,减少一个暴露面。如果你的 OpenWebUI 和 Ollama 都在同一个 compose 网络里,其实 Ollama 的端口根本不用映射到宿主机,直接删掉ports那两行更干净,容器之间用服务名互访就行。

OLLAMA_BASE_URL这里写的是http://ollama:11434,用的是 compose 里的服务名。这是容器网络的好处——不用关心宿主机 IP 变没变。很多人第一次配置失败就是这里填了localhost,容器里的 localhost 指向容器自己,当然连不上。

WEBUI_SECRET_KEY必须改。它是用来签发会话凭证的密钥,用默认值等于谁都知道你的签名密钥。随便生成一个长随机串就行,生成方式可以用openssl rand -hex 32。

deploy.resources.reservations.devices这段是 GPU 声明,count: all表示把宿主机所有可见显卡都给它。如果你有多张卡但只想给一张,写count: 1或者用device_ids指定。没这段的话容器起来就是纯 CPU 模式,你会在日志里看到很慢的生成速度,还以为是模型不行。

OLLAMA_KEEP_ALIVE=30m表示模型加载后在显存里保留 30 分钟,超时卸载。默认是 5 分钟,短了点——你去泡杯茶回来再问,模型已经卸载,重新加载又要等十几秒。设长一点体验好很多,代价是显存一直被占着。

OLLAMA_NUM_PARALLEL=2控制单模型并发请求数,OLLAMA_MAX_LOADED_MODELS=2控制同时加载几个模型。这两个值都要跟显存匹配,盲目调大只会 OOM,后面调优章节细说。

还有一点,新版 Compose 已经不需要文件顶部的version:字段了,写了会有一条 obsolete 警告,不影响运行但看着烦,直接删。

3.3 起容器、拉模型、验证链路

编排文件写好后:

docker compose up -d docker compose ps docker compose logs -f ollama

第一次起 OpenWebUI 会执行数据库初始化,日志里能看到建表过程,大概一两分钟。等docker compose ps里两个容器都是running,就可以拉模型了。

模型不要通过界面拉,用命令行更可控,也能看到进度:

docker exec -it ollama ollama pull qwen:7b docker exec -it ollama ollama pull gemma:7b docker exec -it ollama ollama pull llama2:13b

拉完确认一下:

docker exec -it ollama ollama list

列表里能看到模型名、大小、修改时间。然后用 API 直接测一发,绕过界面,确认推理链路本身是通的:

curl http://127.0.0.1:11434/api/generate -d '{ "model": "qwen:7b", "prompt": "用三句话解释什么是向量数据库", "stream": false }'

返回 JSON 里有response字段就说明模型能正常出词。这一步特别关键,它把"推理有问题"和"界面有问题"彻底分开了。如果这里不通,去界面里点一百次也没用。

接着浏览器打开http://宿主机IP:3000。第一次访问会让你注册账号,注意第一个注册的账号自动成为管理员,所以别在开放网络里让别人抢先注册了。注册完进去,左上角模型选择器里应该能直接看到刚才拉的三个模型——OpenWebUI 会自动去 Ollama 拉模型列表,不需要手动添加。

如果模型选择器是空的,先检查 OpenWebUI 容器里的OLLAMA_BASE_URL是否指向了正确的服务名,再确认两个容器是不是在同一个网络里。用docker exec -it open-webui curl http://ollama:11434/api/tags能直接验证这条链路,返回模型列表说明网络没问题。

4. 参数调优与性能压榨

4.1 Ollama 侧的环境变量怎么配

Ollama 的行为几乎全靠环境变量控制,理解几个关键的就能把资源用得比较干净。

OLLAMA_KEEP_ALIVE前面说过,控制模型驻留时间。这里有个细节:这个值可以按请求覆盖,在 API 请求里带keep_alive字段,所以你可以让常用模型常驻、临时用的模型快速释放。实际配置时我一般设成30m,然后在 OpenWebUI 里对那些"偶尔问问"的模型不做特殊处理,反正 30 分钟后自己就下来了。

OLLAMA_NUM_PARALLEL是最容易配错的一个。它决定同一个模型能同时处理几个请求。要注意并发不省钱——每个并发都要独立的 KV Cache。前面算过 llama2 7B 在 4K 上下文下 KV 约 2GB,两个并发就是 4GB,加上 4GB 权重,8GB 卡直接爆。所以这个值要和你的显存预算对齐:8GB 卡老实设 1,16GB 卡可以设 2,24GB 以上再往上加。设成 1 的代价是两个人同时提问时,第二个要排队等第一个答完,但对个人使用来说完全感觉不到。

OLLAMA_MAX_LOADED_MODELS控制同时驻留几个不同模型。如果你像我一样备了三个模型轮换用,设成 2 比较合适,第三个用时会把最久没用的那个换出去。设成 3 的话显存占用是三者之和,很容易撑爆。

OLLAMA_MAX_QUEUE是请求队列长度,默认 512,个人场景基本用不到,除非你在做压测。

还有一个不常用但偶尔救命的:OLLAMA_HOST,控制监听地址。官方镜像里已经处理成0.0.0.0,一般不用动,但如果你自己基于别的镜像构建,忘记设这个会导致容器外部连不上。

4.2 OpenWebUI 里几个值得改的默认项

界面侧的默认值有些不太适合私有化场景,进去之后建议先改这几处。

关闭开放注册。管理面板里的账号设置,把"允许新用户注册"关掉,之后要加人由管理员手动创建。私有部署最常见的翻车方式就是端口映射到公网、注册开着,然后被扫到,变成别人的免费推理服务。

模型可见性。如果你只想让同事用 qwen,不想让他们乱选一堆模型试,可以在工作空间里配置模型的白名单,把不用的隐藏掉。这个对降低支持成本很有用——不然总有人选了 70B 然后抱怨慢。

系统提示词。OpenWebUI 支持给每个模型预设系统提示词,这个能力被低估了。给中文场景的模型加一句"用简体中文回答,不确定的信息直接说不知道",能显著降低胡编率。给做结构化输出的模型加"只输出 JSON,不要任何解释",能省掉大量后处理。

知识库和向量模型。如果你要用文档问答功能,需要额外拉一个 embedding 模型,比如nomic-embed-text,然后在设置里指定。这个模型很小,几百 MB,但它会常驻显存,做显存预算时别忘算进去。上传大文档时第一次索引会比较慢,属于正常现象。

4.3 量化格式怎么挑:别一味追最小

同一个模型通常提供多个量化档位,常见的有 Q4_K_M、Q5_K_M、Q8_0。数字越小体积越小、速度越快、质量损失越大。Q4_K_M 是通用场景下的甜点,体积约为 FP16 的四分之一,质量损失在日常对话里基本感知不到。Q5_K_M 体积大一点,在需要精确复述、代码生成这类任务上更稳。Q8_0 接近原始精度但体积翻倍,除非你有富余显存且对质量极度敏感,否则不划算。

还有一个更极端的 Q2、Q3 档位,我不建议用在正经任务上。量化到那个程度,模型会开始出现明显的逻辑断裂和重复输出,看起来像"变笨了",实际是精度损失太大。宁可选个小一号的模型用 Q4,也别把大模型压到 Q2——7B 的 Q4 通常比 13B 的 Q2 表现更好,还更快。

选模型标签时也要注意,Ollama 库里的标签有时候不标量化等级,默认给的是某个中间档。想要特定量化版本,可以用模型名:参数量-量化等级这种形式指定,具体标签得看模型页面。标签写错会直接报 not found,这时候ollama pull的报错信息里通常会列出可用的标签,照着改就行。

5. 常见问题与排查实录

5.1 界面能开但发消息一直转圈

这是最高频的问题,成因有三类,按概率从高到低排。

第一类是网络不通。OpenWebUI 容器里连不到 Ollama。验证命令前面给过:docker exec -it open-webui curl http://ollama:11434/api/tags。如果这条命令超时或者拒绝连接,说明两个容器不在同一个网络,或者OLLAMA_BASE_URL写错了。最常见的错误是写成http://localhost:11434或者http://127.0.0.1:11434,在容器里这两个地址指向容器自身,必然连不上。改成服务名就好。

第二类是模型不存在。OpenWebUI 拿到了模型列表,但你选的模型其实没拉下来,或者标签写错。这时候 Ollama 日志里会有model not found之类的记录,docker compose logs ollama一看就明白。解决就是老老实实ollama pull对应的完整标签。

第三类是首次加载太慢被误判成卡死。大模型第一次加载要读几十 GB 文件进显存,机械盘上可能要一两分钟,界面这边就是一直转。这种情况等一会儿就好,之后靠keep_alive保持驻留就不会再等。判断方法是同时看docker stats,如果 Ollama 容器的内存或显存占用在涨,说明在加载,别急着杀。

5.2 显存爆了:从日志认出 OOM

显存不足的表现是请求直接失败,或者速度突然从每秒几十 token 掉到每秒一两个 token。前者是硬 OOM,后者是被迫把部分层卸载到内存。

Ollama 日志里如果出现跟显存分配相关的错误,基本可以确认。这时候的排查顺序是:先看当前加载了几个模型(ollama ps能看到正在运行的模型和它们占用的显存),再看上下文长度设了多大,最后看并发数。

ollama ps这个命令很多人不知道,它输出的是当前驻留的模型、占用大小、卸载倒计时,是判断显存状况最直接的工具。如果发现同时驻留了两个大模型,那就是OLLAMA_MAX_LOADED_MODELS设太大了,改小重启。

上下文长度也是一个隐藏的坑。有些客户端或者某些调用方式会请求很长的上下文(比如 32K),KV Cache 瞬间翻好几倍。解决办法是在请求里显式限制num_ctx参数,或者用 OpenWebUI 的模型参数面板给每个模型设一个上限。我一般给 7B 模型设 4096,14B 设 8192,够用且可控。

5.3 拉模型慢、失败、磁盘莫名暴涨

拉模型的速度取决于源站和网络环境,国内环境下偶尔会很慢甚至中断。中断之后重跑ollama pull是可以续传的,不用删了重来,它会从已下载的分片继续。但如果一直卡在某个分片上,可以试ollama rm 模型名清掉再拉。

磁盘暴涨通常是两个原因。一是模型拉多了,ollama list看一眼,不用的ollama rm掉。二是 Docker 镜像残留,docker system df会显示可回收空间,镜像更新之后旧的镜像层不会自动删,积累几周就是几十 GB。定期docker image prune是必要的。

还有一个隐蔽的磁盘占用是 OpenWebUI 的上传目录。传了几个 PDF 做知识库问答之后,原始文件和向量索引都存在open-webui目录里,几十上百 MB 很常见。这个不算垃圾,但备份时要知道它在那儿。

5.4 中文答得别扭、答到一半断掉、答非所问

中文别扭,很多时候不是模型不行,是系统提示词没设。默认提示词是英文的,模型会倾向于用英文思维组织答案再翻译,读起来就是一股翻译腔。加一句中文指令能改善一大截。另外 gemma 和 llama2 的中文底子确实不如 qwen,这是模型本身的差异,靠提示词补不回来,中文场景直接上 qwen 更省事。

答到一半断掉,九成是上下文或者输出长度限制。检查请求里的num_predict(最大输出 token 数),默认值可能偏小,长回答会被截断。同时检查num_ctx,如果上下文设得太小,历史对话被挤掉,模型会"忘了"前面的内容,表现为答非所问。

答非所问还有一种可能是模型被量化压得太狠,或者拉到的标签其实是个指令微调不充分的版本。换个量化档位、换个标签试试,很快能判断出来。

我把这些问题整理成一张速查表,出问题时按顺序对一遍:

现象优先排查验证方式常见解法
界面打不开OpenWebUI 容器状态docker compose ps看日志,等初始化完成
发消息转圈容器间网络docker exec里 curl Ollama改OLLAMA_BASE_URL为服务名
报模型不存在模型是否已拉取ollama list按完整标签 pull
速度骤降是否卸载到内存ollama ps减小上下文或并发
显存 OOM同时加载的模型数ollama ps、日志调小MAX_LOADED_MODELS
回答被截断输出 token 上限请求参数调大num_predict
中文翻译腔系统提示词界面设置加中文回答指令
磁盘暴涨镜像和模型残留docker system df定期 prune 和 rm

提示:这张表建议存下来。排查时不要凭感觉重启,先看一眼日志和ollama ps,八成问题三十秒内能定位。

6. 我踩过的坑与长期运维经验

6.1 数据持久化这件事,出事一次就记住了

最开始图省事,open-webui那个目录没挂到宿主机,直接放容器里。后来想升级镜像,docker compose pull加up -d,容器重建,账号和所有会话全没了。这事儿的教训很直接:凡是容器里会产生数据的路径,都必须挂出来,而且挂载之前先确认目录权限对得上。

权限这块也有坑。bind mount 之后容器内的进程以某个 UID 运行,如果宿主机目录属主不对,会出现"能读不能写",表现是上传文件失败、会话保存不了。排查方式是看容器日志里的权限报错,解决方式是调整宿主机目录属主,或者在 compose 里指定运行用户。

备份策略我现在的做法很简单:每周把open-webui目录打个 tar 包扔到另一块盘上,模型目录不备(重下就行)。备份前先docker compose stop open-webui,避免数据库文件在写入中途被复制导致损坏。这个动作加起来不到一分钟,但真出事的时候能救命。

6.2 升级与回滚:镜像 tag 不要用 latest

latest是个陷阱。它在不同时间指向不同镜像,某天你pull一下再up -d,行为可能就变了——可能是界面改版、可能是环境变量改名、可能是默认参数调整。我之前遇到过一次界面上某个功能突然不见了,排查半天才发现是新版本把它挪到别的地方去了。

现在的做法是把 tag 固定成具体的版本号,写在 compose 里。升级流程变成:先看变更说明,改 tag,pull,up -d,验证核心功能;有问题就把 tag 改回去,再up -d,两分钟回滚。定版的好处是环境可复现,换机器部署时行为完全一致。

Ollama 镜像同理。不过 Ollama 的兼容性相对好一些,模型格式向后兼容,升级一般不会导致已有模型不能用。但如果你依赖某个特定版本的 API 字段,也建议定版。

6.3 把"私有"两个字落到实处

私有化部署的价值在于数据不出内网,但很多人的实际配置是:端口映射到公网、注册开放、没有访问控制。这种状态下,"私有"只是个心理安慰。

我的做法分三层。第一层是网络,Ollama 的端口只绑回环,不对外;OpenWebUI 的端口只在内网网段可达,不直接暴露到公网;如果要远程访问,走内网穿透或者前置一层带认证的网关,别直接把 3000 端口扔出去。第二层是账号,关闭开放注册,账号由管理员创建,密码策略设严格一点。第三层是内容,虽然数据不出内网,但会话记录本身也是数据,OpenWebUI 支持配置会话保留策略,敏感场景下可以设成不保存历史。

还有一点容易忽略:模型本身也可能是个信息出口。你上传的文档如果含敏感内容,做知识库索引时会连同向量一起存在本地,这部分数据同样要纳入备份和清理策略。

这套东西我从最开始的"能跑起来就行",到后来固定版本、分目录挂载、按显存算并发、关掉一切不必要的暴露面,中间踩的坑基本都写在上面了。有一个体会挺深:真正让人省心的不是把参数调到极限,而是把每个值调到"留有余量"。显存留 1GB,并发留一档,上下文留一半,看着像是浪费,实际是让这套东西在几个月的时间里安静地待着,不会在你要用的时候掉链子。如果后面还想往上扩展,比较自然的方向是接一个本地 embedding 模型把知识库做扎实,或者再加一层带限流的网关做多人共享,这两件事都可以在当前这套编排上直接叠加,不用推倒重来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询