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/