k8s教程(Volume篇)-CSI存储机制详解

简介: k8s教程(Volume篇)-CSI存储机制详解

01 引言

声明:本文为《Kubernetes权威指南:从Docker到Kubernetes实践全接触(第5版)》的读书笔记

Kubernetes从1.9版本开始引入容器存储接口 Container Storage Interface (CSI)机制,用于在Kubernetes和外部存储系统之间建立一套标准的存储管理接口,通过该接口为容器提供存储服务。

02 CSI的核心组件和部署架构

Kubernetes CSI存储插件的关键组件和推荐的容器化部署架构如下(其中主要包括两类组件:CSI ControllerCSI Node):

2.1 CSI Controller

CSI Controller的主要功能是 提供存储服务视角对存储资源和存储卷进行管理和操作。在Kubernetes中建议将其部署为单实例Pod,可以使用StatefulSetDeployment控制器进行部署,设置副本数量为1,保证一种存储插件只运行一个控制器实例。

在这个Pod内部署两个容器,分别提供以下功能.


与Master ( kube-controller-manager) 通信的辅助sidecar容器:在 sidecar容器内又可以包含external-attacher和external-provisioner两个容器,它们的功能分别如下:

  • external-attacher:监控VolumeAttachment资源对象的变更,触发针对 CSI端点的ControllerPublish和ControllerUnpublish操作;
  • externail-provisioner:监控PersistentVolumeClaim资源对象的变更,触发针对CSI端点的CreateVolume和DeleteVolume操作。

另外,社区正在引入具备其他管理功能的sidecar工具,例如:externalsnapshotter,用于管理存储快照,目前为Alpha阶段;external-resizer用于管理存储容量扩容,目前为Beta阶段。


CsI Driver存储驱动容器,由第三方存储提供商提供,需要实现上述接口:这两个容器通过本地Socket (Unix Domain Socket, UDS),并使用gPRC协议进行通信。sidecar容器通过Socket调用CSI Driver容器的CSI接口,CSI Driver 容器负责具体的存储卷操作

2.2 CSI Node

CSI Node的主要功能是对主机(Node )上的Volume进行管理和操作在 Kubernetes中建议将其部署为DaemonSet,在需要提供存储资源的各个Node上都运行一个Pod

在这个Pod中部署以下两个容器:

  • 与kubelet通信的辅助sidecar容器node-driver-registrar:主要功能是将存储驱动注册到kubelet中;
  • CSI Driver存储驱动容器:由第三方存储提供商提供,主要功能是接收 kubelet 的调用,需要实现一系列与 Node 相关的 CSI 接口,例如: NodePublishVolume 接口(用于将 Volume 挂载到容器内的目标路径)、NodeUnpublishVolume接口(用于从容器中卸载Volu加粗样式me),等等。

流程:

  • node-driver-registrar容器kubelet通过 Node主机一个hostPath目录下的 unix socket进行通信。
  • CSI Driver容器kubelet通过Node主机另一个hostPath目录下的 unix socket 进行通信,同时需要将 kubelet 的工作目录(默认为 /var/lib/kubelet)挂载给CSI Driver容器,用于为Pod进行Volume的管理操作 (包括mount、 umount等)。

03 CSI存储插件应用实战

下面以csi-hostpath插件为例,对如何部署CSI插件、用户如何使用CSI插件提供的存储资源进行详细说明。

3.1 设置Kubernetes服务启动参数

为kube-apiserver、 kube-controller-manager和kubelet服务的启动参数添加如下内容:

--feature-gates=VolumeSnapshotDataSource=true, CSINodeInfo=true, CSIDriverRegistry=true

这3个特性开关是Kubernetes 从1.12版本开始引入的Alpha 版本功能, CSINodelnfoCSIDriverRegistry需要手工创建其相应的CRD资源对象。

3.2 创建CRD资源对象

需要创建CSINodelnfoCSIDriverRegistryCRD资源对象。

csidriver.yaml的内容如下:

apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition 
metadata:
  name: csidrivers.csi.storage.k8s.io 
  labels:
    addonmanager.kubernetes.io/mode: Reconcile 
spec:
  group: csi.storage.k8s.io
  names:
    kind: CSIDriver
    plural: csidrivers
  scope: Cluster 
  validation: 
    openAPIV3Schema:
      properties:
        spec:
          description: Specification of the CST Driver.
          properties:
            attachRequired:
              description: Indicates this cSI volume driver requires an attach  operation, and that Kubernetes should call attach and wait for any attach operationto complete before proceeding to mount.
              type: boolean
            podInfoOnMountVersion:
              description: Indicates this cSI volume driver requires additional pod information (1ike podName, poduID, etc.) during mount operations.
              type: string
  version: v1alpha1

csinodeinfo.yaml的内容如下:

apiVersion: apiextensions.k8s.io/v1betal 
kind: CustomResourceDefinition 
metadata:
  name: csinodeinfos.csi.storage.k8s.io 
  labels:
    addonmanager.kubernetes.io/mode: Reconcile 
spec:
  group: csi.storage.k8s.io 
  names:
    kind: CSINodeInfo
    plural: csinodeinfos
  scope: Cluster 
  validation:
    openAPIV3Schema:
      properties:
        spec:
          description: Specification of CSINodeInfo 
          properties:
            drivers:
              description: List of CSI drivers running on the node and their specs.
              type: array 
              items:
                properties:
                  name:
                    description: The CSI driver that this obiect refers to.
                    type: string 
                  nodeID:
                    description: The node from the driver point of view.
                    type: string 
                  topologyKeys:
                    description: Tiist of keys supported by the driver.
                    items:
                      type: string
                    type: array
        status:
          description: Status of CSINodeInfo 
          properties:
            drivers:
              description: List of cSI drivers running on the node and their statuses.
              type: array 
              items:
                properties:
                  name:
                    description: The CSI driver that this obiect refers to.
                  type: string 
                  available:
                    description: Whether the CSI driver is installed.
                    type: boolean
                  volumePluginMechanism:
                    description: Indicates to external components the required mechanism  to use for any in-tree plugins replaced by this driver.
                    pattern: in-treelesi
                    type: string
  version: v1alpha1

接着使用kubectl create命令完成创建。

3.3 创建csi-hostpath存储插件相关组件

创建csi-hostpath存储插件相关组件,包括csi-hostpath-attachercsihostpath-provisionercsi-hostpathplugin(其中包含csi-node-driver-registrar和 hostpathplugin)。其中为每个组件都配置了相应的RBAC权限控制规则,对于安全访问Kubernetes资源对象非常重要。

csi-hostpath-attacher.yaml的内容如下:

#RBAC 相关配置
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: csi-attacher
  namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: external-attacher-runner
rules:
  - apiGroups: [""]
  resources: ["persistentvolumes"]
  verbs: ["get","list","watch","update"] 
  - apiGroups: [""]
  resources: ["nodes"]
  verbs: ["get","list", "watch"]
  - apiGroups: ["csi.storage.k8s.io"] 
    resources: ["csinodeinfos"]
    verbs: ["get","list", "watch"]
  - apiGroups: ["storage.k8s.io"] 
    resources: ["volumeattachments"]
    verbs: ["get", "1ist","watch","update"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1 
metadata:
  name: csi-attacher-role 
subjects:
  - kind: ServiceAccount
    name: csi-attacher
    namespace: default 
roleRef:
  kind: ClusterRole
  name: external-attacher-runner
  apiGroup: rbac.authorization.k8s.io
---
# Attacher must be able to work with config map in current namespace 
# if (and only if) leadership election is enalled
kind: Role
apiVersion: rbac.authorization.k8s.io/v1 
metadata:
  # replace with non-default name space name
  namespace: default
  name: external-attacher-cfg 
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get","watch","1ist", "delete", "update", "create"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1 
metadata:
  name: csi-attacher-role-cfg
  namespace: default 
  subiects:
    - kind: ServiceAccount
        name: csi-attacher 
        namespace: default
    roleRef:
    kind: Role
    name: external-attacher-cfg
    apiGroup: rbac.authorization.k8s.io
---
# Service 和 StatefulSet 的定义
kind: Service
apiversion: v1 
metadata:
  name: csi-hostpath-attacher 
  labels:
    app: csi-hostpath-attacher 
spec:
  selector:
    app: csi-hostpath-attacher 
  ports:
    - name: dummy
    port: 12345
---
kind: StatefulSet
apiVersion: apps /v1 
metadata:
  name: csi-hostpath-attacher 
spec:
  serviceName: "csi-hostpath-attacher" 
  replicas: 1 
  selector:
    matchLabels:
      app: csi-hostpath-attacher 
  template:
    metadata:
      labels:
        app: csi-hostpath-attacher 
      spec:
      serviceAccountName: csi-attacher 
        containers:
      - name: csi-attacher
        image: quay.io/k8scsi/csi-attacher:v1.0.1 
        imagePullPolicy: IfNotPresent 
        args:
          - --v=5
        - --csi-address=$(ADDRESS)
        env:
        - name: ADDRESS
                value: /csi/csi.sock 
              volumeMounts:
        - mountPath: /csi
        name: socket-dir
      volumes:
        - hostPath:
          path: /var/lib/kubelet/plugins/csi-hostpath 
          type: DirectoryorCreate
        name: socket-dir

csi-hostpath-provisioner.yaml的内容如下:

# RBAC 相关配置
---
apiversion: v1
kind: ServiceAccount 
metadata:
  name: csi-provisioner
  # replace with non-default name space name 
  namespace: default
---
kind: ClusterRole
apiversion: rbac.authorization.k8s.io/v1 
metadata:
  name: external-provisioner-runner 
  rules:
    - apiGroups: [""]
    resources: ["secrets"]
    verbs: ["get", "list"]
      - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get","list","watch","create", "'delete"] 
    - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get","list","watch", "update"] 
    - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
    - apiGroups: [""]
    resources: ["events"]
    verbs: ["1ist","watch", "create", "update","patch"] 
    - apiGroups: ["snapshot.storage. k8s.io"]
    resources: ["volumesnapshots"]
    verbs: ["get", "list"]
    - apiGroups: ["snapshot.storage.k8s.io"]
    resources: ["volumesnapshotcontents"]
    verbs: ["get", "list"]
    - apiGroups: ["csi.storage.k8s.io"]
    resources: ["csinodein fos"]
    verbs: ["get", "list","watch"]
    - apiGroups: [""]
    resources: ["nodes"]
    verbs: ["get","list", "watch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1 
metadata:
  name: csi-provisioner-role 
subjeets:
- kind: ServiceAccount
  name: csi-provisioner
  # replace with non-default namespace name
  namespace: default 
roleRef:
  kind: ClusterRole
  name: external-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1 
metadata:
  namespace: default
  name: external-provisioner-cfg 
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get","watch","list","delete","update","create"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1 
metadata:
  name: csi-provisioner-role-cfg
  namespace: default 
subjects:
  - kind: ServiceAccount
  name: csi-provisioner
  namespace: default
roleRef:
  - kind: Role
    name: external-provisioner-cfg
    apiGroup: rbac.authorization.k8s.io
---
kind: Service
apiVersion: v1 
metadata:
  name: csi-hostpath-provisioner 
  labels:
    app: csi-hostpath-provisioner 
spec:
  seleetor:
    app: csi-hostpath-provisioner 
    ports:
      - name: dummy
        port: 12345
---
kind: StatefulSet
apiVersion: apps/v1 
metadata:
  name: csi-hostpath-provisioner 
spec:
  serviceName: "csi-hostpath-provisioner" 
  replicas: 1 
  selector:
    matchLabels:
      app: csi-hostpath-provisioner 
    template:
    metadata:
      labels:
        app: esi-hostpath-provisioner 
    spec:
      serviceAccountName: csi-provisioner 
      containers:
        - name: csi-provisioner
          image: quay.io/k8scsi/csi-provisioner:v1.0.1 
          imagePullPolicy: IfNotPresent 
          args:
            - "--provisioner=csi-hostpath"
          - "--csi-address=$(ADDRESS)"
          - "--connection-timeout=15s"
          env:
            - name: ADDRESS
            value: /csi/csi.sock 
          volumeMounts:
          - mountPath: /csi
                    name: socket-dir
      volumes:
        - hostPath:
          path: /var/lib/kubelet/plugins/csi-hostpath 
          type: DirectoryorCreate
        name: socket-dir

csi-hostpathplugin.yaml的内容如下:

# RBAC 相关配置
apiVersion: v1
kind: ServiceAccount
metadata:
  name: csi-node-sa
  # replace with non-default namespace name
  namespace: default
---
kind: clusterRole
apiVersion: rbac.authorization.k8s.io/v1 
metadata:
  name: driver-registrar-runner 
  rules:
    - apiGroups: [""]
          resources: ["events"]
      verbs: ["get","list", "watch", "create", "update", "patch"]
    # Kubernetes versions.
        # - apiGroups: [""]
    # resources: ["nodes"]
    # verbs: ["get","update", "patch"〕
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1 
metadata:
  name: csi-driver-registrar-role 
subjeets:
  - kind: ServiceAccount
      name: csi-node-sa
    # replace with non-default namespace name
    namespace: default 
roleRef:
  kind: ClusterRole
    name: driver-registrar-runner
    apiGroup: rbac.authorization.k8s.io
# Daemon Set 的定义
---
kind: DaemonSet
apiVersion: apps/v1 
metadata:
  name: csi-hostpathplugin 
spec:
  selector:
    matchLabels:
      app: csi-hostpathplugin 
  template:
    metadata:
      labels:
        app: csi-hostpathplugin 
    spec:
      serviceAccountName: csi-node-sa
      hostNetwork: true 
      containers:
        - name: driver-registrar
        image: quay.io/k8scsi/csi-node-driver-registrar:v1.0.1 
        imagePullPolicy: IfNotPresent 
        args:
          - --v=5
          - --csi-address=/csi/csi.sock
          - --kubelet-registration-path=/var/lib/kubelet/plugins /csi-hostpath/csi.sock 
        env:
          - name: KUBE_NODE_NAME 
          - valueFrom:
            fieldRef:
              apiversion: v1
              fieldPath: spec.nodeName
        volumeMounts:
          - mountPath: /csi
          name: socket-dir
          - mountPath: /registration
                  name: registration-dir
      - name: hostpath
        image: quay.io/k8scsi/hostpathplugin:v1.0.1 
        imagePullPolicy: IfNotPresent 
        args:
          - "--v=5"
        - "--endpoint=$(CSI_ENDPOINT)"
        - "--nodeid=$(KUBE NODE NAME)"
        env:
          - name: CSI_ENDPOINI
          value: unix:///csi/csi.sock
                - name: KUBE_NODE_NAME 
                  valueFrom:
            fieldRef:
            apiVersion: V1
            fieldPath: spec.nodeName
        securityContext:
        privileged: true 
        volumeMounts:
        - mountPath: /csi
          name: socket-dir
        - mountPath: /var/lib/kubelet/pods 
          mountPropagation: Bidirectional 
          name: mountpoint-dir
    volumes:
      - hostPath:
        path: /var/lib/kubelet/plugins/csi-hostpath 
        type: DirectoryorCreate
      name: socket-dir
      - hostPath:
        path: /var/1ib/kubelet/pods
        type: DirectoryorCreate
      name: mountpoint-dir 
      - hostPath:
        path: /var/lib/kubelet/plugins_registry 
        type: Directory
      name: registration-dir

然后使用kubectl create命令去创建csi-hostpath-attacher.yamlcsi-hostpath-provisioner.yamlcsi-hostpathplugin.yaml

至此就完成了CSI存储插件的部署。

3.4 应用容器使用CSI存储

应用程序如果希望使用CSI存储插件提供的存储服务,则仍然使用Kubernetes动态存储管理机制。首先通过创建 StorageClass和PVC为应用容器准备存储资源,然后容器就可以挂载PVC到容器内的目录下进行使用了。

创建一个StorageClass,provisioner为CSI存储插件的类型,在本例中为csihostpath:

# csi-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: Storageclass 
metadata:
  name: csi-hostpath-sc
provisioner: csi-hostpath
reclaimPolicy: Delete
volumeBindingMode: Immediate
# 创建
# kubectl create -f csi-storageclass . yaml
storageclass.storage.k8s.io/csi-hostpath-sc created

创建一个PVC,引用刚刚创建的StorageClass,申请存储空间为1GiB:

# csi-pvc.yaml
apiVersion: V1
kind: PersistentVolumeClaim
metadata:
  name: csi-pvc
spec:
  accessModes:
    ReadwriteOnce
resources:
  requests:
    storage: 1Gi
storageClassName: csi-hostpath-sc
# kubectl create -f csi-pve.yaml 
persistentvolumeclaim/csi-pve created

查看PVC和系统自动创建的PV,状态为Bound,说明创建成功:

$ kubectl get pvc
NAME  STATUS  VOLUME                    CAPACITY    ACCESS  MODES     STORAGECLASS AGE
csi-pve Bound   pve-f8923093-3e25-11e9-a5fa-000c29069202  1Gi       RNO   csi-hostpath-sc        40s
$ kubectl get pv
NAME                    CAPACITY  ACCESS MODES RECLAIM POLICY STATUS CLAIM   STORAGECLASS  REASON          AGE
pve-£8923093-3e25-11e9-a5fa-000c29069202    1Gi        RWO          Delete       Bound  default /csi-pvc      csi-hostpath-sc 42S

最后,在应用容器的配置中使用该PVC:

# csi-app. yaml
kind: Pod
apiVersion: v1 
metadata:
  mame: my-csi-app 
spec:
  contaiiners:
    name: my-csi-app
    image: busybox
    imagePullPolicy: IfNotPresent 
    command: ["sleep", "1000000"] 
    volumeMounts:
    - mountPath:"/data"
      name: my-csi-volume
  volumes:
    - name: my-csi-volume 
      persistentVolumeClaim:
      claimName: csi-pvc
# kubectl create -f csi-app.yaml
# pod/my-csi-app created
# kubect1 get pods
NAME      READY     STATUS    RESTARTS AGE
my-csi-app    1/1     Running       0      40s

在Pod创建成功之后,应用容器中的/data目录使用的就是CSI存储插件提供的存储。

相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。     相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
目录
相关文章
|
8月前
|
存储 监控 对象存储
ACK 容器监控存储全面更新:让您的应用运行更稳定、更透明
针对本地存储和 PVC 这两种容器存储使用方式,我们对 ACK 的容器存储监控功能进行了全新升级。此次更新完善了对集群中不同存储类型的监控能力,不仅对之前已有的监控大盘进行了优化,还针对不同的云存储类型,上线了全新的监控大盘,确保用户能够更好地理解和管理容器业务应用的存储资源。
549 272
|
6月前
|
人工智能 运维 Kubernetes
2025 超详细!Lens Kubernetes IDE 多平台下载安装与集群管理教程
Lens 是一款企业级 Kubernetes 可视化操作平台,2025版实现了三大技术革新:AI智能运维(异常检测准确率98.7%)、多云联邦管理(支持50+集群)和实时3D拓扑展示。本文介绍其安装环境、配置流程、核心功能及高阶技巧,帮助用户快速上手并解决常见问题。适用于 Windows、macOS 和 Ubuntu 系统,需满足最低配置要求并前置依赖组件如 kubectl 和 Helm。通过 Global Cluster Hub 实现多集群管理,AI辅助故障诊断提升运维效率,自定义监控看板和插件生态扩展提供更多功能。
|
6月前
|
存储 运维 Kubernetes
容器数据保护:基于容器服务 Kubernetes 版(ACK)备份中心实现K8s存储卷一键备份与恢复
阿里云ACK备份中心提供一站式容器化业务灾备及迁移方案,减少数据丢失风险,确保业务稳定运行。
|
6月前
|
存储 监控 对象存储
ACK 容器监控存储全面更新:让您的应用运行更稳定、更透明
ACK 容器监控存储全面更新:让您的应用运行更稳定、更透明
140 0
ACK 容器监控存储全面更新:让您的应用运行更稳定、更透明
|
7月前
|
存储 监控 对象存储
ACK 容器监控存储全面更新:让您的应用运行更稳定、更透明
ACK 容器监控存储全面更新:让您的应用运行更稳定、更透明
114 1
|
8月前
|
存储 监控 对象存储
ACK容器监控存储全面更新:让您的应用运行更稳定、更透明
介绍升级之后的ACK容器监控体系,包括各大盘界面展示和概要介绍。
|
8月前
|
存储 Kubernetes 安全
k8s存储类型:emptyDir、hostPath、nfs、pvc及存储类storageclass的静态/动态创建pv
Kubernetes提供了多种存储类型,满足不同的应用需求。`emptyDir`和 `hostPath`适用于临时和宿主机存储需求,`nfs`适用于共享存储,`PersistentVolumeClaim`和 `StorageClass`实现了持久存储的灵活管理。通过理解和配置这些存储类型,可以有效提升Kubernetes集群的存储管理能力。
328 13
|
4月前
|
资源调度 Kubernetes 调度
从单集群到多集群的快速无损转型:ACK One 多集群应用分发
ACK One 的多集群应用分发,可以最小成本地结合您已有的单集群 CD 系统,无需对原先应用资源 YAML 进行修改,即可快速构建成多集群的 CD 系统,并同时获得强大的多集群资源调度和分发的能力。
151 9
|
4月前
|
资源调度 Kubernetes 调度
从单集群到多集群的快速无损转型:ACK One 多集群应用分发
本文介绍如何利用阿里云的分布式云容器平台ACK One的多集群应用分发功能,结合云效CD能力,快速将单集群CD系统升级为多集群CD系统。通过增加分发策略(PropagationPolicy)和差异化策略(OverridePolicy),并修改单集群kubeconfig为舰队kubeconfig,可实现无损改造。该方案具备多地域多集群智能资源调度、重调度及故障迁移等能力,帮助用户提升业务效率与可靠性。
|
6月前
|
存储 Kubernetes 监控
K8s集群实战:使用kubeadm和kuboard部署Kubernetes集群
总之,使用kubeadm和kuboard部署K8s集群就像回归童年一样,简单又有趣。不要忘记,技术是为人服务的,用K8s集群操控云端资源,我们不过是想在复杂的世界找寻简单。尽管部署过程可能遇到困难,但朝着简化复杂的目标,我们就能找到意义和乐趣。希望你也能利用这些工具,找到你的乐趣,满足你的需求。
597 33

推荐镜像

更多