Files
Deploy-Laboratory/docs/03-07-k3s-longhorn-持久化存储.md
2026-03-29 09:08:01 +08:00

25 KiB
Raw Blame History

03-07-k3s Longhorn 持久化存储4 节点实验环境)

本实验:K3s 四节点集群每节点 8 GiB 内存(与下文「系统资源」建议档一致)、磁盘基线为约 10G 系统盘 + 32G 数据盘/storage 必须挂在独立数据盘上(与 / 不同设备),详见 00-02-部署环境说明.md没有 NFS,用 Longhorn 做集群内动态块存储;若后续要部署 GitLab 等重状态系统,可在此基础上接 PVC。副本数可按实验目标在「省空间」与「模拟高可用」之间取舍。

TL;DR

  • 自动化验收./ansible/bin/verify.sh run 03-07
  • 关键前置:按本文「前置条件」准备环境变量/Secret/入口 IP
  • 成功判据:达到本文「预期」且 playbook 断言通过
  • 排障:见本文「排障」

磁盘与 /storage 前提

Longhorn 与 K3s 的 containerd/镜像、local-path 都会大量占用 k3s_data_dir(本仓库默认 /storage。若 /storage 只是根分区上的普通目录,控制节点易出现 DiskPressure / Evicted

  • 请先阅读00-02-部署环境说明.md(四节点统一拓扑、自检命令、推荐 playbook 顺序)。
  • 自检(每台节点):mountpoint -q /storage && findmnt -n -o SOURCE /findmnt -n -o SOURCE /storage 输出须不同
  • Ansible01-05.yml -e k3s_do_install=truek3s_verify_storage_mount: truegroup_vars/all.yml 默认)时会在安装前校验上述条件;可选先跑 01-05.yml -e k3s_do_prepare_storage=true -e k3s_prepare_storage=true 准备第二块盘,见 01-05-节点初始化-ansible-实践.md
  • Longhorn 数据目录建议为 /storage/longhorn(与 Helm values-lab.yaml 一致),勿与系统盘混用。

容量与副本数:每节点数据盘约 32G 时,defaultReplicaCount2 或 3 会使同一份逻辑卷在集群内占用 多倍物理空间(各副本落在不同节点上各占一份),且 Longhorn 元数据与系统组件仍有开销;实验环境可先用副本 1,要演练跨节点冗余再调高并预留磁盘。


为什么要用 Longhorn而不是 hostPath / local-path / 容器文件系统)

  • 容器文件系统Pod 重建即丢,基本不可用
  • hostPath 固定目录:能落盘,但和调度强绑定,迁移/扩缩容/备份都更麻烦
  • local-path PVC(见 03-05-k3s-local-path-pvc.mdK3s 自带,单副本够用;无快照/备份,多副本需 NFS 或 Longhorn
  • LonghornCSI 块存储):对 K8s 来说是标准 PVC即使你只设 副本数=1,也能获得:
    • 统一的 PVC 管理与回收策略
    • 快照snapshot
    • 备份backup target可推到对象存储

重要:副本数=1 时,卷只在集群里有一份数据,节点故障仍可能丢卷。四节点集群里若要演练节点级容灾,把卷的副本数调到 2 或 3并配合备份到外部介质。


前置条件CentOS

每一台计划参与 Longhorn 的节点上安装依赖(本实验为 4 节点,通常四台都要装;若你明确只让部分节点承担存储,也至少要保证这些节点已装齐):

sudo yum install -y iscsi-initiator-utils nfs-utils
sudo systemctl enable --now iscsid

准备数据盘目录(你已有专用盘挂载到 /storage,建议给 Longhorn 单独目录):

sudo mkdir -p /storage/longhorn
sudo chmod 700 /storage/longhorn

系统资源CPU / 内存)

Longhorn 没有在文档里给一张适用于所有场景的「每节点必须 ≥X 核、≥Y GB 内存」的固定表。安装前置、磁盘与网络等见官方 Installation requirementsv1.7.2Best practices - Minimum recommended hardware(其中对磁盘 IOPS/吞吐、SSD 建议、节点间带宽等描述较多)。下面按「能规划容量」和「能对照官方默认值」两部分说明。

CPU与官方默认机制相关

  • 使用默认的 V1 数据引擎时,全局设置 Guaranteed Instance Manager CPU 默认值为 12:表示为每个 Instance Manager Pod 预留该节点 可分配 CPUallocatable的 12%,用于保障引擎与副本稳定(详见 Best practices - V1 Data Engine)。
  • 节点 CPU 越小在其它负载不变时Longhorn 与业务争抢余量的压力越大;卷多、重建/备份繁忙时更明显。

实验环境规划(在 K3s 与系统开销之外,仍能跑少量业务)

档位 每节点 CPU 说明
建议 ≥ 4 vCPU 与常见实验/小型业务负载较匹配
最低(仅验证组件) 2 vCPU 可能频繁触顶、调度排队;与 Instance Manager 预留叠加后余量偏紧

内存

  • longhorn-manager、CSI、engine-image 等常驻组件外,Instance Manager 等会随卷、I/O、副本数、备份与重建任务变化;官方不以「每节点固定 GB」一条公式概括。
  • 若启用 V2 数据引擎,节点还需按官方说明预留 Huge Pages 等(见 V2 前置条件),与 V1 场景不同,勿与下表混用。

实验环境规划V1、常规用途

档位 每节点内存(约) 说明
建议 ≥ 8 GiB 留给 OS、kubelet、系统 Pod、Longhorn、业务
偏低 4 GiB 仅适合极简演示;卷与业务稍多时 OOM 风险

ARM64、整机只有 4 GiB 内存的设备Longhorn 支持 ARM64见官方 Best practices - Architecture),限制主要来自 总内存与 CPU 余量不是「ARM 能不能装」。在 4 GiB 整机上还要跑 K3s 系统组件 + Longhorn DaemonSet/CSI/Instance Manager 时,不建议把该节点当作 Longhorn 的常规存储节点(更不适合再叠加重状态业务)。更稳妥的做法是:存储用 03-05-k3s-local-path-pvc.md 的 local-path 或明确路径的 hostPath或仅在 ≥8 GiB 的节点上跑 Longhorn4 GiB 的 ARM 板尽量只做计算节点并在 Longhorn UI 中禁用其磁盘/不参与副本(若仍装 Longhorn 组件,至少减轻数据面压力)。

官方仓库中的「中等规模」性能测试参考节点8 vCPU、32 GB RAM(用于可复现压测,不是最低装机要求),见 medium node spec

部署后自检(推荐)

已安装 metrics-server 时:

kubectl top node
kubectl top pod -n longhorn-system

未安装时,至少查看节点 Allocatable 与 Longhorn Pod 分布:

kubectl describe node
kubectl get pod -n longhorn-system -o wide

SSH 部署试跑顺序4×8 GiB

当前规格 四台、各 8 GiB,适合按本篇做一次完整试跑:kubectl apply 只在控制节点执行一次;四台都要完成 iSCSI/NFS 依赖与数据目录(见「前置条件」)。

与本仓库 Ansible 清单对应时,主机名为 ylc61(控制 / k3s_server)、ylc62ylc64(工作节点),见 ansible/inventory.ini。下列命令里的 Host 请换成你 SSH 已通 的名字(或 root@192.168.2.61 等形式)。

SSH 配置说明(本机能否直连「各节点」)

  • ylc61(控制节点):常见做法是在本机 ~/.ssh/config 里配置 Host ylc61IdentityFile 指向该节点专用私钥(例如仓库内 .ssh/id_ed25519_k3s_192.168.2.61,与 01-05 / 建链脚本一致)。配好后可 ssh ylc61,并在其上执行 kubectl(设好 KUBECONFIG不必强求本机安装 kubectl 或直连 API Server。
  • ylc62ylc64(工作节点)ansible/inventory.ini 里为每台配置了不同ansible_ssh_private_key_file(如 ~/.ssh/id_ed25519_k3s_192.168.2.62 …)。若本机 ~/.ssh/config 没有对应 Host ylc62 …,则 ssh ylc62Permission denied(用错成控制节点密钥时尤其常见)。需要本机循环 SSH 四台时,请为 6264 各写一段 HostIdentityFile 与清单路径一致。
  • 只做 Longhorn 安装与排查时:多数步骤只需 ssh ylc61 + kubectl;只有要到具体工作节点执行 ctr 预拉镜像、看 kubelet/containerd 时,才必须能登录该节点(直连、串口、或 Ansible -l ylc63 等均可)。

~/.ssh/config 示例(路径按你机器上实际私钥位置修改):

Host ylc62
    HostName 192.168.2.62
    User root
    IdentityFile ~/.ssh/id_ed25519_k3s_192.168.2.62

Host ylc63
    HostName 192.168.2.63
    User root
    IdentityFile ~/.ssh/id_ed25519_k3s_192.168.2.63

Host ylc64
    HostName 192.168.2.64
    User root
    IdentityFile ~/.ssh/id_ed25519_k3s_192.168.2.64

1. 四台节点:依赖 + 目录(从办公机循环 SSH

for h in ylc61 ylc62 ylc63 ylc64; do
  echo "=== $h ==="
  ssh "$h" 'sudo bash -s' <<'REMOTE'
if command -v dnf >/dev/null 2>&1; then
  dnf install -y iscsi-initiator-utils nfs-utils
else
  yum install -y iscsi-initiator-utils nfs-utils
fi
systemctl enable --now iscsid
mkdir -p /storage/longhorn
chmod 700 /storage/longhorn
REMOTE
done

2. 控制节点:安装 Longhorn只做一次

首选Helm + 本仓库 values-lab.yaml(与 K3s 常见实践一致,版本与实验室变量集中在 ansible/group_vars/all.ymllonghorn_chart_version)。

  • Ansible推荐:在控制机执行(与 01-05 顺序一致):
cd ansible
ansible-playbook -i inventory.ini playbooks/verify/03-07.yml

该 playbook 会在各节点安装 iSCSI/NFS 依赖、在控制节点安装 Helmdnf/yumhelm 包则需按 Helm 安装文档手工安装后重跑)、再 helm upgrade --install。values 真源:ansible/files/03-07/values-lab.yaml。可选:longhorn_apply_local_path_lab: true 时一并应用 03-05 中的 local-path 实验室 ConfigMap。

  • 手工 Helm(在 ylc61 或任意已配置 KUBECONFIG 的机器上kubeconfig 见 01-01-k3s-控制节点含traefik.md
ssh ylc61
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml

helm repo add longhorn https://charts.longhorn.io
helm repo update
# 将仓库内 ansible/files/03-07/values-lab.yaml 拷到本机路径后:
helm upgrade --install longhorn longhorn/longhorn \
  --namespace longhorn-system --create-namespace \
  -f ./values-lab.yaml \
  --version 1.7.2 \
  --wait --timeout 15m

Helm 安装时 values-lab.yaml 已包含 defaultDataPathdefaultReplicaCount、默认 StorageClass 等,一般无需再执行下文针对 kubectl apply 方式的 default-data-path / StorageClass patch

备选:官方清单 kubectl apply(无 Helm 或需与旧文档对齐时使用):

export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.7.2/deploy/longhorn.yaml
kubectl -n longhorn-system rollout status deploy/longhorn-ui --timeout=300s

然后按下文 「安装 Longhorn」kubectl 路径default-data-path 补丁默认 StorageClass 等。若经 Windows 再 SSH 导致 JSON patch 失败,用文中 python3 补丁 段落。

Longhorn UI 访问手动验证与下文各节相同。

3. 试跑成功判据(最短)

  • kubectl -n longhorn-system get pod 中关键 Pod 为 Running(四节点时 DaemonSet 应对齐节点数;若有 ImagePullBackOff,到对应节点 ctr 预拉镜像,见「手动验证」)。
  • 能打开 UIport-forward 或临时 NodePortNode 页可见四节点。
  • 可选下文「最小读写验证」PVC + Pod 写文件通过。

安装 Longhorn

不是在每台机器上各执行一遍安装:无论 Helm 还是 kubectl apply对整个集群只做一次(在能访问 API 的机器上执行即可Longhorn 的控制面、CSI、DaemonSet 等会由 Kubernetes 统一下发。

每台节点仍要做的是:前文「磁盘前提」「前置条件」里的 OS 依赖/storage/longhorn 目录Ansible 03-07.yml 会创建)。若节点在集群内且未被 cordonLonghorn 的 DaemonSet 往往也会在各节点拉起组件 Pod因此各节点通常都要能 拉取镜像;只有「存数据」可以只在部分节点上开启(见下文「只让有大盘的节点承载数据」)。

首选Helm + values-lab.yaml

与上文 **「SSH 试跑顺序 §2」**一致:使用 ansible-playbook ... playbooks/verify/03-07.yml(或 labs/longhorn-stack.yml)或手工 helm upgrade --install ... -f values-lab.yaml。Chart 仓库:https://charts.longhorn.iovalues 字段以 Longhorn Helm Chart 当前版本为准。

备选:kubectl apply 官方清单

未使用 Helm 时:

kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.7.2/deploy/longhorn.yaml
kubectl -n longhorn-system rollout status deploy/longhorn-ui

将 Longhorn 默认数据路径改到 /storage/longhorn

kubectl -n longhorn-system patch settings.longhorn.io default-data-path \
  --type=merge -p '{"value":"/storage/longhorn"}'

若出现 invalid character ':' in string escape code(多经一层 SSH/脚本时 JSON 被错误转义),在控制节点用下面方式补丁(不依赖引号嵌套):

python3 - <<'PY'
import subprocess, json
patch = json.dumps({"value": "/storage/longhorn"})
subprocess.check_call([
    "kubectl", "-n", "longhorn-system", "patch", "settings.longhorn.io", "default-data-path",
    "--type=merge", "-p", patch,
])
subprocess.run([
    "kubectl", "-n", "longhorn-system", "get", "settings.longhorn.io", "default-data-path",
    "-o", "jsonpath={.status.value}{'\n'}",
])
PY

longhorn 设为默认 StorageClass推荐

kubectl get storageclass
kubectl patch storageclass longhorn -p '{"metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

patch storageclass 同样因转义失败,可改用:

python3 - <<'PY'
import subprocess, json
p = json.dumps({"metadata": {"annotations": {"storageclass.kubernetes.io/is-default-class": "true"}}})
subprocess.check_call(["kubectl", "patch", "storageclass", "longhorn", "-p", p])
PY

Longhorn UI部署与访问

是否需要单独部署

不需要。 官方 longhorn.yaml 里已包含 Deployment longhorn-uiService longhorn-frontend(命名空间 longhorn-system)。执行上文 kubectl apply -f .../longhorn.yamlUI 会与其它组件一起创建;安装阶段里的 kubectl ... rollout status deploy/longhorn-ui 就是在等 UI 就绪。

若 UI 长期不 Ready优先按节点查 镜像拉取Pod 事件(见下文「手动验证」)。

部署与网络对象核验

kubectl -n longhorn-system get deploy longhorn-ui
kubectl -n longhorn-system get svc longhorn-frontend
kubectl -n longhorn-system get pod -l app=longhorn-ui -o wide
kubectl -n longhorn-system rollout status deploy/longhorn-ui --timeout=300s

longhorn-frontend 默认多为 ClusterIP,集群外浏览器不能直接访问 Service VIP需用 port-forwardNodePortIngress 之一。

访问方式一:kubectl port-forward(实验环境推荐)

已配置 kubeconfig、能访问 API 的机器上执行(可以是办公机,也可以是某台 SSH 上去的控制节点):

kubectl -n longhorn-system port-forward svc/longhorn-frontend 8080:80

浏览器打开:http://127.0.0.1:8080

常见场景

  • 只在控制节点上有 kubectl:先 SSH 到控制节点执行上述命令,再用浏览器访问——若浏览器在本地电脑,需 SSH 本地转发,例如在本机执行
    ssh -L 8080:127.0.0.1:8080 user@控制节点
    然后在控制节点上 port-forward 绑定 127.0.0.1:8080,本地浏览器仍访问 http://127.0.0.1:8080
  • 需要把转发绑到非本机接口时(慎用,内网限定):
    kubectl -n longhorn-system port-forward --address 0.0.0.0 svc/longhorn-frontend 8080:80

无图形界面时的快速验证port-forward 占用当前终端时,另开一个终端):

curl -sI http://127.0.0.1:8080 | head -n 5

能返回 HTTP/1.1 200302 等即说明 UI 服务已响应(具体状态码随版本可能略有不同)。

访问方式二:临时改为 NodePort仅实验内网

便于用「任意节点 IP + 端口」访问,不要对公网暴露

kubectl -n longhorn-system patch svc longhorn-frontend -p '{"spec":{"type":"NodePort"}}'
kubectl -n longhorn-system get svc longhorn-frontend

在输出里查看 PORT(S) 一列的 80:3xxxx/TCP,用浏览器访问:http:// + 任意节点内网 IP + : + NodePort 端口

实验结束后若要改回 ClusterIP(与默认清单一致,便于继续只用 port-forward

kubectl -n longhorn-system patch svc longhorn-frontend -p '{"spec":{"type":"ClusterIP"}}'

一般由 apiserver 重新分配 Cluster IP若与预期不符从当前版本的 longhorn.yaml 中仅重新 apply Service longhorn-frontend 那一段即可。

访问方式三Ingress + Traefik可选生产需认证与 TLS

K3s 默认带 Traefik 时,可为 longhorn-frontendIngress,用域名访问。生产或跨网段务必配 HTTPS身份认证Longhorn 支持多种认证方式,以官方文档当前版本为准),实验环境若仅用内网域名也至少限制来源网段。

界面内验证要点

打开 UI 后确认:

  • Node 页能看到集群节点,状态与磁盘信息合理(与 kubectl get nodes 对照)。
  • Volume 在创建测试 PVC 后出现对应卷(可与下文「最小读写验证」联动)。
  • 需要改默认副本数节点磁盘是否可调度时,在 Setting / Node 中操作(与上文「实验环境建议配置」一致)。

若页面无法加载或接口报错,查看 UI 与后端:

kubectl -n longhorn-system logs deploy/longhorn-ui --tail=80
kubectl -n longhorn-system describe pod -l app=longhorn-ui

实验环境建议配置4 节点)

DaemonSet 与镜像

  • longhorn-managerengine-image 等会以 DaemonSet 形式跑在多个节点上;四节点时每个节点都应对应有 Pod 最终就绪(若某台 ImagePullBackOff,先在该节点ctr 预拉镜像,见下文「手动验证」)。
  • 数据路径与 default-data-path按节点生效的:四台都要能访问你设定的目录(例如每台都有 /storage/longhorn,或只在有盘的节点上建目录并在 UI 里禁用无盘节点)。

副本数

  • 默认副本数=1:省磁盘、适合先跑通功能;四节点集群里卷仍可能只落在某一个节点上。在 每节点约 32G 数据盘 的实验室基线下,优先用 1 避免多副本占满盘。
  • 默认副本数=2 或 3:利用多节点做副本,更接近生产里的冗余;物理占用约为逻辑卷大小 × 副本数再加引擎与文件系统开销32G 盘上调高前务必在 UI 或 kubectl 中确认各节点剩余空间。
  • 迁移卷或临时演练容灾时,可对单个卷调高副本数,待同步完成再调回。

只让“有大盘”的节点承载数据

若 4 节点里只有部分机器有 /storage 或大盘:

  • 在 Longhorn UI 里把无盘或不想参与存储的节点的 disk 设为不可调度
  • 或给存储节点打标签,配合工作负载的 nodeSelector/affinity让应用尽量靠近数据

注意:副本=1 时,卷不会“随使用自动从小盘迁到大盘”,需要你手动迁移或从源头限制调度。


GitLab 这类重状态系统如何落地

原则:所有关键组件都用 PVCLonghorn

  • 必须 PVCPostgreSQL、Redis、Gitalyrepo、uploads/artifacts/packages、registry如启用
  • 备份
    • 应用层GitLab 自带 backup+ 存储层Longhorn snapshot/backup双保险

Traefik acme.json 如何持久化/备份(可选)

Traefik 的 ACME 状态很小,但也建议持久化以避免重建后触发频繁签发。

  • 推荐:给 Traefik 的 /data 使用 PVCLonghornacme.json 走标准持久化
  • 兜底:定期导出 acme.json 备份(例如 kubectl cp pod/<traefik-pod>:/data/acme.json ...

验证

kubectl -n longhorn-system get pod
kubectl get pvc -A
kubectl get pv

手动验证(按顺序,便于排障)

K3s 节点上(与 kubelet 使用同一套 containerd可先手动拉镜像区分「网络/仓库问题」与「Longhorn 配置问题」:

镜像与节点K3s 每台节点自带 containerd镜像按节点本地缓存不会在节点间自动同步。Pod 第一次调度到某节点时,该节点会自己去仓库拉取(联网正常则不必事先每台手动 ctr pull。Longhorn 的 DaemonSet 在 4 节点上各跑一份时,每台都可能要拉同一批镜像;某台若 ImagePullBackOff,到该台上执行下面的 ctr pull 做预热或排障。

# 使用 k3s 的 containerd 命名空间
CTR="sudo ctr --address /run/k3s/containerd/containerd.sock -n k8s.io"

$CTR images pull docker.io/longhornio/longhorn-manager:v1.7.2
$CTR images pull docker.io/longhornio/longhorn-ui:v1.7.2
$CTR images pull docker.io/longhornio/longhorn-share-manager:v1.7.2
$CTR images pull docker.io/longhornio/longhorn-engine:v1.7.2
$CTR images pull docker.io/longhornio/longhorn-instance-manager:v1.7.2
$CTR images pull docker.io/longhornio/backing-image-manager:v1.7.2

# 其余 longhornio/* 镜像以你安装的版本清单为准(避免 tag 与文档漂移):
# curl -sSL https://raw.githubusercontent.com/longhorn/longhorn/v1.7.2/deploy/longhorn.yaml | grep -E 'image:.*longhornio' | sort -u

# 对照:非 Longhorn 镜像(判断是否为 Docker Hub 整体不通)
$CTR images pull docker.io/library/nginx:alpine

确认控制面与 DaemonSetlonghorn-manager 每节点一份;4 节点时应看到与节点数相关的 Pod 分布,engine-image 等会陆续就绪):

kubectl -n longhorn-system get pod,deploy,ds -o wide
kubectl get nodes -o wide
kubectl -n longhorn-system rollout status deploy/longhorn-ui --timeout=300s
kubectl -n longhorn-system rollout status deploy/longhorn-driver-deployer --timeout=300s

确认默认数据路径与 StorageClass

kubectl -n longhorn-system get settings.longhorn.io default-data-path -o yaml
kubectl get storageclass

longhornlocal-path 同时带有 storageclass.kubernetes.io/is-default-class: "true",未写 storageClassName 的 PVC 行为可能不符合预期;建议只保留一个默认类,或业务 PVC 显式写 storageClassName: longhorn

最小读写验证(动态卷 + 临时 Pod

kubectl apply -f - <<'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: longhorn-pvc-smoke
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: longhorn
  resources:
    requests:
      storage: 2Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: longhorn-smoke
  namespace: default
spec:
  containers:
    - name: app
      image: busybox:1.36
      command: ["sh", "-c", "echo ok-longhorn > /data/test.txt && sleep 3600"]
      volumeMounts:
        - name: data
          mountPath: /data
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: longhorn-pvc-smoke
EOF

kubectl wait --for=condition=Ready pod/longhorn-smoke -n default --timeout=180s
kubectl exec -n default longhorn-smoke -- cat /data/test.txt

清理冒烟资源:

kubectl delete pod longhorn-smoke -n default --ignore-not-found
kubectl delete pvc longhorn-pvc-smoke -n default --ignore-not-found

Longhorn UI 的部署核对、port-forward、NodePort 与界面内验证要点见上文 「Longhorn UI部署与访问


下一步

  • 03-05-k3s-local-path-pvc.md:单副本、无快照需求时,用 K3s 自带 local-path 即可
  • 返回后续 GitOps / 业务部署(如 GitLab章节

排障

  • 先看 playbook 输出:失败时先定位是 deploy/wait/http_check 哪一步。
  • 集群侧总览kubectl get nodes -o widekubectl -n kube-system get pods -o wide
  • 事件与日志kubectl -n <ns> describe ...kubectl -n <ns> logs ... --tail=200