拉完 deepseek-r1:14b/32b/70b 後第一次推理就 OOM,不一定是模型文件大小問題。三件常被忽略的事疊在一起:預設
keep_alive = 5m讓上一個模型繼續佔著記憶體;預設num_ctx = 4096推高 KV cache;OLLAMA_MAX_LOADED_MODELS預設允許多個模型併發。把它們都收住,多數機器都能救回來。
Issue & Env(案發現場與環境)
- 業務場景:本地拉完
deepseek-r1:14b或deepseek-r1:32b,跑ollama run立刻被 kill,Linux 上 dmesg 報oom-kill;有時換了一個更小的 tag 反而沒事。 - 環境:Ollama v0.34.x,DeepSeek-R1 蒸餾模型 Q4_K_M 量化(預設體積
1.5b=1.1GB / 7b=4.7GB / 8b=5.2GB / 14b=9.0GB / 32b=20GB / 70b=43GB),官方上下文窗口統一 128K,671b 主模型 160K。
StackTrace(堆疊日誌)
CUDA 報:
Error: CUDA error: out of memory
進程被系統 OOM killer 幹掉後,Linux 內核日誌會出現:
ollama invoked oom-killer: gfp_mask=0x100cca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0
500 形態(服務層把推理進程的死亡包了一下):
500 Internal Server Error: memory layout cannot be allocated
看到這些,先翻 journalctl -u ollama -n 200 --no-pager,裡面會有 Ollama 估算的可用顯存、模型大小、KV cache 佔用分配。
Reproduce(復現程式碼)
最小復現路徑:
# 1) 拉一个比显存/内存大的 tag
ollama pull deepseek-r1:32b # 20GB
# 2) 先让另一个模型占着内存(默认 keep_alive 5 分钟)
ollama run deepseek-r1:8b --verbose "" # 然后 bye,模型还在内存里
# 3) 五分钟内再 run
ollama run deepseek-r1:32b
# Error: model requires more system memory than is available
# 或者直接 CUDA error: out of memory
顯存預算粗估:Q4_K_M 量化約 0.6 GB × 十亿参数 + KV cache。14b 想要 ~10GB VRAM,32b 想要 ~20GB,70b 想要 ~43GB。ollama ps 的 SIZE 列加上 VRAM 列就是實際佔用。
Root Cause(根因分析)
OOM 不是一個原因,是多個原因疊在一起。
一、模型本身超過 VRAM/RAM
DeepSeek-R1 蒸餾家族覆蓋 1.5b 到 70b,磁盤體積跟運行時佔用幾乎是 1:1(Q4_K_M 量化需要近乎線性把權重讀到顯存)。32GB 記憶體的機器硬上 32b,能跑但要靠 swap;16GB 上 14b 已經吃緊,70b 直接死。
二、keep_alive 預設 5 分鐘,舊模型沒卸掉
官方 FAQ:By default models are kept in memory for 5 minutes before being unloaded. 這意味著 5 分鐘內連續 run 兩個不同模型,兩個都會佔記憶體。ollama ps 會列出當前駐留的全部。OLLAMA_MAX_LOADED_MODELS 預設是 3 × GPU 数(CPU 推理預設 3),多個模型併發時會同時駐留。
三、num_ctx 4096 預設值推高 KV cache
Ollama 全模型統一預設 num_ctx = 4096,DeepSeek-R1 蒸餾家族最大上下文是 128K。不改預設情況下,KV cache 佔的額外顯存跟上下文長度線性相關。4096 時還能塞進普通機器,8192/16384 起步就開始擠;128K 配 70b,KV cache 就要 80GB+。很多人改了 num_ctx 後第一次跑就崩,其實是把 KV cache 頂上去的。
四、Q4_K_M 量化對精度的取捨
Ollama 庫頁發的是 Q4_K_M(預設)、Q4_K_S、Q5_K_F/Q5_K_M/Q8_0 等不同量化。Q4_K_M 是體積/質量甜點,但 Q8_0 同樣模型能膨脹 1.5–1.7 倍。手動拉 Hugging Face 上的 GGUF 改 Q8_0 時容易撞 VRAM。
五、容器/W SL/記憶體統計口徑不一致
WSL2 預設 VM cap 是 50% 主機記憶體或 8GB 取較小值。64GB 主機跑在 WSL2 裡沒改 .wslconfig,Ollama 看到的是 8GB。Docker 容器 / K8s pod 有 --memory 或 resources.limits.memory 上限,Ollama 只能看到容器內的額度,宿主機再多也幫不上忙。ZFS 文件系統上 ARC 緩存會刻意吃滿空閒記憶體,讓 Ollama 誤判可用 RAM。
The Fix(解決方案)
按便宜程度排序,先試不花錢的。
第一步:先看現在佔著多少
ollama ps
輸出形如:
NAME ID SIZE PROCESSOR UNTIL
deepseek-r1:8b ... 5.5 GB 100% GPU 4 minutes from now
UNTIL 列就是 keep_alive 倒計時。
第二步:手動清掉舊模型
ollama stop deepseek-r1:8b
# 或者接口里 keep_alive=0
curl http://localhost:11434/api/generate -d '{"model":"deepseek-r1:8b","keep_alive":0}'
第三步:把 num_ctx 收緊
num_ctx 是 KV cache 的最大頭。不需要長上下文就先收小:
# 临时
ollama run deepseek-r1:14b --num-ctx 2048
# 永久(Modelfile)
cat > Modelfile <<'EOF'
FROM deepseek-r1:14b
PARAMETER num_ctx 2048
EOF
ollama create deepseek-r1:14b-2k -f Modelfile
ollama run deepseek-r1:14b-2k
或者全局把預設值調小:服務側 OLLAMA_CONTEXT_LENGTH=2048。DeepSeek-R1 推薦 temperature 0.6、top_p 0.95,跟預設就夠,num_predict 不需要單獨壓。
第四步:讓 keep_alive 短一點或徹底卸掉
OLLAMA_KEEP_ALIVE=0 讓模型用完立即釋放;想要快速重新加載就 -1 一直駐留:
# systemd drop-in
sudo systemctl edit ollama
[Service]
Environment="OLLAMA_KEEP_ALIVE=0"
sudo systemctl daemon-reload && sudo systemctl restart ollama
第五步:把最大駐留模型數壓到 1
[Service]
Environment="OLLAMA_MAX_LOADED_MODELS=1"
Environment="OLLAMA_NUM_PARALLEL=1"
這樣 Ollama 必須先卸舊模型才能加載新的,永遠只有一個佔記憶體。多請求併發在隊列裡等。
第六步:換更小的量化檔或更小的 tag
顯存吃緊時優先選更小的 tag,而不是上 Q4_K_S(不一定發佈)或 Q8_0(更大)。deepseek-r1:14b 用 Q4_K_M ~9GB;deepseek-r1:7b 走 Q4_K_M 是 4.7GB,16GB 記憶體機器也能跑。
GPU 實在不夠就回退到純 CPU:Modelfile 加 PARAMETER num_gpu 0,把模型強制留在記憶體裡運行,速度慢但穩。
第七步:環境側的記憶體可見性
# /etc/modprobe.d/zfs.conf (限制 ZFS ARC)
options zfs zfs_arc_max=8589934592
# %UserProfile%\.wslconfig (WSL2 内存上限)
[wsl2]
memory=24GB
swap=8GB
然後 wsl --shutdown 重啟 WSL2。Docker / K8s 同理上調容器 memory。
按這套順序走一遍,out of memory 一般都能收斂。如果還崩,說明機器硬件確實跑不動這個 tag,往下一檔換即可。
References(參考來源)
- FAQ - Ollama 官方文檔(How do I keep a model loaded...)ollama-docs
- deepseek-ai/DeepSeek-R1 - Hugging Facehuggingface
- Ollama Model Requires More System Memory: Fix the RAM Checktroubleshooting-guide
