Volumn和PV的关系
Volumn卷
属于 Pod 内部共享资源存储,生命周期和 Pod 相同,与 Container 无关,即使 Pod 上的容 器停止或者重启,Volume 不会受到影响,但是如果 Pod 终止,那么这个 Volume 的生命周 期也将结束。
存储的基本组件
在 k8s 中 对 于 存 储 的 资 源 抽 象 了 两 个 概 念 , 分 别 是 PersistentVolume(PV)和PersistentVolumeClaim(PVC)。
PV 是集群中的资源
PVC 是对这些资源的请求
PV 和 PVC 都只是抽象的概念,在 k8s 中是通过插件的方式提供具体的存储实现。
目前包含 有 NFS、ceph、iSCSI 和云提供商指定的存储系统
管理存储是管理计算的一个明显问题。
该 PersistentVolume 子系统为用户和管理员提供 了一个 API,用于抽象如何根据消费方式提供存储的详细信息。
为此,我们引入了两个新的 API 资源:PersistentVolume 和 PersistentVolumeClaim
PV/PVC/StorageClass
PersistentVolume(持久化卷):
是对底层的共享存储的一种抽象,PV 由管理员 进行创建和配置,它和具体的底层的共享存储技术的实现方式有关,比如 Ceph、GlusterFS、 NFS 等,都是通过插件机制完成与共享存储的对接
PersistentVolumeClaim(PVC):
是由用户进行存储的请求。 它类似于 pod。 Pod 消耗 节点资源,PVC 消耗 PV 资源。Pod 可以请求特定级别的资源(CPU 和内存)。声明可以请 求特定的大小和访问模式(例如,可以一次读/写或多次只读)。
StorageClass :
为管理员提供了一种描述他们提供的存储的“类”的方法。
不同的类可能映 射到服务质量级别,或备份策略,或者由群集管理员确定的任意策略。
Kubernetes 本身对于什么类别代表是不言而喻的。 这个概念有时在其他存储系统中称为“配置文件”。
PV 是运维人员来创建的,开发操作 PVC,可是大规模集群中可能会有很多 PV,如果这 些 PV 都需要运维手动来处理这也是一件很繁琐的事情,所以就有了动态供给概念.
也就是 Dynamic Provisioning,动态供给的关键就是 StorageClass,它的作用就是创建 PV 模板。
创 建 StorageClass 里面需要定义 PV 属性比如存储类型、大小等;另外创建这种 PV 需要用到 存储插件。最终效果是,用户提交PVC,里面指定存储类型,如果符合我们定义的StorageClass, 则会为其自动创建 PV 并进行绑定
PVC 和 PV 是一一对应
PV 和 PVC 中的 spec 关键字段要匹配,比如存储(storage)大小。
PV 和 PVC 中的 storageClassName 字段必须一致。
四个概念的关系
Kubernetes 中 存 储 中 有 四 个 重 要 的 概 念 : Volume 、 PersistentVolume ( PV )、 PersistentVolumeClaim(PVC)、StorageClass。
声明周期
PV 是群集中的资源。PVC 是对这些资源的请求,并且还充当对资源的检查。
PV 和 PVC 之间的相互作用遵循以下生命周期:
Provisioning ——-> Binding ——–>Using——>Releasing——>Recycling
供应准备 Provisioning
通过集群外的存储系统或者云平台来提供存储持久化支持
- 静态提供 Static:集群管理员创建多个 PV。 它们携带可供集群用户使用的真实存储的详 细信息。 它们存在于 Kubernetes API 中,可用于消费
- 动态提供 Dynamic:当管理员创建的静态 PV 都不匹配用户的 PersistentVolumeClaim 时, 集群可能会尝试为 PVC 动态配置卷。 此配置基于 StorageClasses:PVC 必须请求一个类, 并且管理员必须已创建并配置该类才能进行动态配置。 要求该类的声明有效地为自己禁用 动态配置。
绑定 Binding
用户创建 pvc 并指定需要的资源和访问模式。在找到可用 pv 之前,pvc 会 保持未绑定状态。
使用 Using
用户可在 pod 中像 volume 一样使用 pvc。
释放 Releasing
用户删除 pvc 来回收存储资源,pv 将变成“released”状态。由于还保留着 之前的数据,这些数据需要根据不同的策略来处理,否则这些存储资源无法被其他 pvc 使 用。
回收Recycling
pv可以设置三种回收策略:保留(Retain),回收(Recycle)和删除(Delete)。
- 保留策略:允许人工处理保留的数据。
- 删除策略:将删除 pv 和外部关联的存储资源,需要插件支持。
- 回收策略:将执行清除操作,之后可以被新的 pvc 使用,需要插件支持。 注:目前只有 NFS 和 HostPath 类型卷支持回收策略,AWS EBS,GCE PD,Azure Disk 和 Cinder 支持删除(Delete)策略
PV 类型
- GCEPersistentDisk
- AWSElasticBlockStore
- AzureFile AzureDisk
- FC (Fibre Channel)
- Flexvolume
- NFS
- iSCSI
- RBD (Ceph Block Device)
- CephFS
- Cinder (OpenStack block storage)
- Glusterfs
- VsphereVolume
- Quobyte Volumes
- StorageOS
PV 卷阶段状态
- Available – 资源尚未被 claim 使用
- Bound – 卷已经被绑定到 claim 了
- Released – claim 被删除,卷处于释放状态,但未被集群回收。
- Failed – 卷自动回收失败
k8s存储架构图
Kubernetes 卷插件实现方法
In Tree
Kubernetes 卷插件目前是“in-tree”,意味着它们与核心 kubernetes 二进制文件链接,编译, 构建和一起发布。
有不利于核心代码的发布,增加了工作量,并且卷插件的权限太高等缺点 需要将后端存储的代码逻辑放到 K8S 的代码中运行。逻辑代码可能会引起与 K8S 其他部件 之间的相互影响
Out-of-tree Provisioner-FlexVolume
现有的 Flex Volume 插件需要访问节点和主机的根文件系统才能部署第三方驱动程序文件, 并且对主机的依赖性强.
调用一个主机的可执行程序包的方式执行存储卷的挂载使用。
解决了 In-Tree 方式的强耦合, 不过命令行调用的方式,在主机安全性、部署依赖的容器化、与 K8S 服务之间的相互扩展性 等方面存在不足Flexvolume 运行在 host 空间,不能使用 rbac授权机制访问Kubernetes API, 导致其功能极大的受限 Storage providers support this type and develop Driver
Out-of-tree Provisioner -CSI
容器存储接口(CSI)是由来自各个 CO 的社区成员(包括 Kubernetes,Mesos,Cloud Foundry 和 Docker)之间的合作产生的规范。
此接口的目标是为 CO 建立标准化机制,以将任意存 储系统暴露给其容器化工作负载。
CSI 标准使 K8S 和存储提供者之间将彻底解耦,将存储的所有的部件作为容器形式运行在 K8S 上。 CO Container Orchestrer Storage providers support this type and develop Driver
NFS实现存储
创建一个 NFS 服务器作为 K8s 的存储系统, nfs 默认不支持动态存储。
使用了第三方的 NFS 插件 实现 flexvolume and CSI 方式使用存 储。
NFS服务建立
- 安装nfs-server
yum install nfs-utils rpcbind -y systemctl enable rpcbind nfs-server systemctl start rpcbind nfs-server systemctl status rpcbind nfs-server
- 创建共享目录
mkdir /nfs-server
- 配置共享目录
vi /etc/exports # 编辑或创建/etc/exports文件 /nfs-server *(rw,sync,no_root_squash # 配置nfs-server
- 对/etc/exports 增加或修改的部分进行挂载和卸载
exportfs -a
注意: exportfs 命令,参考 https://wker.com/linux-command/exportfs.html
Client 安装NFS服务
Note: 在每一个节点都需要安装NFS服务
yum install nfs-utils rpcbind -y systemctl enable rpcbind nfs-server systemctl start rpcbind nfs-server systemctl status rpcbind nfs-server
测试共享卷是否可用
showmount -e 192.168.56.5 # nfs server 的ip mount -t nfs 192.168.56.5:/nfs-server /home # bind 共享卷 umount /home # unbind 共享卷
NFS共享存储实践
静态供应
- 创建PV
pv1.yaml
apiVersion: v1 kind: PersistentVolume metadata: name: pv0001 spec: capacity: storage: 5Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Recycle nfs: path: "/nfs-server" server: 192.168.10.201
- 创建PVC
pvc1.yaml
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nfs-pvc spec: accessModes: - ReadWriteMany resources: requests: storage: 1Gi
- 创建Pod
pod1.yaml
kind: Pod apiVersion: v1 metadata: name: nfs-test spec: containers: - name: my-busybox image: busybox volumeMounts: - mountPath: "/data" name: sample-volume command: ["sleep", "1000000"] imagePullPolicy: IfNotPresent volumes: - name: sample-volume persistentVolumeClaim: claimName: nfs-pvc
- 测试
进入 pod容器中,在/data目录创建一个test.txt文件
查看nfs-server 是否包含共享了test.txt文件
NFS CSI类型动态供应
NFS CSI Driver 是 K8s 官方提供的 CSI 示例程序,只实现了 CSI 的最简功能 Controller 由 CSI Plugin+csi-provisioner+livenessprobe 组成 node-server 由 CSI Plugin+liveness-probe+node-driver-registrar 组成
安装CSI组件
rbac-csi.yaml 文件配置
---apiVersion v1 kind ServiceAccount metadata name csi-nfs-controller-sa namespace kube-system ---kind ClusterRole apiVersion rbac.authorization.k8s.io/v1 metadata name nfs-external-provisioner-role rulesapiGroups"" 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"get""list""watch""create""update""patch"apiGroups"storage.k8s.io" resources"csinodes" verbs"get""list""watch"apiGroups"" resources"nodes" verbs"get""list""watch"apiGroups"coordination.k8s.io" resources"leases" verbs"get""list""watch""create""update""patch"apiGroups"" resources"secrets" verbs"get"---kind ClusterRoleBinding apiVersion rbac.authorization.k8s.io/v1 metadata name nfs-csi-provisioner-binding subjectskind ServiceAccount name csi-nfs-controller-sa namespace kube-system roleRef kind ClusterRole name nfs-external-provisioner-role apiGroup rbac.authorization.k8s.io
csi-nfs-driverinfo.yaml 文件配置
---apiVersion storage.k8s.io/v1 kind CSIDriver metadata name nfs.csi.k8s.io spec attachRequiredfalse volumeLifecycleModes Persistent Ephemeral
csi-nfs-node.yaml 文件配置
---# This YAML file contains driver-registrar & csi driver nodeplugin API objects# that are necessary to run CSI nodeplugin for nfskind DaemonSet apiVersion apps/v1 metadata name csi-nfs-node namespace kube-system spec updateStrategy rollingUpdate maxUnavailable1 type RollingUpdate selector matchLabels app csi-nfs-node template metadata labels app csi-nfs-node spec hostNetwork true # original nfs connection would be broken without hostNetwork setting dnsPolicy ClusterFirstWithHostNet nodeSelector kubernetes.io/os linux tolerationsoperator"Exists" containersname liveness-probe image registry.cn-hangzhou.aliyuncs.com/liuyik8s/livenessprobe v2.5.0 args --csi-address=/csi/csi.sock --probe-timeout=3s --health-port=29653 --v=2 volumeMountsname socket-dir mountPath /csi resources limits memory 100Mi requests cpu 10m memory 20Mi name node-driver-registrar image registry.cn-hangzhou.aliyuncs.com/liuyik8s/csi-node-driver-registrar v2.4.0 args --v=2 --csi-address=/csi/csi.sock --kubelet-registration-path=$(DRIVER_REG_SOCK_PATH) livenessProbe exec command /csi-node-driver-registrar --kubelet-registration-path=$(DRIVER_REG_SOCK_PATH) --mode=kubelet-registration-probe initialDelaySeconds30 timeoutSeconds15 envname DRIVER_REG_SOCK_PATH value /var/lib/kubelet/plugins/csi-nfsplugin/csi.sock name KUBE_NODE_NAME valueFrom fieldRef fieldPath spec.nodeName volumeMountsname socket-dir mountPath /csi name registration-dir mountPath /registration resources limits memory 100Mi requests cpu 10m memory 20Mi name nfs securityContext privilegedtrue capabilities add"SYS_ADMIN" allowPrivilegeEscalationtrue image mcr.microsoft.com/k8s/csi/nfs-csi v3.1.0 args"-v=5""--nodeid=$(NODE_ID)""--endpoint=$(CSI_ENDPOINT)" envname NODE_ID valueFrom fieldRef fieldPath spec.nodeName name CSI_ENDPOINT value unix ///csi/csi.sock portscontainerPort29653 name healthz protocol TCP livenessProbe failureThreshold5 httpGet path /healthz port healthz initialDelaySeconds30 timeoutSeconds10 periodSeconds30 imagePullPolicy"IfNotPresent" volumeMountsname socket-dir mountPath /csi name pods-mount-dir mountPath /var/lib/kubelet/pods mountPropagation"Bidirectional" resources limits memory 300Mi requests cpu 10m memory 20Mi volumesname socket-dir hostPath path /var/lib/kubelet/plugins/csi-nfsplugin type DirectoryOrCreate name pods-mount-dir hostPath path /var/lib/kubelet/pods type Directory hostPath path /var/lib/kubelet/plugins_registry type Directory name registration-dir
csi-nfs-controller.yaml 文件配置
---kind Deployment apiVersion apps/v1 metadata name csi-nfs-controller namespace kube-system spec replicas2 selector matchLabels app csi-nfs-controller template metadata labels app csi-nfs-controller spec hostNetwork true # controller also needs to mount nfs to create dir dnsPolicy ClusterFirstWithHostNet serviceAccountName csi-nfs-controller-sa nodeSelector kubernetes.io/os linux # add "kubernetes.io/role: master" to run controller on master node priorityClassName system-cluster-critical tolerationskey"node-role.kubernetes.io/master" operator"Exists" effect"NoSchedule"key"node-role.kubernetes.io/controlplane" operator"Exists" effect"NoSchedule" containersname csi-provisioner image registry.cn-hangzhou.aliyuncs.com/liuyik8s/csi-provisioner v2.2.2 args"-v=2""--csi-address=$(ADDRESS)""--leader-election" envname ADDRESS value /csi/csi.sock volumeMountsmountPath /csi name socket-dir resources limits memory 400Mi requests cpu 10m memory 20Mi name liveness-probe image registry.cn-hangzhou.aliyuncs.com/liuyik8s/livenessprobe v2.5.0 args --csi-address=/csi/csi.sock --probe-timeout=3s --health-port=29652 --v=2 volumeMountsname socket-dir mountPath /csi resources limits memory 100Mi requests cpu 10m memory 20Mi name nfs image mcr.microsoft.com/k8s/csi/nfs-csi v3.1.0 securityContext privilegedtrue capabilities add"SYS_ADMIN" allowPrivilegeEscalationtrue imagePullPolicy IfNotPresent args"-v=5""--nodeid=$(NODE_ID)""--endpoint=$(CSI_ENDPOINT)" envname NODE_ID valueFrom fieldRef fieldPath spec.nodeName name CSI_ENDPOINT value unix ///csi/csi.sock portscontainerPort29652 name healthz protocol TCP livenessProbe failureThreshold5 httpGet path /healthz port healthz initialDelaySeconds30 timeoutSeconds10 periodSeconds30 volumeMountsname pods-mount-dir mountPath /var/lib/kubelet/pods mountPropagation"Bidirectional"mountPath /csi name socket-dir resources limits memory 200Mi requests cpu 10m memory 20Mi volumesname pods-mount-dir hostPath path /var/lib/kubelet/pods type Directory name socket-dir emptyDir
storageclass-nfs.yaml 文件配置
---apiVersion storage.k8s.io/v1 kind StorageClass metadata name nfs-csi provisioner nfs.csi.k8s.io parameters server 192.168.56.5 #修改为自己的nfs的服务器地址 share /nfs-server #修改为nfs的目录reclaimPolicy Retain # only retain is supported,目前这个回收策略只支持RetainvolumeBindingMode Immediate mountOptions hard nfsvers=4.1
安装CSI
kubectl apply -f rbac-csi.yaml kubectl apply -f csi-nfs-driverinfo.yaml kubectl apply -f csi-nfs-node.yaml kubectl apply -f csi-nfs-controller.yaml kubectl apply -f storageclass-nfs.yaml
官网连接更新参考:
https://artifacthub.io/packages/helm/keyporttech/csi-driver-nfs/0.1.4https://github.com/kubernetes-csi/csi-driver-nfs/blob/master/deploy/example/README.md
CSI动态供应存储创建 PVC/POD
Deployment 无状态使用
pod-pvc-csi.yaml 文件配置
apiVersion v1 kind PersistentVolumeClaim metadata name nfs2 # annotations:# hitachi.io/note: "pvc-sample"spec accessModes ReadWriteOnce resources requests storage 5Gi storageClassName nfs-csi # 指定className,动态创建pv---kind Pod apiVersion v1 metadata name nfs-test-csi spec containersname my-busybox image busybox volumeMountsmountPath"/data" name sample-volume command"sleep""1000000" imagePullPolicy IfNotPresent volumesname sample-volume persistentVolumeClaim claimName nfs2
安装 pod-pvc-csi.yaml
kubectl apply -f pod-pvc-csi.yaml
查看pvc
查看storageClassName
测试
进入 nfs-test-csi 容器中,创建 test-csi.txt
查看nfs-server的共享卷
Statefulset 有状态使用
apiVersion v1 kind Service metadata name nginx labels app nginx spec portsport80 name web clusterIP None selector app nginx ---apiVersion apps/v1 kind StatefulSet metadata name web spec serviceName"nginx" replicas3 selector matchLabels app nginx volumeClaimTemplatesmetadata name test annotations volume.beta.kubernetes.io/storage-class"nfs-csi" spec accessModes"ReadWriteOnce" resources requests storage 2Gi #---- template metadata labels app nginx spec containersname nginx image nginx1.9 portscontainerPort80 name web volumeMountsname test readOnlyfalse mountPath"/usr/share/nginx/html"