背景与痛点

在云原生时代,有状态应用(如数据库、消息队列、缓存系统)对存储性能有着极高的要求。传统的网络存储方案虽然提供了便利的共享能力,但往往面临以下挑战:

  • 延迟问题 │ 网络 I/O 引入额外延迟,影响实时性应用
  • 成本高昂 │ 高性能网络存储设备价格昂贵
  • 单点瓶颈 │ 网络带宽可能成为性能瓶颈
  • 管理复杂 │ 本地磁盘缺乏统一管理和调度能力

为了解决以上的痛点,经过测试和综合考虑,使用更为简单的open-local作为数据存储管理

open-local介绍

Open-Local是由多个组件构成的本地磁盘管理系统,目标是解决当前 Kubernetes 本地存储能力缺失问题。通过Open-Local,使用本地存储会像集中式存储一样简单。
目前 open-local 支持以下存储特性: 本地存储池管理、存储卷动态分配、存储调度算法扩展、存储卷扩容、存储卷快照、存储卷监控、存储卷 IO 限流、原生块设备及临时卷。

Open-Local包含四大类组件:

  • Scheduler-Extender: 作为 Kubernetes Scheduler 的扩展组件,通过 Extender 方式实现,新增本地存储调度算法
  • CSI: 按照 CSI(Container Storage Interface) 标准实现本地磁盘管理能力
  • Agent: 运行在集群中的每个节点,根据配置清单初始化存储设备,并通过上报集群中本地存储设备信息以供 Scheduler-Extender 决策调度
  • Controller: 获取集群存储初始化配置,并向运行在各个节点的 Agent 下发详细的配置清单

open-local 包含两个 CRD
1、NodeLocalStorage: open-local 通过 NodeLocalStorage 资源上报每个节点上的存储设备信息,该资源由 controller 创建,由每个节点的 agent 组件更新其 status。该 CRD 属于全局范围的资源。
2、NodeLocalStorageInitConfig: open-local controller 可通过 NodeLocalStorageInitConfig 资源创建每个 NodeLocalStorage 资源。NodeLocalStorageInitConfig 资源中包含全局默认节点配置和特定节点配置,若节点的 node label 满足表达式则使用特定节点配置,否则使用默认配置

支持的存储类型

存储类型StorageClass 名称适用场景
LVMopen-local-lvm通用场景,支持动态扩容和快照
LVM 直写open-local-lvm-xfs高性能场景,XFS 文件系统
裸设备open-local-device数据库等需要直接访问磁盘的场景
临时卷open-local-lvm-ephemeral临时数据存储

Open-Local支持两种类型本地磁盘的使用:

  • LVM: 共享盘类型,即通过 LVM 方式管理存储设备。当在 K8s 中创建 PV 资源时,Open-Local会从对应 VolumeGroup 中创建 LogicalVolume 来代表该PV。
  • Device: 独占盘类型,即一个 PV 一个块设备。这里块设备可以是一整块磁盘,也可以是一个分区。

不同类型 PV 所支持的存储能力也不同。

类型动态分配PV扩容PV快照原生块设备IO限流临时卷监控数据
LVM(共享盘类型)支持支持支持支持支持支持支持
Device(独占盘类型)支持不支持不支持支持不支持不支持支持

在这里插入图片描述

  1. 用户创建带有PVC的StatefulSet。
  2. PvController调用External Provisioner来创建PV,本地存储未创建真实的lv。
  3. 调度带有PVC的Pod时,kube-scheduler调用extender注册的接口,根据存储情况确定创建lv的节点。
  4. PvController完成PVC和PV的延迟绑定后,kube-scheduler完成调度Pod。
  5. kubelet拉起Pod时,csi创建lv,把lv挂载到PV卷目录,相当于完成了attach+mount。kubelet再把PV卷目录挂载到容器目录。

环境准备

组件最低版本推荐版本说明
Kubernetesv1.20v1.24+需开启 CSI
Helmv3.0v3.8+用于包管理
Linux 发行版-RHEL/CentOS 7+, Ubuntu 18.04+需 LVM2
空闲块设备-每个节点至少 1 块用于存储池

安装 LVM2

# Red Hat/CentOS
sudo yum install -y lvm2

# Debian/Ubuntu
sudo apt-get install -y lvm2

# 验证安装
lvm version

准备空闲块设备

# 查看可用磁盘(确保未使用)
lsblk

# 示例输出:
# NAME   MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
# sda      8:0    0  100G  0 disk
# ├─sda1   8:1    0   50G  0 part /
# ├─sda2   8:2    0   50G  0 part [SWAP]
# sdb      8:16   0   50G  0 disk  ← 目标设备,未挂载

# 重要:确保设备未被使用(无文件系统、无挂载点)
sudo wipefs -a /dev/sdb  # 清除残留签名(可选)

节点标签(可选)

# 为节点打标签,便于后续管理
kubectl label node <node-name> open-local=enabled

部署 open-local

下载 Helm Chart

# 克隆官方仓库
git clone https://github.com/alibaba/open-local.git
cd open-local

# 或直接使用 Helm 仓库(推荐)
helm repo add open-local https://alibaba.github.io/open-local
helm repo update

配置 Helm Values

编辑 helm/values.yaml 或创建自定义配置文件:

# values-custom.yaml
# values-production.yaml
name: open-local
namespace: open-local  # 推荐独立命名空间

driver: local.csi.aliyun.com  # 可保持官方,或改为 csi.open-local.io

images:
  local:
    image: registry.cn-hangzhou.aliyuncs.com/open-local/open-local
    tag: v0.8.0
  registrar:
    image: registry.cn-hangzhou.aliyuncs.com/open-local/csi-node-driver-registrar
    tag: v2.9.0
  provisioner:
    image: registry.cn-hangzhou.aliyuncs.com/open-local/csi-provisioner
    tag: v3.5.0
  resizer:
    image: registry.cn-hangzhou.aliyuncs.com/open-local/csi-resizer
    tag: v1.9.0
  snapshotter:
    image: registry.cn-hangzhou.aliyuncs.com/open-local/csi-snapshotter
    tag: v6.3.0
  snapshot_controller:
    image: registry.cn-hangzhou.aliyuncs.com/open-local/snapshot-controller
    tag: v6.3.0

agent:
  name: open-local-agent
  device: /dev/sdb  # 必须根据实际修改!
  kubelet_dir: /var/lib/kubelet
  volume_name_prefix: local
  spdk: false
  driverMode: all  # 完整功能
  max_volumes_per_node: 256

extender:
  name: open-local-scheduler-extender
  strategy: spread  # 生产环境高可用
  port: 23000
  init_job: true

controller:
  update_nls: "true"

storageclass:
  lvm:
    enabled: true
    name: open-local-lvm
    parameters:
      volumeType: "LVM"
      csi.storage.k8s.io/fstype: ext4
    reclaimPolicy: Delete
    volumeBindingMode: WaitForFirstConsumer
    allowVolumeExpansion: true

  # 按需启用其他 SC
  lvm_xfs:
    enabled: true  # 数据库场景推荐
    name: open-local-lvm-xfs
    parameters:
      volumeType: LVM
      csi.storage.k8s.io/fstype: xfs
    reclaimPolicy: Delete
    volumeBindingMode: WaitForFirstConsumer
    allowVolumeExpansion: true

  # 禁用不需要的 SC
  lvm_throttle:
    enabled: false
  device_ssd:
    enabled: false
  device_hdd:
    enabled: false

monitor:
  enabled: true  # 生产环境建议开启
  namespace: monitoring  # 你的监控命名空间

global:
  RegistryURL: registry.cn-hangzhou.aliyuncs.com/open-local  # 国内镜像源
  ImagePullPolicy: Always

配置说明

  • agent.device必须修改,指定节点上的空闲块设备
  • 多节点差异化配置:如果节点设备不同,可通过 nodeSelector 或 Helm 的 --set 参数实现

部署安装

# 使用推荐配置
helm install open-local open-local/open-local \
  -n open-local \
  --create-namespace \
  -f values-production.yaml

# 验证
kubectl get po -n open-local
kubectl get sc

验证安装

# 查看所有组件(应在 kube-system 或 open-local 命名空间)
kubectl get po -n open-local

# 预期输出(所有 Pod 应为 Running/Completed):
NAME                                             READY   STATUS      RESTARTS   AGE
open-local-agent-6zmkb                           3/3     Running     0          28s
open-local-csi-provisioner-6dbb7c459c-mcp9l      1/1     Running     0          28s
open-local-csi-resizer-57cfd85df7-x44zg          1/1     Running     0          28s
open-local-csi-snapshotter-689b6bbcfc-wwc57      1/1     Running     0          28s
open-local-init-job-2wvbs                        0/1     Completed   0          28s
open-local-init-job-bw8nh                        0/1     Completed   0          28s
open-local-init-job-frdxp                        0/1     Completed   0          28s
open-local-scheduler-extender-7d5cf688b6-pr426   1/1     Running     0          28s
open-local-snapshot-controller-d6f78754-czfnw    1/1     Running     0          28s

关键组件说明

  • open-local-agent:节点代理,负责磁盘初始化和卷管理
  • open-local-csi-provisioner:CSI 动态供给器
  • open-local-scheduler-extender:调度器扩展,确保 Pod 与存储节点绑定
  • open-local-init-job:初始化任务,每个节点一个,完成 LVM 卷组创建

验证存储池

# 查看节点上的 LVM 卷组(在任意节点执行)
sudo vgs

# 预期输出:
# VG                #PV  #LV  #SN  Attr   VSize   VFree
# open-local-pool-0  1    0    0  wz--n- <50.00g <50.00g

# 查看详细信息
sudo vgdisplay open-local-pool-0

检查 CRD 和 StorageClass

# 查看 CRD
kubectl get crd | grep open-local

# 查看 StorageClass(应自动创建)
kubectl get sc

# 预期输出:
NAME                     PROVISIONER               RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
open-local-lvm           csi.open-local.io         Delete          WaitForFirstConsumer   true                5m

LVM实现挂载

LVM 核心概念

物理磁盘 (Physical Disk)
    ↓
物理卷 (PV - Physical Volume) → pvcreate
    ↓
卷组 (VG - Volume Group) → vgcreate
    ↓
逻辑卷 (LV - Logical Volume) → lvcreate
    ↓
文件系统 (Filesystem)mkfs
    ↓
挂载点 (Mount Point)mount

手动操作示例(理解 open-local 底层)

# 假设设备为 /dev/sdc2(一个分区)

# 1. 创建物理卷(PV)
sudo pvcreate /dev/sdc2
# 输出:Physical volume "/dev/sdc2" successfully created.

# 2. 创建卷组(VG),命名为 s2
sudo vgcreate s2 /dev/sdc2
# 输出:Volume group "s2" successfully created

# 3. 创建逻辑卷(LV),1GB 大小,命名为 lv_test
sudo lvcreate --size 1G --name lv_test s2
# 输出:Logical volume "lv_test" created.

# 4. 格式化为 ext4 文件系统
sudo mkfs.ext4 /dev/s2/lv_test

# 5. 创建挂载点并挂载
sudo mkdir -p /apps/a
sudo mount /dev/s2/lv_test /apps/a

# 6. 验证挂载
df -h | grep lv_test
# 输出:/dev/mapper/s2-lv_test  1014M  1.3M  951M  1% /apps/a

# 7. 卸载(清理)
sudo umount /apps/a
sudo lvremove s2/lv_test
sudo vgremove s2
sudo pvremove /dev/sdc2

open-local 自动化了上述 1-3 步,你只需提供设备,它会自动创建 open-local-pool-0 VG。

创建 StorageClass

# sc-lvm.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: open-local-lvm
provisioner: csi.open-local.io
parameters:
  type: lvm          # 存储类型
  volumeType: "xfs"  # 文件系统
  pool: "open-local-pool-0"  # 存储池名称
reclaimPolicy: Delete
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer  # 关键:延迟绑定


kubectl apply -f sc-lvm.yaml

部署 MySQL 应用

# mysql-app.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-data
spec:
  storageClassName: open-local-lvm
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "password123"
        - name: MYSQL_DATABASE
          value: "testdb"
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
        resources:
          requests:
            memory: "512Mi"
            cpu: "500m"
          limits:
            memory: "1Gi"
            cpu: "1000m"
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: mysql-data

验证数据持久性

# 进入 MySQL 创建数据
kubectl exec -it $(kubectl get pod -l app=mysql -o jsonpath='{.items[0].metadata.name}') -- mysql -uroot -ppassword123

# 在 MySQL 中执行:
# CREATE DATABASE test;
# USE test;
# CREATE TABLE users (id INT, name VARCHAR(20));
# INSERT INTO users VALUES (1, 'open-local');
# SELECT * FROM users;

# 重启 Pod 验证数据
kubectl rollout restart deployment/mysql

# 重新连接查询(数据应保留)

更多推荐