Kubernetes存储体系实战:CSI插件开发与动态存储卷供给配置方案

Kubernetes存储体系是容器编排中数据持久化的核心基础设施。CSI(Container Storage Interface)作为Kubernetes与存储系统的标准接口,将存储驱动从Kubernetes核心代码中解耦,实现了存储插件的独立开发与部署。掌握CSI插件机制和动态存储供给配置,是Kubernetes容器编排向生产环境落地的基础。

Kubernetes存储架构与CSI接口规范

Kubernetes存储体系分为三层:In-Tree存储驱动(已废弃)、FlexVolume(过渡方案)、CSI(当前标准)。CSI定义了Identity Service、Controller Service、Node Service三组gRPC接口,分别负责插件身份、卷管理、节点挂载。

CSI接口核心方法:

# Identity Service
GetPluginInfo()      # 插件名称、版本
GetPluginCapabilities() # 插件能力声明
Probe()               # 健康检查

# Controller Service
CreateVolume()        # 创建存储卷
DeleteVolume()        # 删除存储卷
ControllerPublishVolume() # 控制器侧挂载
ControllerUnpublishVolume() # 控制器侧卸载
ValidateVolumeCapabilities() # 验证卷能力
ListVolumes()         # 列出所有卷
GetCapacity()         # 查询可用容量
CreateSnapshot()      # 创建快照
DeleteSnapshot()      # 删除快照

# Node Service
NodeStageVolume()     # 节点侧格式化
NodePublishVolume()    # 节点侧挂载到Pod
NodeUnpublishVolume()  # 节点侧卸载
NodeUnstageVolume()    # 节点侧清理
NodeGetCapabilities()  # 节点能力
NodeGetInfo()          # 节点信息

CSI插件开发:基于Go实现自定义存储驱动

使用kubernetes-csi/csi-lib-driver框架开发CSI插件,以下为Controller Service中CreateVolume的实现示例:

package main

import (
    "context"
    "fmt"
    "github.com/container-storage-interface/spec/lib/go/csi"
    "google.golang.org/grpc/codes"
    "google.golang.org/grpc/status"
)

type ControllerServer struct {
    csi.UnimplementedControllerServer
    storageBackend StorageBackend
}

func (cs *ControllerServer) CreateVolume(
    ctx context.Context,
    req *csi.CreateVolumeRequest,
) (*csi.CreateVolumeResponse, error) {
    if req.GetName() == "" {
        return nil, status.Error(codes.InvalidArgument, "name required")
    }

    capBytes := req.GetCapacityRange().GetRequiredBytes()
    volID, err := cs.storageBackend.CreateVolume(req.GetName(), capBytes)
    if err != nil {
        return nil, status.Errorf(codes.Internal, "create failed: %v", err)
    }

    return &csi.CreateVolumeResponse{
        Volume: &csi.Volume{
            VolumeId:      volID,
            CapacityBytes: capBytes,
            VolumeContext: map[string]string{
                "fsType": "ext4",
                "pool":   "default",
            },
            AccessibleTopology: []*csi.Topology{
                {Segments: map[string]string{"region": "us-east-1"}},
            },
        },
    }, nil
}

func (cs *ControllerServer) DeleteVolume(
    ctx context.Context,
    req *csi.DeleteVolumeRequest,
) (*csi.DeleteVolumeResponse, error) {
    if req.GetVolumeId() == "" {
        return nil, status.Error(codes.InvalidArgument, "volume_id required")
    }
    if err := cs.storageBackend.DeleteVolume(req.GetVolumeId()); err != nil {
        return nil, status.Errorf(codes.Internal, "delete failed: %v", err)
    }
    return &csi.DeleteVolumeResponse{}, nil
}

Node Service中NodePublishVolume负责将卷挂载到Pod的target path:

func (ns *NodeServer) NodePublishVolume(
    ctx context.Context,
    req *csi.NodePublishVolumeRequest,
) (*csi.NodePublishVolumeResponse, error) {
    targetPath := req.GetTargetPath()
    stagingPath := req.GetStagingTargetPath()

    // 确保target path存在
    if err := os.MkdirAll(targetPath, 0750); err != nil {
        return nil, status.Error(codes.Internal, err.Error())
    }

    // 从staging path bind mount到target path
    mountOptions := append(req.GetVolumeCapability().GetMount().GetMountFlags(),
        "bind")
    if err := unix.Mount(stagingPath, targetPath, "", unix.MS_BIND, ""); err != nil {
        return nil, status.Errorf(codes.Internal, "mount failed: %v", err)
    }

    return &csi.NodePublishVolumeResponse{}, nil
}

动态存储供给:StorageClass与PVC配置

CSI插件部署完成后,通过StorageClass定义动态供给策略。以下为NFS CSI的StorageClass配置:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-csi
provisioner: nfs.csi.k8s.io
parameters:
  server: 10.0.1.100
  share: /data/nfs
  subDir: ${pvc.metadata.namespace}/${pvc.metadata.name}
  mountPermissions: "0755"
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
mountOptions:
  - hard
  - nfsvers=4.1
  - rsize=1048576
  - wsize=1048576
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-data
spec:
  accessModes: ["ReadWriteMany"]
  storageClassName: nfs-csi
  resources:
    requests:
      storage: 50Gi

volumeBindingMode: WaitForFirstConsumer延迟绑定,直到Pod被调度后才创建卷,避免跨可用区问题。allowVolumeExpansion: true允许在线扩容PVC。

CSI插件部署架构

CSI插件以Sidecar模式部署,包含多个组件:

# csi-driver-nfs DaemonSet(节点侧)
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: csi-nfs-node
spec:
  template:
    spec:
      hostNetwork: true
      containers:
      - name: nfs
        image: registry.k8s.io/sig-storage/nfsplugin:v4.6.0
        args:
        - "--endpoint=$(CSI_ENDPOINT)"
        - "--nodeid=$(NODE_ID)"
        - "--v=5"
        env:
        - name: CSI_ENDPOINT
          value: unix:///csi/csi.sock
        - name: NODE_ID
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        volumeMounts:
        - name: plugin-dir
          mountPath: /csi
        - name: pods-mount-dir
          mountPath: /var/lib/kubelet/pods
          mountPropagation: Bidirectional
      volumes:
      - name: plugin-dir
        hostPath:
          path: /var/lib/kubelet/plugins/nfs.csi.k8s.io
          type: DirectoryOrCreate
      - name: pods-mount-dir
        hostPath:
          path: /var/lib/kubelet/pods
          type: Directory

Sidecar容器包括:external-provisioner(监听PVC创建事件调用CSI CreateVolume)、external-attacher(处理VolumeAttachment资源)、external-resizer(处理PVC扩容)、external-snapshotter(处理VolumeSnapshot)。每个Sidecar通过gRPC与CSI插件通信,Kubernetes通过CRD资源驱动整个存储生命周期。

生产环境存储选型建议

块存储(RWO)适合数据库等单写场景,选择支持快照和扩容的CSI驱动,如Ceph RBD、OpenEBS LocalPV。文件存储(RWX)适合共享配置、多Pod读场景,NFS CSI部署简单但性能有限,CephFS CSI更适合大规模。对象存储通过CSI挂载S3兼容存储适合日志归档和备份场景。生产环境务必配置reclaimPolicy: Retain防误删,配合Velero做定期快照备份。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/kubernetes-cun-chu-ti-xi-shi-zhan-csi-cha-jian-kai-fa-yu/

(0)
小编小编
上一篇 21小时前
下一篇 21小时前

相关推荐