带你玩转kubernetes-k8s(第51篇:共享储存原理-[GlusterFS+Heketi+CSI])

释放双眼,带上耳机,听听看~!

动态存储管理实战: GlusterFS

本节以GlusterFS为例,从定义StorageClass、创建GlusterFS和Heketi服务、用户申请PVC到创建Pod使用存储资源,对StorageClass和动态资源分配进行详细说明,进一步剖析Kubernetes的存储机制。(ps: 第26篇有详细的部署过程,此处不上传操作图片了)

准备工作

为了能够使用GlusterFS,首先在计划用于GlusterFS的各Node上安装GlusterFS客户端:


1
2
1yum install -y  glusterfs glusterfs-fuse
2

GlusterFS管理服务容器需要以特权模式运行,在kube-apiserver的启动参数中增加:


1
2
1--allow-privileged=true
2

给要部署GlusterFS管理服务的节点打上“storagenode=glusterfs”的标签,是为了将GlusterFS容器定向部署到安装了GlusterFS的Node上:


1
2
3
4
1kubectl label node k8s-node-1  storagenode=glusterfs
2kubectl label node k8s-node-2  storagenode=glusterfs
3kubectl label node k8s-node-3  storagenode=glusterfs
4

创建GlusterFS管理服务容器集群

      GlusterFS管理服务容器以DaemonSet的方式进行部署,确保在每个Node上都运行一个GlusterFS管理服务。glusterfs-daemonset.yaml的内容如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
1kind: DaemonSet
2apiVersion: extensions/v1beta1
3metadata:
4  name: glusterfs
5  labels:
6    glusterfs: daemonset
7  annotations:
8    description: GlusterFS DaemonSet
9    tags: glusterfs
10spec:
11  template:
12    metadata:
13      name: glusterfs
14      labels:
15        glusterfs: pod
16        glusterfs-node: pod
17    spec:
18      nodeSelector:
19        storagenode: glusterfs
20      hostNetwork: true
21      containers:
22      - image: gluster/gluster-centos:latest
23        name: glusterfs
24        imagePullPolicy: IfNotPresent
25        volumeMounts:
26        - name: glusterfs-heketi
27          mountPath: "/var/lib/heketi"
28        - name: glusterfs-run
29          mountPath: "/run"
30        - name: glusterfs-lvm
31          mountPath: "/run/lvm"
32        - name: glusterfs-etc
33          mountPath: "/etc/glusterfs"
34        - name: glusterfs-logs
35          mountPath: "/var/log/glusterfs"
36        - name: glusterfs-config
37          mountPath: "/var/lib/glusterd"
38        - name: glusterfs-dev
39          mountPath: "/dev"
40        - name: glusterfs-misc
41          mountPath: "/var/lib/misc/glusterfsd"
42        - name: glusterfs-cgroup
43          mountPath: "/sys/fs/cgroup"
44          readOnly: true
45        - name: glusterfs-ssl
46          mountPath: "/etc/ssl"
47          readOnly: true
48        securityContext:
49          capabilities: {}
50          privileged: true
51        readinessProbe:
52          timeoutSeconds: 3
53          initialDelaySeconds: 60
54          exec:
55            command:
56            - "/bin/bash"
57            - "-c"
58            - systemctl status glusterd.service
59        livenessProbe:
60          timeoutSeconds: 3
61          initialDelaySeconds: 60
62          exec:
63            command:
64            - "/bin/bash"
65            - "-c"
66            - systemctl status glusterd.service
67      volumes:
68      - name: glusterfs-heketi
69        hostPath:
70          path: "/var/lib/heketi"
71      - name: glusterfs-run
72      - name: glusterfs-lvm
73        hostPath:
74          path: "/run/lvm"
75      - name: glusterfs-etc
76        hostPath:
77          path: "/etc/glusterfs"
78      - name: glusterfs-logs
79        hostPath:
80          path: "/var/log/glusterfs"
81      - name: glusterfs-config
82        hostPath:
83          path: "/var/lib/glusterd"
84      - name: glusterfs-dev
85        hostPath:
86          path: "/dev"
87      - name: glusterfs-misc
88        hostPath:
89          path: "/var/lib/misc/glusterfsd"
90      - name: glusterfs-cgroup
91        hostPath:
92          path: "/sys/fs/cgroup"
93      - name: glusterfs-ssl
94        hostPath:
95          path: "/etc/ssl"
96

1
2
3
1kubectl apply -f glusterfs-daemonset.yaml
2kubectl get pod
3

创建Heketi服务

Heketi 是一个提供RESTful API管理GlusterFS卷的框架,并能够在OpenStack、Kubernetes、OpenShift等云平台上实现动态存储资源供应,支持GlusterFS多集群管理,便于管理员对GlusterFS进行操作。

在部署Heketi服务之前,需要为它创建一个ServiceAccount对象:


1
2
3
4
5
6
1apiVersion: v1
2kind: ServiceAccount
3metadata:
4  name: heketi-service-account
5
6

 


1
2
1kubectl apply -f heketi-service-account.yaml
2

部署Heketi服务


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
1---
2kind: Service
3apiVersion: v1
4metadata:
5  name: deploy-heketi
6  labels:
7    glusterfs: heketi-service
8    deploy-heketi: support
9  annotations:
10    description: Exposes Heketi Service
11spec:
12  selector:
13    name: deploy-heketi
14  ports:
15  - name: deploy-heketi
16    port: 8080
17    targetPort: 8080
18---
19kind: Deployment
20apiVersion: extensions/v1beta1
21metadata:
22  name: deploy-heketi
23  labels:
24    glusterfs: heketi-deployment
25    deploy-heketi: heket-deployment
26  annotations:
27    description: Defines how to deploy Heketi
28spec:
29  replicas: 1
30  template:
31    metadata:
32      name: deploy-heketi
33      labels:
34        glusterfs: heketi-pod
35        name: deploy-heketi
36    spec:
37      serviceAccountName: heketi-service-account
38      containers:
39      - image: heketi/heketi
40        imagePullPolicy: IfNotPresent
41        name: deploy-heketi
42        env:
43        - name: HEKETI_EXECUTOR
44          value: kubernetes
45        - name: HEKETI_FSTAB
46          value: "/var/lib/heketi/fstab"
47        - name: HEKETI_SNAPSHOT_LIMIT
48          value: '14'
49        - name: HEKETI_KUBE_GLUSTER_DAEMONSET
50          value: "y"
51        ports:
52        - containerPort: 8080
53        volumeMounts:
54        - name: db
55          mountPath: "/var/lib/heketi"
56        readinessProbe:
57          timeoutSeconds: 3
58          initialDelaySeconds: 3
59          httpGet:
60            path: "/hello"
61            port: 8080
62        livenessProbe:
63          timeoutSeconds: 3
64          initialDelaySeconds: 30
65          httpGet:
66            path: "/hello"
67            port: 8080
68      volumes:
69      - name: db
70        hostPath:
71          path: "/heketi-data"
72
73

需要注意的是,Heketi的DB数据需要持久化保存,建议使用hostPath或其他共享存储进行保存:


1
2
1kubectl apply -f heketi-deployment-svc.yaml
2

为Heketi设置GlusterFS集群

在Heketi能够管理GlusterFS集群之前,首先要为其设置GlusterFS集群的信息。可以用一个topology.json配置文件来完成各个GlusterFS节点和设备的定义。Heketi要求在一个GlusterFS集群中至少有3个节点。在topology.json配置文件hostnames字段的manage上填写主机名,在storage上填写IP地址,devices要求为未创建文件系统的裸设备(可以有多块盘),以供Heketi自动完成PV(Physical Volume)、VG(Volume Group)和LV(Logical Volume)的创建。topology.json文件的内容如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
1{
2  "clusters": [
3    {
4      "nodes": [
5        {
6          "node": {
7            "hostnames": {
8              "manage": [
9                "k8s-master2"
10              ],
11              "storage": [
12                "20.0.40.52"
13              ]
14            },
15            "zone": 1
16          },
17          "devices": [
18            "/dev/sdb1"
19          ]
20        },
21        {
22          "node": {
23            "hostnames": {
24              "manage": [
25                "k8s-master3"
26              ],
27              "storage": [
28                "20.0.40.53"
29              ]
30            },
31            "zone": 1
32          },
33          "devices": [
34            "/dev/sdb1"
35          ]
36        },
37        {
38          "node": {
39            "hostnames": {
40              "manage": [
41                "k8s-node3"
42              ],
43              "storage": [
44                "20.0.40.56"
45              ]
46            },
47            "zone": 1
48          },
49          "devices": [
50            "/dev/sdb1"
51          ]
52        }
53      ]
54    }
55  ]
56}
57

 

进入Heketi容器,使用命令行工具heketi-cli完成GlusterFS集群的创建(此处不在过多描述,详细参考第26篇)。

经过这个操作,Heketi完成了GlusterFS集群的创建,同时在GlusterFS集群的各个节点的/dev/sdb盘上成功创建了PV和VG。

查看Heketi的topology信息,可以看到Node和Device的详细信息,包括磁盘空间的大小和剩余空间。此时,Volume和Brick还未创建:

定义StorageClass

准备工作已经就绪,集群管理员现在可以在Kubernetes集群中定义一个StorageClass了。storageclass-gluster-heketi.yaml配置文件的内容如下:


1
2
3
4
5
6
7
8
9
1apiVersion: storage.k8s.ip/v1
2kind: StorageClass
3metadata:
4  name: gluster-heketi
5provisioner: kubernetes.io/glusterfs
6parameters:
7  resturl: "http://20.0.40.53:8080"     #填写宿主机ip,不要填写clusterip
8  restauthenabled: "false"
9

Provisioner参数必须被设置为“kubernetes.io/glusterfs”。
resturl的地址需要被设置为API Server所在主机可以访问到的Heketi服务的某个地址,可以使用服务ClusterIP+端口号、容器IP地址+端口号,或将服务映射到物理机,使用物理机IP+NodePort。
创建这个StorageClass资源对象:


1
2
1kubectl apply -f storageclass-glusterfs-heketi.yaml
2

定义PVC

现在,用户可以申请一个PVC了。例如,一个用户申请一个1GiB空间的共享存储资源,StorageClass使用“gluster-heketi”,未定义任何Selector,说明使用动态资源供应模式:


1
2
3
4
5
6
7
8
9
10
11
12
1kind: PersistentVolumeClaim
2apiVersion: v1
3metadata:
4  name: pvc-gluster-heketi
5spec:
6  storageClassName: gluster-heketi
7  accessModes:
8  - ReadWriteOnce
9  resources:
10    requests:
11      storage: 1Gi
12

PVC的定义一旦生成,系统便将触发Heketi进行相应的操作,主要为在GlusterFS集群上创建brick,再创建并启动一个Volume。整个过程可以在Heketi的日志中查到:

查看PVC的状态,可见其已经为Bound(已绑定):kubectl get pvc

查看PV,可见系统自动创建的PV: kubecatl get pv

查看该PV的详细信息,可以看到其容量、引用的StorageClass等信息都已正确设置,状态也为Bound,回收策略则为默认的Delete。同时Gluster的Endpoint和Path也由Heketi自动完成了设置: kubectl describe pv <pv-pvname>

至此,一个可供Pod使用的PVC就创建成功了。接下来Pod就能通过Volume的设置将这个PVC挂载到容器内部进行使用了。

Pod使用PVC的存储资源

在Pod中使用PVC定义的存储资源非常容易,只需设置一个Volume,其类型为persistentVolumeClaim,即可轻松引用一个PVC。下例中使用一个busybox容器验证对PVC的使用,注意Pod需要与PVC属于同一个Namespace:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1apiVersion: v1
2kind: Pod
3metadata:
4  name: pod-use-pvc
5spec:
6  containers:
7  - name: pod-use-pvc
8    image: busybox
9    imagePullPolucy: IfNotPresent
10    command:
11    - sleep
12    - &quot;3600&quot;
13    volumeMounts:
14    - name: gluster-volume
15      mounthPath: &quot;/pv-data&quot;
16      readOnly: false
17   volumes:
18   - name: gluster-volume
19     persistentVolumeClain:
20       claimName: pvc-gluster-heketi
21

1
2
1kubectl apply -f pod-use-pvc.yaml
2

进入容器pod-use-pvc,在/pv-data目录下创建一些文件。可以验证文件a和b在GlusterFS集群中是否正确生成。

至此,使用Kubernetes最新的动态存储供应模式,配合StorageClass和Heketi共同搭建基于GlusterFS的共享存储就完成了。有兴趣的读者可以继续尝试StorageClass的其他设置,例如调整GlusterFS的Volume类型、修改PV的回收策略等。

在使用动态存储供应模式的情况下,相对于静态模式的优势至少包括如下两点。
(1)管理员无须预先创建大量的PV作为存储资源。
(2)用户在申请PVC时无法保证容量与预置PV的容量完全匹配。从Kubernetes 1.6版本开始,建议用户优先考虑使用StorageClass的动态存储供应模式进行存储管理

 

CSI存储机制详解

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

CSI的设计背景

Kubernetes通过PV、PVC、Storageclass已经提供了一种强大的基于插件的存储管理机制,但是各种存储插件提供的存储服务都是基于一种被称为“in-true”(树内)的方式提供的,这要求存储插件的代码必须被放进Kubernetes的主干代码库中才能被Kubernetes调用,属于紧耦合的开发模式。这种“in-tree”方式会带来一些问题:

◎ 存储插件的代码需要与Kubernetes的代码放在同一代码库中,并与Kubernetes的二进制文件共同发布;
◎ 存储插件代码的开发者必须遵循Kubernetes的代码开发规范;
◎ 存储插件代码的开发者必须遵循Kubernetes的发布流程,包括添加对Kubernetes存储系统的支持和错误修复;
◎ Kubernetes社区需要对存储插件的代码进行维护,包括审核、测试等工作;
◎ 存储插件代码中的问题可能会影响Kubernetes组件的运行,并且很难排查问题;
◎ 存储插件代码与Kubernetes的核心组件(kubelet和kube-controller-manager)享有相同的系统特权权限,可能存在可靠性和安全性问题。

Kubernetes已有的Flex Volume插件机制试图通过为外部存储暴露一个基于可执行程序(exec)的API来解决这些问题。尽管它允许第三方存储提供商在Kubernetes核心代码之外开发存储驱动,但仍然有两个问题没有得到很好的解决:

◎ 部署第三方驱动的可执行文件仍然需要宿主机的root权限,存在安全隐患;
◎ 存储插件在执行mount、attach这些操作时,通常需要在宿主机上安装一些第三方工具包和依赖库,使得部署过程更加复杂,例如部署Ceph时需要安装rbd库,部署GlusterFS时需要安装mount.glusterfs库,等等。

基于以上这些问题和考虑,Kubernetes逐步推出与容器对接的存储接口标准,存储提供方只需要基于标准接口进行存储插件的实现,就能使用Kubernetes的原生存储机制为容器提供存储服务。这套标准被称为CSI(容器存储接口)。在CSI成为Kubernetes的存储供应标准之后,存储提供方的代码就能和Kubernetes代码彻底解耦,部署也与Kubernetes核心组件分离,显然,存储插件的开发由提供方自行维护,就能为Kubernetes用户提供更多的存储功能,也更加安全可靠。基于CSI的存储插件机制也被称为“out-of-tree”(树外)的服务提供方式,是未来Kubernetes第三方存储插件的标准方案。

CSI存储插件的关键组件和部署架构

下图描述了Kubernetes CSI存储插件的关键组件和推荐的容器化部署架构。

     其中主要包括两种组件:CSI Controller和CSI Node。

**CSI Controller **

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

       在这个Pod内部署两个容器,如下所述。

(1)与Master(kube-controller-manager)通信的辅助sidecar容器。

在sidecar容器内又可以包含external-attacher和external-provisioner两个容器,它们的功能分别如下。
◎ external-attacher:监控VolumeAttachment资源对象的变更,触发针对CSI端点的ControllerPublish和ControllerUnpublish操作。
◎ external-provisioner:监控PersistentVolumeClaim资源对象的变更,触发针对CSI端点的CreateVolume和DeleteVolume操作。

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

CSI Node

     CSI Node的主要功能是对主机(Node)上的Volume进行管理和操作。在Kubernetes中建议将其部署为DaemonSet,在每个Node上都运行一个Pod。

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

       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等)。

CSI存储插件的使用示例:

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

(1)设置Kubernetes服务启动参数。为kube-apiserver、kube-controller-manager和kubelet服务的启动参数添加:


1
2
3
1--feature-gates=VolumeSnapshotDataSource=true,CSINodeInfo=true,CSIDriverRegistry=true
2
3

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

Kubernetes 1.10版本所需的CSIPersistentVolume和MountPropagation特性开关已经默认启用,KubeletPluginsWatcher特性开关也在Kubernetes 1.12版本中默认启用,无须在命令行参数中指定。

(2)创建CSINodeInfo和CSIDriverRegistry CRD资源对象。

csidriver.yaml的内容如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1apiVersion: apiextensions.k8s.io/v1beta1
2kind: CustomResourceDefinition
3metadata:
4  name: csidrivers.csi.storage.k8s.io
5  labels:
6    addonmanager.kubernetes.io/mode: Reconcile
7spec:
8  group: csi.storage.k8s.io
9  names:
10    kind: CSIDriver
11    plural: csidrivers
12  scope: Cluster
13  validation:
14    openAPIV3Schema:
15      properties:
16        spec:
17          description: Specification of the CSI Driver.
18          properties:
19            attachRequired:
20              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.
21              type: boolean
22            podInfoOnMountVersion:
23              description: Indicates this CSI volume driver requires additional pod
24                information (like podName, podUID, etc.) during mount operations.
25              type: string
26  version: v1alpha1
27

csinodeinfo.yaml的内容如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
1apiVersion: apiextensions.k8s.io/v1beta1
2kind: CustomResourceDefinition
3metadata:
4  name: csinodeinfos.csi.storage.k8s.io
5  labels:
6    addonmanager.kubernetes.io/mode: Reconcile
7spec:
8  group: csi.storage.k8s.io
9  names:
10    kind: CSINodeInfo
11    plural: csinodeinfos
12  scope: Cluster
13  validation:
14    openAPIV3Schema:
15      properties:
16        spec:
17          description: Specification of CSINodeInfo
18          properties:
19            drivers:
20              description: List of CSI drivers running on the node and their specs.
21              type: array
22              items:
23                properties:
24                  name:
25                    description: The CSI driver that this object refers to.
26                    type: string
27                  nodeID:
28                    description: The node from the driver point of view.
29                    type: string
30                  topologyKeys:
31                    description: List of keys supported by the driver.
32                    items:
33                      type: string
34                    type: array
35        status:
36          description: Status of CSINodeInfo
37          properties:
38            drivers:
39              description: List of CSI drivers running on the node and their statuses.
40              type: array
41              items:
42                properties:
43                  name:
44                    description: The CSI driver that this object refers to.
45                    type: string
46                  available:
47                    description: Whether the CSI driver is installed.
48                    type: boolean
49                  volumePluginMechanism:
50                    description: Indicates to external components the required mechanism
51                      to use for any in-tree plugins replaced by this driver.
52                    pattern: in-tree|csi
53                    type: string
54  version: v1alpha1
55

使用kubectl  apply命令完成创建:


1
2
3
1kubectl apply -f csidriver.yaml
2kubectl apply -f csinodeinfo.yaml
3

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
1---
2apiVersion: v1
3kind: ServiceAccount
4metadata:
5  name: csi-attacher
6  # replace with non-default namespace name
7  namespace: default
8---
9kind: ClusterRole
10apiVersion: rbac.authorization.k8s.io/v1
11metadata:
12  name: external-attacher-runner
13rules:
14  - apiGroups: [&quot;&quot;]
15    resources: [&quot;persistentvolumes&quot;]
16    verbs: [&quot;get&quot;, &quot;list&quot;, &quot;watch&quot;, &quot;update&quot;]
17  - apiGroups: [&quot;&quot;]
18    resources: [&quot;nodes&quot;]
19    verbs: [&quot;get&quot;, &quot;list&quot;, &quot;watch&quot;]
20  - apiGroups: [&quot;csi.storage.k8s.io&quot;]
21    resources: [&quot;csinodeinfos&quot;]
22    verbs: [&quot;get&quot;, &quot;list&quot;, &quot;watch&quot;]
23  - apiGroups: [&quot;storage.k8s.io&quot;]
24    resources: [&quot;volumeattachments&quot;]
25    verbs: [&quot;get&quot;, &quot;list&quot;, &quot;watch&quot;, &quot;update&quot;]
26---
27kind: ClusterRoleBinding
28apiVersion: rbac.authorization.k8s.io/v1
29metadata:
30  name: csi-attacher-role
31subjects:
32  - kind: ServiceAccount
33    name: csi-attacher
34    # replace with non-default namespace name
35    namespace: default
36roleRef:
37  kind: ClusterRole
38  name: external-attacher-runner
39  apiGroup: rbac.authorization.k8s.io
40---
41kind: Role
42apiVersion: rbac.authorization.k8s.io/v1
43metadata:
44  # replace with non-default namespace name
45  namespace: default
46  name: external-attacher-cfg
47rules:
48- apiGroups: [&quot;&quot;]
49  resources: [&quot;configmaps&quot;]
50  verbs: [&quot;get&quot;, &quot;watch&quot;, &quot;list&quot;, &quot;delete&quot;, &quot;update&quot;, &quot;create&quot;]
51---
52kind: RoleBinding
53apiVersion: rbac.authorization.k8s.io/v1
54metadata:
55  name: csi-attacher-role-cfg
56  # replace with non-default namespace name
57  namespace: default
58subjects:
59  - kind: ServiceAccount
60    name: csi-attacher
61    # replace with non-default namespace name
62    namespace: default
63roleRef:
64  kind: Role
65  name: external-attacher-cfg
66  apiGroup: rbac.authorization.k8s.io
67
68---
69kind: Service
70apiVersion: v1
71metadata:
72  name: csi-hostpath-attacher
73  labels:
74    app: csi-hostpath-attacher
75spec:
76  selector:
77    app: csi-hostpath-attacher
78  ports:
79    - name: dummy
80      port: 12345
81---
82kind: StatefulSet
83apiVersion: apps/v1
84metadata:
85  name: csi-hostpath-attacher
86spec:
87  serviceName: &quot;csi-hostpath-attacher&quot;
88  replicas: 1
89  selector:
90    matchLabels:
91      app: csi-hostpath-attacher
92  template:
93    metadata:
94      labels:
95        app: csi-hostpath-attacher
96    spec:
97      serviceAccountName: csi-attacher
98      containers:
99        - name: csi-attacher
100          image: quay.io/k8scsi/csi-attacher:v1.0.1
101          imagePullPolicy: IfNotPresent
102          args:
103            - --v=5
104            - --csi-address=$(ADDRESS)
105          env:
106            - name: ADDRESS
107              value: /csi/csi.sock
108          volumeMounts:
109          - mountPath: /csi
110            name: socket-dir
111      volumes:
112        - hostPath:
113            path: /var/lib/kubelet/plugins/csi-hostpath
114            type: DirectoryOrCreate
115          name: socket-dir
116
117
118

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
1# csi-hostpath-provisioner
2---
3apiVersion: v1
4kind: ServiceAccount
5metadata:
6  name: csi-provisioner
7  # replace with non-default namespace name
8  namespace: default
9---
10kind: ClusterRole
11apiVersion: rbac.authorization.k8s.io/v1
12metadata:
13  name: external-provisioner-runner
14rules:
15  - apiGroups: [&quot;&quot;]
16    resources: [&quot;secrets&quot;]
17    verbs: [&quot;get&quot;, &quot;list&quot;]
18  - apiGroups: [&quot;&quot;]
19    resources: [&quot;persistentvolumes&quot;]
20    verbs: [&quot;get&quot;, &quot;list&quot;, &quot;watch&quot;, &quot;create&quot;, &quot;delete&quot;]
21  - apiGroups: [&quot;&quot;]
22    resources: [&quot;persistentvolumeclaims&quot;]
23    verbs: [&quot;get&quot;, &quot;list&quot;, &quot;watch&quot;, &quot;update&quot;]
24  - apiGroups: [&quot;storage.k8s.io&quot;]
25    resources: [&quot;storageclasses&quot;]
26    verbs: [&quot;get&quot;, &quot;list&quot;, &quot;watch&quot;]
27  - apiGroups: [&quot;&quot;]
28    resources: [&quot;events&quot;]
29    verbs: [&quot;list&quot;, &quot;watch&quot;, &quot;create&quot;, &quot;update&quot;, &quot;patch&quot;]
30  - apiGroups: [&quot;snapshot.storage.k8s.io&quot;]
31    resources: [&quot;volumesnapshots&quot;]
32    verbs: [&quot;get&quot;, &quot;list&quot;]
33  - apiGroups: [&quot;snapshot.storage.k8s.io&quot;]
34    resources: [&quot;volumesnapshotcontents&quot;]
35    verbs: [&quot;get&quot;, &quot;list&quot;]
36  - apiGroups: [&quot;csi.storage.k8s.io&quot;]
37    resources: [&quot;csinodeinfos&quot;]
38    verbs: [&quot;get&quot;, &quot;list&quot;, &quot;watch&quot;]
39  - apiGroups: [&quot;&quot;]
40    resources: [&quot;nodes&quot;]
41    verbs: [&quot;get&quot;, &quot;list&quot;, &quot;watch&quot;]
42---
43kind: ClusterRoleBinding
44apiVersion: rbac.authorization.k8s.io/v1
45metadata:
46  name: csi-provisioner-role
47subjects:
48  - kind: ServiceAccount
49    name: csi-provisioner
50    # replace with non-default namespace name
51    namespace: default
52roleRef:
53  kind: ClusterRole
54  name: external-provisioner-runner
55  apiGroup: rbac.authorization.k8s.io
56---
57kind: Role
58apiVersion: rbac.authorization.k8s.io/v1
59metadata:
60  # replace with non-default namespace name
61  namespace: default
62  name: external-provisioner-cfg
63rules:
64- apiGroups: [&quot;&quot;]
65  resources: [&quot;endpoints&quot;]
66  verbs: [&quot;get&quot;, &quot;watch&quot;, &quot;list&quot;, &quot;delete&quot;, &quot;update&quot;, &quot;create&quot;]
67---
68kind: RoleBinding
69apiVersion: rbac.authorization.k8s.io/v1
70metadata:
71  name: csi-provisioner-role-cfg
72  # replace with non-default namespace name
73  namespace: default
74subjects:
75  - kind: ServiceAccount
76    name: csi-provisioner
77    # replace with non-default namespace name
78    namespace: default
79roleRef:
80  kind: Role
81  name: external-provisioner-cfg
82  apiGroup: rbac.authorization.k8s.io
83
84---
85kind: Service
86apiVersion: v1
87metadata:
88  name: csi-hostpath-provisioner
89  labels:
90    app: csi-hostpath-provisioner
91spec:
92  selector:
93    app: csi-hostpath-provisioner
94  ports:
95    - name: dummy
96      port: 12345
97---
98kind: StatefulSet
99apiVersion: apps/v1
100metadata:
101  name: csi-hostpath-provisioner
102spec:
103  serviceName: &quot;csi-hostpath-provisioner&quot;
104  replicas: 1
105  selector:
106    matchLabels:
107      app: csi-hostpath-provisioner
108  template:
109    metadata:
110      labels:
111        app: csi-hostpath-provisioner
112    spec:
113      serviceAccountName: csi-provisioner
114      containers:
115        - name: csi-provisioner
116          image: quay.io/k8scsi/csi-provisioner:v1.0.1
117          imagePullPolicy: IfNotPresent
118          args:
119            - &quot;--provisioner=csi-hostpath&quot;
120            - &quot;--csi-address=$(ADDRESS)&quot;
121            - &quot;--connection-timeout=15s&quot;
122          env:
123            - name: ADDRESS
124              value: /csi/csi.sock
125          volumeMounts:
126            - mountPath: /csi
127              name: socket-dir
128      volumes:
129        - hostPath:
130            path: /var/lib/kubelet/plugins/csi-hostpath
131            type: DirectoryOrCreate
132          name: socket-dir
133

csi-hostpathplugin.yaml的内容如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
1---
2apiVersion: v1
3kind: ServiceAccount
4metadata:
5  name: csi-node-sa
6  # replace with non-default namespace name
7  namespace: default
8---
9kind: ClusterRole
10apiVersion: rbac.authorization.k8s.io/v1
11metadata:
12  name: driver-registrar-runner
13rules:
14  - apiGroups: [&quot;&quot;]
15    resources: [&quot;events&quot;]
16    verbs: [&quot;get&quot;, &quot;list&quot;, &quot;watch&quot;, &quot;create&quot;, &quot;update&quot;, &quot;patch&quot;]
17  # The following permissions are only needed when running
18  # driver-registrar without the --kubelet-registration-path
19  # parameter, i.e. when using driver-registrar instead of
20  # kubelet to update the csi.volume.kubernetes.io/nodeid
21  # annotation. That mode of operation is going to be deprecated
22  # and should not be used anymore, but is needed on older
23  # Kubernetes versions.
24  # - apiGroups: [&quot;&quot;]
25  #   resources: [&quot;nodes&quot;]
26  #   verbs: [&quot;get&quot;, &quot;update&quot;, &quot;patch&quot;]
27---
28kind: ClusterRoleBinding
29apiVersion: rbac.authorization.k8s.io/v1
30metadata:
31  name: csi-driver-registrar-role
32subjects:
33  - kind: ServiceAccount
34    name: csi-node-sa
35    # replace with non-default namespace name
36    namespace: default
37roleRef:
38  kind: ClusterRole
39  name: driver-registrar-runner
40  apiGroup: rbac.authorization.k8s.io
41
42---
43kind: DaemonSet
44apiVersion: apps/v1
45metadata:
46  name: csi-hostpathplugin
47spec:
48  selector:
49    matchLabels:
50      app: csi-hostpathplugin
51  template:
52    metadata:
53      labels:
54        app: csi-hostpathplugin
55    spec:
56      serviceAccountName: csi-node-sa
57      hostNetwork: true
58      containers:
59        - name: driver-registrar
60          image: quay.io/k8scsi/csi-node-driver-registrar:v1.0.1
61          imagePullPolicy: IfNotPresent
62          args:
63            - --v=5
64            - --csi-address=/csi/csi.sock
65            - --kubelet-registration-path=/var/lib/kubelet/plugins/csi-hostpath/csi.sock
66          env:
67            - name: KUBE_NODE_NAME
68              valueFrom:
69                fieldRef:
70                  apiVersion: v1
71                  fieldPath: spec.nodeName
72          volumeMounts:
73          - mountPath: /csi
74            name: socket-dir
75          - mountPath: /registration
76            name: registration-dir
77        - name: hostpath
78          image: quay.io/k8scsi/hostpathplugin:v1.0.1
79          imagePullPolicy: IfNotPresent
80          args:
81            - &quot;--v=5&quot;
82            - &quot;--endpoint=$(CSI_ENDPOINT)&quot;
83            - &quot;--nodeid=$(KUBE_NODE_NAME)&quot;
84          env:
85            - name: CSI_ENDPOINT
86              value: unix:///csi/csi.sock
87            - name: KUBE_NODE_NAME
88              valueFrom:
89                fieldRef:
90                  apiVersion: v1
91                  fieldPath: spec.nodeName
92          securityContext:
93            privileged: true
94          volumeMounts:
95            - mountPath: /csi
96              name: socket-dir
97            - mountPath: /var/lib/kubelet/pods
98              mountPropagation: Bidirectional
99              name: mountpoint-dir
100      volumes:
101        - hostPath:
102            path: /var/lib/kubelet/plugins/csi-hostpath
103            type: DirectoryOrCreate
104          name: socket-dir
105        - hostPath:
106            path: /var/lib/kubelet/pods
107            type: DirectoryOrCreate
108          name: mountpoint-dir
109        - hostPath:
110            path: /var/lib/kubelet/plugins_registry
111            type: Directory
112          name: registration-dir
113
114

使用kubectl apply命令完成创建:


1
2
3
4
1kubectl apply -f  csi-hostpath-attacher.yaml
2kubectl apply -f  csi-hostpath-provisioner.yaml
3kubectl apply -f  csi-hostpathplugin.yaml
4

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

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

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

 


1
2
3
4
5
6
7
8
1apiVersion: storage.k8s.io/v1
2kind: StorageClass
3metadata:
4  name: csi-hostpath-sc
5provisioner: csi-hostpath
6reclaimPolicy: Delete
7volumeBindingMode: Immediate
8

1
2
1kubectl apply -f csi-storageclass.yaml
2

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


1
2
3
4
5
6
7
8
9
10
11
12
1apiVersion: v1
2kind: PersistentVolumeClaim
3metadata:
4  name: csi-pvc
5spec:
6  accessModes:
7  - ReadWriteOnce
8  resources:
9    requests:
10      storage: 1Gi
11  storageClassName: csi-hostpath-sc
12

1
2
1kubectl apply -f csi-pvc.yaml
2

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


1
2
1kubectl get pv,pvc
2

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

csi-app.yaml


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1kind: Pod
2apiVersion: v1
3metadata:
4  name: my-csi-app
5spec:
6  containers:
7  - name: my-csi-app
8    image: busybox
9    imagePullPolicy: IfNotPresent
10    command: [&quot;sleep&quot;, &quot;1000000&quot;]
11    volumeMounts:
12    - mountPath: &quot;/data&quot;
13      name: my-csi-volume
14  volumes:
15  - name: my-csi-volume
16    persistentVolumeClaim:
17      claimName: csi-pvc
18

1
2
1kubectl apply -f csi-app.yaml
2

1
2
1kubecatl get pods
2

在Pod创建成功之后,应用容器中的/data目录使用的就是CSI存储插件提供的存储。
我们通过kubelet的日志可以查看到Volume挂载的详细过程。

每种CSI存储插件都提供了容器镜像,与external-attacher、external-provisioner、node-driver-registrar等sidecar辅助容器共同完成存储插件系统的部署,每个插件的部署配置详见官网https://kubernetes-csi.github.io/docs/drivers.html中的链接。

Kubernetes从1.12版本开始引入存储卷快照(Volume Snapshots)功能,通过新的CRD自定义资源对象VolumeSnapshotContent、VolumeSnapshot和VolumeSnapshotClass进行管理。VolumeSnapshotContent定义从当前PV创建的快照,类似于一个新的PV;VolumeSnapshot定义需要绑定某个快照的请求,类似于PVC的定义;VolumeSnapshotClass用于屏蔽VolumeSnapshotContent的细节,类似于StorageClass的功能。

下面是一个VolumeSnapshotContent的例子:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
1apiVersion: snapshot.storage.k8s.io/v1alpha1
2kind: VolumeSnapshotContent
3metadata:
4  name: new-snapshot-content-test
5spec:
6  snapshotClassName: csi-hostpath-snapclass
7  source:
8    name: pvc-test
9    kind: PersistentVolumeClaim
10  volumeSnapshotSource:
11    csiVolumeSnapshotSource:
12      driver:          csi-hostpath
13      restoreSize:     10Gi
14

下面是一个VolumeSnapshot的例子:


1
2
3
4
5
6
7
8
9
10
1apiVersion: snapshot.storage.k8s.io/v1alpha1
2kind: VolumeSnapshot
3metadata:
4  name: new-snapshot-test
5spec:
6  snapshotClassName: csi-hostpath-snapclass
7  source:
8    name: pvc-test
9    kind: PersistentVolumeClaim
10

后续要进一步完善的工作如下。
(1)将以下Alpha版功能更新到Beta版:
◎ Raw Block类型的Volumes;
◎ 拓扑感知,Kubernetes理解和影响CSI卷的配置位置(如zone、region 等)的能力;
◎ 完善基于CRD的扩展功能(如Skip attach、Pod info on mount等)。

(2)完善对本地短暂卷(Local Ephemeral Volume)的支持。
(3)将Kubernetes“in-tree”存储卷插件迁移到CSI。

小结:

            本节内容到此结束,内容有点多,慢慢消化。

 

给TA打赏
共{{data.count}}人
人已打赏
安全运维

故障复盘的简洁框架-黄金三问

2021-9-30 19:18:23

安全运维

OpenSSH-8.7p1离线升级修复安全漏洞

2021-10-23 10:13:25

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索