K8s 基礎教學:在公司 Kubernetes 上部署 Ollama(從零開始)
公司有一台機器被灌成全 K8s 環境,同事開了一個 namespace 給我用,我只能透過 kubectl 跟 YAML 操作。
目標:在 namespace 裡起一個 Ollama 服務,跑 qwen2.5:7b 和 qwen2.5vl:7b 兩個模型,讓同網段的 Mac 可以呼叫。
一、Kubernetes 是什麼
一句話:一個幫你管理容器的作業系統控制平台。
你不是直接跟機器說「幫我開 container、開 port、掛硬碟」, 而是用 YAML 跟 Kubernetes 說:我想要的狀態是什麼,然後它負責實現。
這叫做 declarative(宣告式):
- 你不是一步步下指令
- 你是描述目標狀態,系統自己去達成
二、核心概念速查
| 概念 | 白話解釋 |
|---|---|
| Cluster | 整個 K8s 環境,可以是一台或多台機器 |
| Node | 一台實際跑容器的機器(實體機或 VM) |
| Namespace | K8s 裡的隔離空間,像資料夾,你的東西放這裡不會跟別人混 |
| Pod | K8s 最小的執行單位,容器的外殼,通常裡面跑一個 container |
| Deployment | Pod 的管理器,負責維持你要的 Pod 數量,掛了會自動補 |
| Service | 穩定的網路入口,把流量導到後面的 Pod(Pod IP 會變,Service 不會) |
| PVC | PersistentVolumeClaim,向 K8s 申請一塊持久化儲存空間 |
容易搞混的點
- Pod 不是 VM:它是一個執行中的應用單位,不是完整主機
- Service 不是程式:它只轉發流量,不會幫你跑 Ollama
- Deployment 不是 Pod:Deployment 管 Pod,更新 Deployment 底下 Pod 可能被換掉
- 容器裡的資料預設不可靠:所以模型要掛 PVC
- YAML 不是腳本:不是一步步執行,而是描述目標狀態
三、這次的架構
Mac(你的電腦)
│
│ HTTP API
▼
Node IP : NodePort (31144)
│
▼
Kubernetes Service
│
▼
Ollama Pod (port 11434)
│
▼
/root/.ollama → 掛到 PVC(模型存這裡)
只需要 3 個 K8s 資源:
- PVC — 準備硬碟存模型
- Deployment — 跑 Ollama 程式
- Service (NodePort) — 讓外面打得到
一個 Ollama 服務就能管多個 model,不需要開兩個 Pod。呼叫時指定 model 名稱即可。
四、部署前先跟同事確認
- namespace 名稱是什麼
- 能不能建 PVC(不能的話每次 Pod 重建都要重抓模型)
- 能不能開 NodePort(有些公司會限制,預設範圍 30000-32767)
- 哪台 Node IP 你的 Mac 可以 ping 到
- 那台機器有沒有 GPU
五、完整 YAML(帶逐行註解)
把 YOUR_NAMESPACE 換成你的 namespace 名稱後直接用。
# ============================================
# 第一段:PVC — 申請持久化儲存空間
# ============================================
apiVersion: v1 # K8s API 版本
kind: PersistentVolumeClaim # 資源類型:持久化儲存申請
metadata:
name: ollama-models-pvc # PVC 名稱,等等 Deployment 會引用
namespace: YOUR_NAMESPACE # 你的 namespace
spec:
accessModes:
- ReadWriteOnce # 同一時間給一個 node 讀寫掛載(單 Pod 夠用)
resources:
requests:
storage: 80Gi # 申請 80Gi,兩個 7B 模型大概要這麼多
---
# ============================================
# 第二段:Deployment — 跑 Ollama 服務
# ============================================
apiVersion: apps/v1 # Deployment 用的 API 版本
kind: Deployment # 資源類型:部署管理器
metadata:
name: ollama # Deployment 名稱
namespace: YOUR_NAMESPACE
spec:
replicas: 1 # 維持 1 個 Pod(掛了會自動補)
selector:
matchLabels:
app: ollama # 用 label 選擇要管理的 Pod
template: # 以下定義 Pod 的模板
metadata:
labels:
app: ollama # Pod 的標籤,要跟上面 selector 對上
spec:
containers:
- name: ollama # container 名稱
image: ollama/ollama:latest # 使用的 Docker image
imagePullPolicy: IfNotPresent # 本機有就不重抓
ports:
- containerPort: 11434 # 告訴 K8s 這個 container 用 11434 port
env:
- name: OLLAMA_HOST
value: "0.0.0.0:11434" # 重要!預設只聽 127.0.0.1,改成 0.0.0.0 才能被外部連到
volumeMounts:
- name: ollama-models
mountPath: /root/.ollama # Ollama 預設存模型的路徑,掛到 PVC
resources:
requests: # 最少需要的資源(排程用)
cpu: "2"
memory: "8Gi"
limits: # 最多可用的資源(超過會被限制/kill)
cpu: "4"
memory: "16Gi"
volumes:
- name: ollama-models # volume 名稱,跟上面 volumeMounts 對應
persistentVolumeClaim:
claimName: ollama-models-pvc # 引用第一段建的 PVC
---
# ============================================
# 第三段:Service — 對外暴露服務
# ============================================
apiVersion: v1
kind: Service
metadata:
name: ollama-nodeport # Service 名稱
namespace: YOUR_NAMESPACE
spec:
type: NodePort # 在每台 node 開一個固定 port 對外
selector:
app: ollama # 把流量導給 label 是 app=ollama 的 Pod
ports:
- name: http
port: 11434 # Service 自己的 port(cluster 內用)
targetPort: 11434 # 轉發到 Pod/container 的 port
nodePort: 31144 # 對外開在 node 上的 port(Mac 打這個)
Port 三層關係
Mac 打 → NodeIP:31144 (nodePort)
↓
Service:11434 (port,cluster 內用)
↓
Pod:11434 (targetPort,container 真正聽的)
六、部署步驟
Step 1:套用 YAML
kubectl apply -f ollama.yaml
Step 2:確認資源都起來了
# 看 Pod 狀態(要等到 Running)
kubectl get pod -n YOUR_NAMESPACE -o wide
# 看 Service
kubectl get svc -n YOUR_NAMESPACE
# 看 PVC(狀態要是 Bound)
kubectl get pvc -n YOUR_NAMESPACE
Step 3:看 log 確認 Ollama 正常啟動
kubectl logs -n YOUR_NAMESPACE deploy/ollama -f
Step 4:進 Pod 拉模型
kubectl exec -it -n YOUR_NAMESPACE deploy/ollama -- sh
進去後:
ollama pull qwen2.5:7b
ollama pull qwen2.5vl:7b
ollama list # 確認兩個模型都在
Step 5:找 Node IP
kubectl get nodes -o wide
找到 node 的 INTERNAL-IP,例如 192.168.1.50。
Step 6:從 Mac 測試
# 確認服務活著
curl http://192.168.1.50:31144/api/tags
# 呼叫純文字模型
curl http://192.168.1.50:31144/api/generate -d '{
"model": "qwen2.5:7b",
"prompt": "你好",
"stream": false
}'
# 用 chat 格式
curl http://192.168.1.50:31144/api/chat -d '{
"model": "qwen2.5:7b",
"messages": [
{ "role": "user", "content": "幫我列出三個導入地端 LLM 的注意事項" }
],
"stream": false
}'
七、如果不能開 NodePort
用 port-forward 替代(開發測試用,不是正式方案):
kubectl port-forward -n YOUR_NAMESPACE svc/ollama-nodeport 11434:11434 --address 0.0.0.0
然後 Mac 打 http://127.0.0.1:11434/api/tags。
八、如果有 GPU
需要叢集已裝好 NVIDIA device plugin,然後在 Deployment 的 resources 加:
resources:
limits:
nvidia.com/gpu: "1"
沒裝 device plugin 的話加了也沒用,先跟同事確認。
九、常用 kubectl 指令速查
# 看 namespace 裡所有資源
kubectl get all -n YOUR_NAMESPACE
# 看 Pod 詳細狀態(排錯用)
kubectl describe pod <pod-name> -n YOUR_NAMESPACE
# 看 Service 詳細
kubectl describe svc ollama-nodeport -n YOUR_NAMESPACE
# 看 PVC 詳細
kubectl describe pvc ollama-models-pvc -n YOUR_NAMESPACE
# 看 log
kubectl logs -n YOUR_NAMESPACE deploy/ollama -f
# 進 Pod
kubectl exec -it -n YOUR_NAMESPACE deploy/ollama -- sh
十、常見問題排查
| 問題 | 可能原因 |
|---|---|
| Pod 一直 Pending | CPU/memory request 太高、namespace quota 不夠、沒有可用 node、PVC 沒綁成功 |
| Pod 起來又掛掉 | 記憶體不足(OOMKilled)、image 啟動失敗、OLLAMA_HOST 沒設對 |
| Mac 打不到 | NodePort 被防火牆擋、打錯 node IP、Service selector 沒對到 Pod、Pod 沒 ready |
| 模型不見了 | 沒掛 PVC、掛載路徑錯、Pod 重建用到新的空 volume |
十一、第一階段驗收標準
完成以下 4 件事就算成功:
kubectl get pod顯示 ollama 狀態是 Runningkubectl exec進 Pod 後ollama list看到qwen2.5:7b和qwen2.5vl:7b- Mac 執行
curl http://<node-ip>:31144/api/tags有回應 - Mac 呼叫
qwen2.5:7b的 generate API 能拿到回覆
十二、注意事項
- Ollama 模型是按需載入:
ollama list有模型不代表常駐記憶體,用到時才載入,反而省資源 - qwen2.5vl:7b(視覺模型)比純文字更吃記憶體:RAM 不夠的話兩個模型可能無法同時順暢跑
- NodePort 通了不代表所有 node 都能通:要看 Pod 排到哪台 node、防火牆規則
- PVC 很重要:沒掛的話 Pod 重建就要重抓模型,浪費時間
十三、Kubernetes vs Docker 對照
| Docker(你自己手動) | Kubernetes(交給平台) |
|---|---|
docker run |
Deployment |
-v mount volume |
PVC + volumeMounts |
-p publish port |
Service (NodePort) |
--restart=always |
Deployment replicas |
| 手動管理 | 宣告式,K8s 自動維護 |
十四、第一階段先懂這五個就夠
- Pod — 最小執行單位
- Deployment — Pod 的管理器
- Service — 穩定的網路入口
- PVC — 持久化儲存
- Namespace — 隔離空間
其他(Ingress、ConfigMap、Secret、StatefulSet、Helm、HPA…)之後再學。