K8s卷存储详解(二)
- 持久卷
- 持久卷(PV)
- 持久卷申领(PVC)
- 存储类(StorageClass)
- 存储制备器(Provisioner)
- PV和PVC的生命周期
- 持久卷的类型
- CSI 卷快照
- CSI 卷克隆
 
- 投射卷
- ConfigMap
- Secret
 
持久卷
容器销毁时,容器数据持久化的问题解决了。但新的问题出现了,如果某个Node节点故障了,Pod如果转移到了别的节点,本地临时存储的数据就不能访问了,此时需要网络存储系统来解决这个问题。
网络存储方案有很多,比较常用的如NFS、CIFS。为了屏蔽网络存储底层实现的细节,方便用户使用, kubernetes引入PV(持久卷:Persistent Volume)和 PVC(持久卷申领:Persistent Volume Claim)两种资源对象。

持久卷(PV)
PV是通过PVC来实现和Pod动态绑定, 在Pod定义里指定创建好的PVC, 然后PVC会根据Pod的要求去自动绑定合适的PV给Pod使用。
因此PVC是用户对存储的请求,期望。
PV访问模式(accessModes)
每个 PV 卷的访问模式都会设置为对应卷所支持的模式值
访问模式有:
- RWO(ReadWriteOnce): PV以读写方式挂载到一个节点,允许运行在同一节点上的多个 Pod 访问卷。
- RWX(ReadWriteMany):PV以读写方式挂载到多个节点。
- ROX(ReadOnlyMany):PV以只读方式挂载到多个节点。
- RWO(PReadWriteOncePod):(特性状态: Kubernetes v1.27 [beta])卷可以被单个 Pod 以读写方式挂载。如你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC(只支持 CSI 卷以及需要 Kubernetes 1.22 以上版本。)更详细内容见: Introducing Single Pod Access Mode for PersistentVolumes
持久卷申领(PVC)
概念上与 Pod 类似, Pod 会耗用节点资源,而 PVC 申领会耗用 PV 资源。Pod 可以请求特定数量的资源(CPU 和内存);同样 PVC 申领也可以请求特定的大小和访问模式 。
存储类(StorageClass)
PV的制备有两种方式:
-  静态制备 
-  动态制备 
静态制备:
 集群管理员手动创建若干PV卷(这些卷对象带有真实存储的细节信息, 并且对集群用户可用(可见)),供容器消费使用。静态制备的缺点是维护成本很高。
动态制备:
 集群管理员也可以使用StorageClass实现PV资源的动态制备。每个StorageClass都包含provisioner、parameters和reclaimPolicy字段。
默认存储类
基于存储类完成动态的存储制备,集群管理员需要在 API 服务器上启用默认存储类 DefaultStorageClass 准入控制器。
如果持久卷申领PersistentVolumeClaim创建的请求,没有指定存储类,则会添加默认存储类。
只能设定一种存储类为默认的存储类,不能将多种存储类设置为默认;如果不小心设置了多个DefaultStorageClass, 当 PVC 动态配置时,将使用最新设置的DefaultStorageClass。如果默认存储类没有设置时,此准入控制器不执行任何操作
注意,某些云服务提供商可能已经定义了一个默认的 StorageClass
如何改变默认存储类?
1.列出你的集群中的 StorageClasses
$ kubectl get storageclass
NAME                 PROVISIONER               AGE
standard (default)   kubernetes.io/gce-pd      1d
gold                 kubernetes.io/gce-pd      1d
默认 StorageClass 以 (default) 标记
2.标记默认 StorageClass 非默认:
要标记一个 StorageClass 为非默认的,你需要改变它的值为 false:
kubectl patch storageclass standard -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}'
默认 StorageClass 的注解 storageclass.kubernetes.io/is-default-class 设置为 true。 注解的其它任意值或者缺省值将被解释为 false。
这里的 standard 是你选择的 StorageClass 的名字。
3.标记一个 StorageClass 为默认的:
kubectl patch storageclass <your-class-name> -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
4.验证你选用的 StorageClass 为默认的:
$ kubectl get storageclass
NAME             PROVISIONER               AGE
standard         kubernetes.io/gce-pd      1d
gold (default)   kubernetes.io/gce-pd      1d
存储制备器(Provisioner)
每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备 PV。 该字段必须指定。

官方内部支持的制备器:
| 卷插件 | 内置制备器 | 配置示例 | 
|---|---|---|
| AWSElasticBlockStore | ✓ | AWS EBS | 
| AzureFile | ✓ | Azure File | 
| AzureDisk | ✓ | Azure Disk | 
| CephFS | - | - | 
| Cinder | ✓ | OpenStack Cinder | 
| FC | - | - | 
| FlexVolume | - | - | 
| GCEPersistentDisk | ✓ | GCE PD | 
| iSCSI | - | - | 
| NFS | - | NFS | 
| RBD | ✓ | Ceph RBD | 
| VsphereVolume | ✓ | vSphere | 
| PortworxVolume | ✓ | Portworx Volume | 
| Local | - | Local | 
PV和PVC的生命周期
1.制备
2.绑定
控制平面中的控制回路监测新的 PVC 对象,寻找与之匹配的 PV 卷(如果可能的话), 并将二者绑定到一起
如果找不到匹配的 PV 卷,PVC 申领会无限期地处于未绑定状态。
3.使用
Pod 将 PVC 申领当做存储卷来使用。集群会检视 PVC 申领,找到所绑定的卷, 并为 Pod 挂载该卷。对于支持多种访问模式的卷, 用户要在 Pod 中以卷的形式使用申领时指定期望的访问模式。
4.回收(Reclaiming)
当用户不再使用其存储卷时,他们可以从 API 中将 PVC 对象删除, 从而允许该资源被回收再利用。
回收策略
-  保留(Retain) 
-  删除(Delete) 
-  回收(Recycle):回收策略 Recycle已被废弃。取而代之的建议方案是使用动态制备。
持久卷的类型
PV 持久卷是用插件的形式来实现的。Kubernetes 目前支持以下插件:
cephfs- CephFS volume
csi- 容器存储接口 (CSI)
fc- Fibre Channel (FC) 存储
hostPath- HostPath 卷 (仅供单节点测试使用;不适用于多节点集群;请尝试使用local卷作为替代)
iscsi- iSCSI (SCSI over IP) 存储
local- 节点上挂载的本地存储设备
nfs- 网络文件系统 (NFS) 存储
rbd- Rados 块设备 (RBD) 卷以下的持久卷已被弃用。这意味着当前仍是支持的,但是 Kubernetes 将来的发行版会将其移除。
awsElasticBlockStore- AWS 弹性块存储(EBS) (于 v1.17 弃用)
azureDisk- Azure Disk (于 v1.19 弃用)
azureFile- Azure File (于 v1.21 弃用)
cinder- Cinder(OpenStack 块存储)(于 v1.18 弃用)
flexVolume- FlexVolume (于 v1.23 弃用)
gcePersistentDisk- GCE Persistent Disk (于 v1.17 弃用)
portworxVolume- Portworx 卷 (于 v1.25 弃用)
vsphereVolume- vSphere VMDK 卷 (于 v1.19 弃用)旧版本的 Kubernetes 仍支持这些“树内(In-Tree)”持久卷类型:
photonPersistentDisk- Photon 控制器持久化盘。(从 v1.15 版本开始将不可用)
scaleIO- ScaleIO 卷(v1.21 之后不可用)
flocker- Flocker 存储 (v1.25 之后不可用)
quobyte- Quobyte 卷 (v1.25 之后不可用)
storageos- StorageOS 卷 (v1.25 之后不可用)
CSI 卷快照
特性状态: Kubernetes v1.20 [stable]
卷快照(Volume Snapshot)仅支持树外 CSI 卷插件。 有关细节可参阅卷快照文档。 树内卷插件被弃用。
卷快照能力为 Kubernetes 用户提供了一种标准的方式来在指定时间点复制卷的内容,并且不需要创建全新的卷。 例如,这一功能使得数据库管理员能够在执行编辑或删除之类的修改之前对数据库执行备份。
注意:
- API 对象
VolumeSnapshot,VolumeSnapshotContent和VolumeSnapshotClass是 CRD, 不属于核心 API。
VolumeSnapshot支持仅可用于 CSI 驱动。- 作为
VolumeSnapshot部署过程的一部分,Kubernetes 团队提供了一个部署于控制平面的快照控制器, 并且提供了一个叫做csi-snapshotter的边车(Sidecar)辅助容器,和 CSI 驱动程序一起部署。 快照控制器监视VolumeSnapshot和VolumeSnapshotContent对象, 并且负责创建和删除VolumeSnapshotContent对象。 边车 csi-snapshotter 监视VolumeSnapshotContent对象, 并且触发针对 CSI 端点的CreateSnapshot和DeleteSnapshot的操作。- 还有一个验证性质的 Webhook 服务器,可以对快照对象进行更严格的验证。 Kubernetes 发行版应将其与快照控制器和 CRD(而非 CSI 驱动程序)一起安装。 此服务器应该安装在所有启用了快照功能的 Kubernetes 集群中。
- CSI 驱动可能实现,也可能没有实现卷快照功能。CSI 驱动可能会使用 csi-snapshotter 来提供对卷快照的支持。详见 CSI 驱动程序文档
- Kubernetes 负责 CRD 和快照控制器的安装。
CSI 卷克隆
克隆(Clone),意思是为已有的 Kubernetes 卷创建副本,它可以像任何其它标准卷一样被使用。 唯一的区别就是配置后,后端设备将创建指定完全相同的副本,而不是创建一个“新的”空卷。
用户在使用该功能时,需要注意以下事项:
- 克隆支持(
VolumePVCDataSource)仅适用于 CSI 驱动。- 克隆支持仅适用于 动态供应器。
- CSI 驱动可能实现,也可能未实现卷克隆功能。
- 仅当 PVC 与目标 PVC 存在于同一命名空间(源和目标 PVC 必须在相同的命名空间)时,才可以克隆 PVC。
- 支持用一个不同存储类进行克隆。
- 目标卷和源卷可以是相同的存储类,也可以不同。
- 可以使用默认的存储类,也可以在 spec 中省略 storageClassName 字段。
- 克隆只能在两个使用相同 VolumeMode 设置的卷中进行 (如果请求克隆一个块存储模式的卷,源卷必须也是块存储模式)。
投射卷
和普通的存储卷不一样,它们不是用来存储容器的数据,也不是用来进行容器和宿主机之间的数据交换(Docker Volume的作用)。这些特殊的Volume是为容器提供预先定义好的数据。
可以被投射的卷类型
- secret:用来给POD传递敏感信息的,例如:密码
- downwardAPI:为应用提供 downward API 数据。 在这类卷中,所公开的数据以纯文本格式的只读文件形式存在
- configMap:提供了向POD中注入配置信息方式,ConfigMap 对象中存储的数据可以被
configMap类型的卷引用,然后被 Pod 中运行的容器化应用使用。- serviceAccountToken:可以将服务账号的token注入到POD中,使用该token来访问Kubernetes API 服务器。
所有的卷源都要求处于 Pod 所在的同一个名字空间内
ConfigMap
ConfigMap是一种比较特殊的存储卷,它的主要作用是用来存储配置信息的。
示例:
apiVersion: v1
kind: ConfigMap
metadata:
  name: configmap
  namespace: dev
data:
  info: |
    username:admin
    password:123456
创建config map
# 创建configmap
[root@k8s-master01 ~]# kubectl create -f configmap.yaml
configmap/configmap created
# 查看configmap详情
[root@k8s-master01 ~]# kubectl describe cm configmap -n dev
Name:         configmap
Namespace:    dev
Labels:       <none>
Annotations:  <none>
Data
====
info:
----
username:admin
password:123456
Events:  <none>
接下来创建一个pod-configmap.yaml,将上面创建的configmap挂载进去
apiVersion: v1
kind: Pod
metadata:
  name: pod-configmap
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    volumeMounts: 						# 将configmap挂载到目录
    - name: config1
      mountPath: /configmap/config 		# 挂载的目录
  volumes: 								# 引用configmap
  - name: config1
    configMap:
      name: configmap
创建pod
[root@k8s-master01 ~]# kubectl create -f pod-configmap.yaml
pod/pod-configmap created
# 查看pod
[root@k8s-master01 ~]# kubectl get pod pod-configmap -n dev
NAME            READY   STATUS    RESTARTS   AGE
pod-configmap   1/1     Running   0          6s
# 进入容器
[root@k8s-master01 ~]# kubectl exec -it pod-configmap -n dev /bin/sh
# cd /configmap/config/
# ls
info
# more info
username:admin
password:123456
# 可以看到映射已经成功,每个configmap都映射成了一个目录
# key--->文件     value---->文件中的内容
# 此时如果更新configmap的内容, 容器中的值也会动态更新
Secret
在kubernetes中,还存在一种和ConfigMap非常类似的对象,称为Secret对象。它主要用于存储敏感信息,例如密码、秘钥、证书等等。
1.首先使用base64对数据进行编码
[root@k8s-master01 ~]# echo -n 'admin' | base64 #准备username
YWRtaW4=
[root@k8s-master01 ~]# echo -n '123456' | base64 #准备password
MTIzNDU2
2.接下来编写secret.yaml,并创建Secret
apiVersion: v1
kind: Secret
metadata:
  name: secret
  namespace: dev
type: Opaque
data:
  username: YWRtaW4=
  password: MTIzNDU2
创建secret
[root@k8s-master01 ~]# kubectl create -f secret.yaml
secret/secret created
# 查看secret详情
[root@k8s-master01 ~]# kubectl describe secret secret -n dev
Name:         secret
Namespace:    dev
Labels:       <none>
Annotations:  <none>
Type:  Opaque
Data
====
password:  6 bytes
username:  5 bytes
3.创建pod-secret.yaml,将上面创建的secret挂载进去:
apiVersion: v1
kind: Pod
metadata:
  name: pod-secret
  namespace: dev
spec:
  containers:
  - name: nginx
    image: nginx:1.17.1
    volumeMounts: # 将secret挂载到目录
    - name: config
      mountPath: /secret/config #挂载的目录
  volumes:
  - name: config
    secret:
      secretName: secret
创建Pod
# 创建pod
[root@k8s-master01 ~]# kubectl create -f pod-secret.yaml
pod/pod-secret created
# 查看pod
[root@k8s-master01 ~]# kubectl get pod pod-secret -n dev
NAME            READY   STATUS    RESTARTS   AGE
pod-secret      1/1     Running   0          2m28s
# 进入容器,查看secret信息,发现已经自动解码了
[root@k8s-master01 ~]# kubectl exec -it pod-secret /bin/sh -n dev
/ # ls /secret/config/
password  username
/ # more /secret/config/username
admin
/ # more /secret/config/password
123456
参阅:
k8s存储:卷、持久卷、存储类
k8s数据存储详解
存储
K8S系列之存储



















