项目介绍:https://www.rancher.cn/longhorn/

Longhorn 是 Kubernetes 云原生分布式块存储解决方案。

Longhorn 支持以下的的 Access Modes:

  • ReadWriteOnce (RWO)
  • ReadWriteOncePod (RWOP)
  • ReadWriteMany (RWX)

Longhorn 不支持 ReadOnlyMany (ROX),但可以通过以只读方式挂载 ReadWriteMany。比如:

# PVC 用 RWX(不是 ROX)
spec:
  accessModes:
    - ReadWriteMany

# Pod 中设置只读挂载
volumeMounts:
  - name: data
    mountPath: /data
    readOnly: true 

技术文档:https://longhorn.io/docs/

本案例使用 helm 部署一个 1.12.0 版本的单节点测试示例。也可以使用 kubectl 部署

前提

节点 IP:192.168.4.250

物理节点名称:k8s-Longhorn

kubeadm 版本:v1.35.5

kubernetes node 节点名称:k8s-longhorn

在这台单节点的机器中,设置了一个单独的硬盘给 Longhorn 使用。挂载目录为/data/longhorn:

lsblk -f /dev/nvme0n2p1 
NAME      FSTYPE FSVER LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINTS
nvme0n2p1 ext4   1.0         520428ce-b354-4f88-8cd9-8ac0106ccb48   55.7G     0% /data/longhorn

安装依赖组件

安装 open-iscsi

kubernetes 其他数据节点和 longhorn 都需要安装 open-iscsi:

dnf install -y iscsi-initiator-utils

systemctl enable --now iscsid

安装 nfs

ReadWriteMany (RWX) 类型的卷底层需要 NFS 协议,为每个 RWX 卷启动一个专用的 NFS 服务器,从而让多个 Pod 可以同时读写同一个卷,所以需要安装 nfs-utils:

dnf install -y nfs-utils

当您创建一个访问模式为 ReadWriteMany 的 PVC 时,Longhorn 会在 longhorn-system 命名空间中自动创建一个名为 share-manager-<卷名> 的 Pod。这个 Pod 内部运行着一个 NFSv4 服务器,它通过 NFS 协议将底层的 Longhorn 块卷共享出来 。随后,无论您的业务 Pod 调度到哪个节点,都可以通过网络挂载这个 NFS 共享,实现多节点、多 Pod 的并发读写。

设置污点

Longhorn 专用节点打上污点,隔离存储节点,阻止普通应用调度上去:

kubectl taint nodes k8s-longhorn Longhorn=true:NoSchedule

设置 Label

对所有需要使用 Longhorn 的节点设置 label:

kubectl label nodes k8s-longhorn storage=longhorn
kubectl label nodes k8s-node01 storage=longhorn
kubectl label nodes k8s-node02 storage=longhorn

设置默认存储数据节点的 label:

kubectl label nodes k8s-longhorn default-storage-node=true

部署 Longhorn

添加 repo

添加 Longhorn Helm 存储库:

helm repo add longhorn https://charts.longhorn.io

从存储库中获取更新:

helm repo update

搜索 longhorn:

# helm search repo longhorn/longhorn
NAME             	CHART VERSION	APP VERSION	DESCRIPTION                                       
longhorn/longhorn	1.12.0       	v1.12.0    	Longhorn is a distributed block storage system ...

拉取 longhorn 并解压:

helm pull longhorn/longhorn
tar xzf longhorn-1.12.0.tgz

修改安装参数

设置 tolerations

如果希望创建专用于 Longhorn 的节点(仅用于存储副本数据),并为其分配大容量存储空间和/或 CPU 资源,同时拒绝其他通用工作负载,则可以对这些节点添加污点(taint),并为 Longhorn 组件配置相应的容忍(toleration)。这样,Longhorn 就可以部署到这些节点上。

  1. 使用 Helm 安装 Longhorn,为用户部署的组件(例如 Longhorn Manager、Longhorn Driver 和 Longhorn UI)设置污点容忍度。在安装 chart 之前更改 value. yaml 文件中的global.tolerations, longhornManager.tolerations, longhornUI.tolerations, longhornDriver.tolerations的 Helm 值:
  tolerations:
  - key: "Longhorn"
    operator: "Equal"
    value: "true"
    effect: "NoSchedule"
  1. 使用 Helm 安装 Longhorn,为系统管理的组件(例如,Instance Manager, CSI Driver, 和 Engine images)设置污点容忍度。更改 value. yaml 文件中的defaultSettings.taintToleration值:
taintToleration: Longhorn=true:NoSchedule

注意:这个默认一定要设置,否则 Engine images 等一些系统 DaemonSet 组件无法在有污点的节点上运行。比如kubectl get engineimages.longhorn.io -n longhorn-system查询 longhorn-engine 的状态一直在 deploying 部署中。

更多信息:https://longhorn.io/docs/1.12.0/advanced-resources/deploy/taint-toleration/

设置 nodeSelector

  1. 如果想把 Longhorn 的系统组件部署到专用节点上(例如 Longhorn Manager、Longhorn Driver 和 Longhorn UI),则需要对这些组件设置 nodeSelector。使用 Helm 安装 Longhorn,在安装 chart 之前更改 value. yaml 文件中的global.nodeSelector, longhornManager.nodeSelector, longhornUI.nodeSelector, longhornDriver.nodeSelector的 Helm 值:
  nodeSelector:
    storage: longhorn
  1. 使用 Helm 安装 Longhorn,为系统管理的组件(例如,Instance Manager,Backing Image Manager,Share Manager,CSI Driver,和 Engine Image)。更改 value. yaml 文件中的defaultSettings.taintToleration值:
systemManagedComponentsNodeSelector: "storage=longhorn"

更多信息:https://longhorn.io/docs/1.12.0/advanced-resources/deploy/node-selector/

设置默认数据路径

在安装 chart 之前更改 value. yaml 文件中的defaultSettings.defaultDataPath

defaultDataPath: /data/longhorn

修改默认存储数据的节点

设置 Longhorn 仅使用具有指定标签的节点来存储卷数据:

  defaultNodeSelector:
    enable: true
    selector: "default-storage-node"

如后面需要修改节点标签,除了修改这里,还需要修改 Longhorn 节点对象中的标签信息,kubectl edit nodes.longhorn.io -n longhorn-system k8s-longhorn

spec:
  tags:
  - default-storage-node

修改副本数

由于使用的是单节点,修改默认副本数defaultSettings.defaultReplicaCountpersistence.defaultClassReplicaCount为 1:

persistence:
  defaultReplicaCount: 1

defaultSettings:
  defaultClassReplicaCount: 1
  • <font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">persistence</font> 部分,属于StorageClass设置。主要影响使用默认StorageClass动态创建的持久卷(PV)
  • <font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">defaultSettings</font> 部分,属于系统默认设置。主要影响通过Longhorn UI或API创建的卷

两者使用区别如下:

  1. 如果您主要使用Kubernetes的PVC(PersistentVolumeClaim)来管理存储:
    • 您应该关注 persistence.defaultClassReplicaCount。它会修改Longhorn安装时自动创建的默认StorageClass的副本数配置。
    • 如果您需要更精细的控制,也可以不依赖默认StorageClass,而是创建自定义的StorageClass,并在其中明确指定numberOfReplicas参数。
  2. 如果您习惯使用Longhorn UI或命令行工具(如longhornctl)来直接操作卷:
    • 那么 defaultSettings.defaultReplicaCount 会生效。它设定了通过UI或API创建新卷时的默认副本数,您仍然可以在创建时单独修改每个卷的副本数。

其他更多参数,参考https://longhorn.io/docs/1.12.0/references/helm-values/

安装 Longhorn

kubectl create namespace longhorn-system
helm install -n longhorn-system longhorn .

安装完成后,正常的节点状态:

# kubectl get nodes.longhorn.io -n longhorn-system
NAME           READY   ALLOWSCHEDULING   SCHEDULABLE   AGE
k8s-longhorn   True    true              True          20m
k8s-node01     True    true              True          20m
k8s-node02     True    true              True          20m

Longhorn 在创建卷时,每个节点都需要运行一个 Engine 进程来处理数据操作,

# kubectl get engineimage -n longhorn-system
NAME          INCOMPATIBLE   STATE      IMAGE                                          REFCOUNT   BUILDDATE   AGE
ei-a4d05f02   false          deployed   docker.io/longhornio/longhorn-engine:v1.12.0   3          53d         110m

查看输出中 STATE 列是否为deployed。如果显示DeployingError,说明镜像还没准备好,或有异常。

Engine Image 的数量也应有 3 个:

# kubectl get pods -n longhorn-system -l app=longhorn-manager
NAME                     READY   STATUS    RESTARTS      AGE
longhorn-manager-7cr5w   2/2     Running   1 (11m ago)   12m
longhorn-manager-9xvr7   2/2     Running   0             12m
longhorn-manager-sc4hk   2/2     Running   0             12m

测试使用

部署 pvc

pvc.yaml 示例如下:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: longhorn-pvc-claim
spec:
  storageClassName: longhorn
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi

部署后 pvc 和 pv 已动态绑定:

# kubectl get pvc
NAME                 STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
longhorn-pvc-claim   Bound    pvc-8124476b-1fa1-4191-b949-67a4c22d37b1   3Gi        RWO            longhorn       <unset>                 6s
#
# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                        STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
pvc-8124476b-1fa1-4191-b949-67a4c22d37b1   3Gi        RWO            Delete           Bound    default/longhorn-pvc-claim   longhorn       <unset>                          13s

部署 nginx

nginx.yaml 示例如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx:1.30.0
        name: nginx
        volumeMounts:
          - mountPath: "/usr/share/nginx/html"
            name: task-pv-storage  # volume 的名称
      volumes:
        - name: task-pv-storage  # volume 的名称
          persistentVolumeClaim:
            claimName: longhorn-pvc-claim  # PVC 的名称

部署这个 deployment 后,查看 pod 的 Event 事件可看到 Attach 关联上:

Events:
  Type    Reason                  Age   From                     Message
  ----    ------                  ----  ----                     -------
  Normal  Scheduled               36s   default-scheduler        Successfully assigned default/nginx-584b8c5fcb-ztwfv to k8s-node02
  Normal  SuccessfulAttachVolume  20s   attachdetach-controller  AttachVolume.Attach succeeded for volume "pvc-8124476b-1fa1-4191-b949-67a4c22d37b1"
  Normal  Pulling                 18s   kubelet                  spec.containers{nginx}: Pulling image "nginx:1.30.0"
  Normal  Pulled                  9s    kubelet                  spec.containers{nginx}: Successfully pulled image "nginx:1.30.0" in 8.446s (8.446s including waiting). Image size: 62964182 bytes.
  Normal  Created                 9s    kubelet                  spec.containers{nginx}: Container created
  Normal  Started                 9s    kubelet                  spec.containers{nginx}: Container started

查看

在 longhorn 节点上,可以查看到 pvc 的卷数据:

# ls -lh /data/longhorn/replicas/pvc-471c68a7-c311-489c-8030-56c48f47849e-46e2fd68/
total 114M
-rw-r--r-- 1 root root 3.0G Jul 26 01:50 volume-head-000.img
-rw-r--r-- 1 root root  126 Jul 26 01:49 volume-head-000.img.meta
-rw-r--r-- 1 root root  160 Jul 26 01:49 volume.meta