Longhorn 存储 Runbook:安装、查看、调试
Longhorn 是本集群的分布式块存储。它让 Pod 可以申请 PVC,Pod 重启或迁移后数据还在。
1. Longhorn 解决什么问题
没有 Longhorn 时:
- Pod 的容器文件系统随 Pod 删除而消失。
- 数据库、Jenkins、Grafana、Harbor 这类有状态服务不能稳定落盘。
- 节点坏了,本地盘上的数据很难恢复。
Longhorn 做的事:
- 提供默认 StorageClass。
- PVC 自动创建 Longhorn Volume。
- Volume 有多个 replica,分散在不同节点。
- CSI 负责把卷 attach/mount 到运行 Pod 的节点。
- UI 能查看卷、replica、snapshot、backup。
为什么选 Longhorn:学习和中小集群运维成本低,有 UI,概念比 Ceph 简单。
2. 安装前置
5 台节点都要有:
apt-get update
apt-get install -y open-iscsi nfs-common jq
systemctl enable --now iscsid
modprobe iscsi_tcp
echo iscsi_tcp > /etc/modules-load.d/iscsi_tcp.conf
mkdir -p /var/lib/longhorn
为什么:
- Longhorn RWO 块卷通过 iSCSI/engine 暴露给 kubelet。
- RWX 卷需要 NFS client。
/var/lib/longhorn是 Longhorn 数据目录。
先看清磁盘布局,别把 Longhorn 数据写到小盘上:
ssh root@<ip> 'lsblk -f -o NAME,FSTYPE,SIZE,MOUNTPOINT; df -h /'
本集群真实布局(重装后实测,和老笔记不一样):
vda 30G ← 系统盘
└─vda1 ext4 29.9G / ← 根分区,只有 ~24G 可用
vdb 100G ← 数据盘
└─vdb1 ext4 100G /www ← 100G 大盘挂在 /www
问题:Longhorn 默认数据目录是 /var/lib/longhorn,落在只有 24G 的根盘上;而 100G 大盘空在 /www。生产应让 Longhorn 用大盘。
最省事、对 runbook 其它步骤零改动的办法:用 bind mount 把 /var/lib/longhorn 指到大盘上的目录。5 台都做:
ssh root@<ip> '
mkdir -p /www/longhorn /var/lib/longhorn
mountpoint -q /var/lib/longhorn || mount --bind /www/longhorn /var/lib/longhorn
grep -q "/var/lib/longhorn" /etc/fstab || echo "/www/longhorn /var/lib/longhorn none bind 0 0" >> /etc/fstab
df -h /var/lib/longhorn # 应显示挂载源 /dev/vdb1、可用 ~80G+
'
mount --bind A B:把目录 A 再挂到路径 B,之后读写 B 实际落在 A 所在的文件系统上。写进/etc/fstab才能重启后保留。Longhorn 仍按默认路径/var/lib/longhorn工作,但字节落在 100G 的vdb1上。
⚠️ 别 mkfs /dev/vdb——它已经有数据(挂在 /www)。只在里面建子目录 /www/longhorn,不碰 /www 本身。
3. 安装 Longhorn
kubectl create namespace longhorn-system
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.7.2/deploy/longhorn.yaml
小集群要允许 Longhorn 跑在 control-plane 节点(本集群只有 2 个 worker,默认 3 副本凑不齐,必须让控制面也当存储节点):
kubectl patch setting taint-toleration -n longhorn-system --type=merge \
-p '{"value":"node-role.kubernetes.io/control-plane:NoSchedule;node-role.kubernetes.io/master:NoSchedule"}'
⚠️ 只改这个设置不够(实测坑)。
taint-toleration设置只作用于 Longhorn 系统托管组件(instance-manager / CSI 等),不会自动给longhorn.yaml部署的longhorn-managerDaemonSet 加 toleration。结果:longhorn-manager的 tolerations 仍为空,只跑在 2 个 worker 上(kubectl get ds longhorn-manager -n longhorn-system看到DESIRED 2),存储节点只有 2 个。还要直接给这三个用户部署的组件补 toleration:
TOL='[{"op":"add","path":"/spec/template/spec/tolerations","value":[ {"key":"node-role.kubernetes.io/control-plane","operator":"Exists","effect":"NoSchedule"}, {"key":"node-role.kubernetes.io/master","operator":"Exists","effect":"NoSchedule"}]}]' for r in ds/longhorn-manager deploy/longhorn-driver-deployer deploy/longhorn-ui; do kubectl patch -n longhorn-system $r --type=json -p "$TOL" done改完
longhorn-manager应扩到DESIRED 5 / READY 5,5 个节点都成为存储节点。
暴露 UI:
kubectl patch svc longhorn-frontend -n longhorn-system \
-p '{"spec":{"type":"NodePort","ports":[{"port":80,"targetPort":8000,"nodePort":31172}]}}'
设置默认 StorageClass:
kubectl patch storageclass longhorn \
-p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
4. 安装后验收
kubectl get pods -n longhorn-system -o wide
kubectl get sc
kubectl get csidrivers
kubectl get nodes.longhorn.io -n longhorn-system
kubectl get volumes.longhorn.io -n longhorn-system
真实输出(健康时):
$ kubectl get sc
NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION
longhorn (default) driver.longhorn.io Delete Immediate true
longhorn-static driver.longhorn.io Delete Immediate true
$ kubectl get csidrivers
NAME ATTACHREQUIRED PODINFOONMOUNT MODES AGE
driver.longhorn.io true true Persistent 6m
$ kubectl get nodes.longhorn.io -n longhorn-system
NAME READY ALLOWSCHEDULING
k8s-cp-1 True true ← 5 个都 True,说明 5 个存储节点就位
k8s-cp-2 True true
k8s-cp-3 True true
k8s-w-1 True true
k8s-w-2 True true
期望:
longhorn-manager每节点都有(5 个)。longhorn-csi-plugin每节点都有,且3/3 Running。driver.longhorn.io出现在kubectl get csidrivers。longhornStorageClass 是默认(名字后带(default))。
⚠️ 刚装完 / 刚改完 toleration 时,CSI 边车可能短暂 CrashLoopBackOff(实测)。
csi-attacher / csi-provisioner / csi-resizer / csi-snapshotter / longhorn-csi-plugin报:Failed to connect to the CSI driver ... /csi/csi.sock ... context deadline exceeded Failed to initialize Longhorn API client: Get "http://longhorn-backend:9500/v1/..." context deadline exceeded原因:
longhorn-manager刚因 toleration 改动在重排,longhorn-backendService 的 endpoints 短暂不稳,CSI 组件连不上后端 API。等 manager 全部2/2 Running、Service endpoints 齐了,删掉这些 CrashLoop pod 让控制器重建即可(绕过 backoff 退避):kubectl get pods -n longhorn-system --no-headers | grep -vE 'Running|Completed' \ | awk '{print $1}' | xargs -r kubectl delete pod -n longhorn-system验证后端通:
kubectl exec -n longhorn-system <任一 longhorn-manager> -c longhorn-manager -- curl -sS -o /dev/null -w '%{http_code}' http://longhorn-backend:9500/v1/,返回301/200即正常。
5. 创建 PVC 验证
kubectl create ns storage-test
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data
namespace: storage-test
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: longhorn
---
apiVersion: v1
kind: Pod
metadata:
name: writer
namespace: storage-test
spec:
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "date >> /data/hello.txt; sleep 3600"]
volumeMounts:
- mountPath: /data
name: data
volumes:
- name: data
persistentVolumeClaim:
claimName: data
EOF
kubectl wait -n storage-test --for=condition=Ready pod/writer --timeout=120s
kubectl exec -n storage-test writer -- cat /data/hello.txt
查 Longhorn 卷:
kubectl get pvc,pv -n storage-test
kubectl get volumes.longhorn.io -n longhorn-system
kubectl get replicas.longhorn.io -n longhorn-system | grep pvc-
成功长这样(真实输出):
$ kubectl exec -n storage-test writer -- cat /data/hello.txt
Thu May 28 07:40:15 UTC 2026 ← 数据真的写进卷了
$ kubectl get pvc -n storage-test
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS
data Bound pvc-efe3f0fb-38bf-42e3-a81e-1db083e4fbc3 1Gi RWO longhorn
^^^^^ Bound = 动态制备成功;PV 名 pvc-xxx 由 Longhorn 自动创建
$ kubectl get volumes.longhorn.io -n longhorn-system \
-o custom-columns=NAME:.metadata.name,STATE:.status.state,ROBUSTNESS:.status.robustness
NAME STATE ROBUSTNESS
pvc-efe3.. attached healthy ← attached=已挂到 Pod 所在节点;healthy=3 副本齐
✅ 验证点:PVC Bound + 能读到写入内容 + 卷 attached/healthy。 ⚠️ 若卡在 Pending(PVC)或 degraded(卷),分别见 7.1 和 7.5。
6. 看懂状态
kubectl get volumes.longhorn.io -n longhorn-system \
-o custom-columns=NAME:.metadata.name,STATE:.status.state,ROBUSTNESS:.status.robustness,CURRENT:.status.currentNodeID
| 字段 | 含义 |
|---|---|
STATE=detached | 卷没挂到任何节点,通常没有 Pod 在用 |
STATE=attached | 卷已经挂到某节点 |
ROBUSTNESS=healthy | 副本数满足要求 |
ROBUSTNESS=degraded | 副本不足或正在重建 |
ROBUSTNESS=unknown | manager/engine 状态不明,需要继续查 |
看到 degraded 不要马上删卷。先看 replica:
kubectl get replicas.longhorn.io -n longhorn-system \
-o custom-columns=NAME:.metadata.name,VOLUME:.spec.volumeName,NODE:.spec.nodeID,STATE:.status.currentState,FAILED:.status.failedAt
7. 常见故障
7.1 PVC 一直 Pending
kubectl describe pvc -n <ns> <pvc>
kubectl get sc
kubectl get pods -n longhorn-system
kubectl logs -n longhorn-system deploy/csi-provisioner --tail=100
常见原因:
- 没有默认 StorageClass。
- CSI provisioner 没 Running。
- Longhorn manager 不健康。
- 节点磁盘不可调度。
7.2 Pod 卡在 ContainerCreating / MountVolume
kubectl describe pod -n <ns> <pod>
kubectl get volumeattachments.storage.k8s.io | grep <pvc>
kubectl get volumes.longhorn.io -n longhorn-system | grep <pvc>
journalctl -u kubelet --since "20 min ago" --no-pager | grep -i mount
describe pod 里的 Events 最重要。常见事件:
| 事件 | 含义 | 处理 |
|---|---|---|
driver.longhorn.io not found | CSI driver 没注册好 | 查 longhorn-csi-plugin |
Multi-Attach error | RWO 卷还挂在旧节点 | 等旧 Pod 删除,必要时查 VolumeAttachment |
volume is not ready for workloads | Longhorn 卷还在恢复 | 观察 Longhorn UI / manager 日志 |
Input/output error | 卷或 engine I/O 异常 | 停止自动重试,查 Longhorn engine/replica |
7.3 Multi-Attach
RWO 卷同一时间只能挂到一个节点。单副本 Deployment 挂 RWO PVC 时,不要用默认 RollingUpdate,改 Recreate:
kubectl patch deployment -n <ns> <deploy> --type=merge \
-p '{"spec":{"strategy":{"type":"Recreate","rollingUpdate":null}}}'
如果由 Helm 管理,要把值写回 Helm values,避免下次 upgrade 覆盖。
7.4 degraded 卷
VOL=pvc-xxxx
kubectl get volumes.longhorn.io -n longhorn-system $VOL -o yaml | sed -n '1,220p'
kubectl get replicas.longhorn.io -n longhorn-system -l longhornvolume=$VOL -o wide
kubectl get engines.longhorn.io -n longhorn-system -l longhornvolume=$VOL -o yaml | grep -A20 replicaModeMap
原则:
- 业务还在 Running 时,先观察 replica rebuild,不要删 PVC。
WO通常表示副本正在写入/重建。RW表示 engine 正在读写这个 replica。- 多个副本都失败时,先做快照/备份评估,再操作。
7.5 卷一直 degraded、副本卡在 WO 重建不完 → 八成是跨节点 MTU(实测踩坑)
本集群装完后第一个 PVC 就遇到:卷 attached 但 robustness=degraded,3 个副本都 running,可 engine 的 replicaModeMap 里总有一个卡在 WO(写入/重建中),robustness 死活不转 healthy。
# 看 engine 认了几个副本是 RW(健康),几个是 WO(卡重建)
kubectl get engines.longhorn.io -n longhorn-system -o jsonpath='{.items[0].status.replicaModeMap}'; echo
# 坏的样子:{"...-r-3204":"WO","...-r-4fd6":"RW","...-r-d541":"RW"} ← 一个 WO
# 看 instance-manager 重建日志(在 WO 副本所在节点)
kubectl logs -n longhorn-system <该节点的 instance-manager> --tail=200 | grep -iE 'rebuild|sync|error'
关键报错长这样:
FailedRebuilding ... failed to sync files ... failed to write data:
Post "http://10.244.0.241:10024/v1-ssync/writeData?begin=557056&end=561152":
context deadline exceeded
注意 begin=557056——同步传到约 544KB 才超时,小数据过、大数据断。这不是 Longhorn 的问题,是跨节点 VXLAN MTU 配大了:Longhorn 副本同步走 Pod IP 直连传大块数据,MTU 一超路径就丢,重建永远完不成。
修复步骤:
按 Cilium Runbook 2.5 节 把 Cilium
MTU调到1400并重启 cilium。重启 Longhorn 的 instance-manager(旧 Pod 还是旧 MTU 的 veth,副本进程跑在里面):
# 先确保没有重要卷 attached,再删(控制器会重建) kubectl delete pod -n longhorn-system -l longhorn.io/component=instance-manager删掉旧的 degraded 卷重建(或等 Longhorn 自动重试重建)。新卷的 3 副本应很快全
RW、robustness=healthy:kubectl get volumes.longhorn.io -n longhorn-system \ -o custom-columns=NAME:.metadata.name,STATE:.status.state,ROBUSTNESS:.status.robustness # 好的样子:STATE=attached ROBUSTNESS=healthy
教训:验证 CNI 时一定要把测试 Pod 强制分到不同节点测大包,否则同节点测不出 MTU 问题,等装了 Longhorn 才暴露,排查链路长得多。
8. UI 入口
http://154.201.73.31:31172
UI 里重点看:
- Node:节点是否 schedulable、磁盘剩余。
- Volume:state、robustness、attached node。
- Replica:分布在哪些节点,是否 rebuilding。
- Event log:Longhorn 自身错误。