# 存储基础知识
#### Volume（卷）
容器中的文件在磁盘上是临时存放的，这给容器中运行的较重要的应用程序带来如下两个问题：
- 当容器重建时，容器中的文件将会丢失。
- 当在一个Pod中同时运行多个容器时，容器间需要共享文件。
Kubernetes抽象出了Volume（卷）来解决以上两个问题。Kubernetes的Volume是Pod的一部分，Volume不是单独的对象，不能独立创建，只能在Pod中定义。Pod中的所有容器都可以使用Volume，但需要将Volume挂载到容器中的目录下。
实际中使用容器存储如下图所示，可将同一个Volume挂载到不同的容器中，实现不同容器间的存储共享。
![](https://support.huaweicloud.com/usermanual-cce/zh-cn_image_0000001539603581.png)
存储卷的基本使用原则如下：
- 一个Pod可以挂载多个Volume。虽然单Pod可以挂载多个Volume，但是并不建议给一个Pod挂载过多卷。
- 一个Pod可以挂载多种类型的Volume。
- 每个被Pod挂载的Volume卷，可以在不同的容器间共享。
- Kubernetes环境推荐使用PVC和PV方式挂载Volume。
![](https://support.huaweicloud.com/usermanual-cce/public_sys-resources/note_3.0-zh-cn.png)
卷（Volume）的生命周期与挂载它的Pod相同，即Pod被删除的时候，Volume也一起被删除。但是Volume里面的文件可能在Volume消失后仍然存在，这取决于Volume的类型。
Kubernetes提供了非常丰富的Volume类型，主要可分为In-Tree和Out-of-Tree两个大类：
| 卷（Volume）分类   | 描述                                                                                                                                                                                                                                                                                                                                                                              |
|:---|:---|
| In-Tree        | In-Tree卷是通过Kubernetes代码仓库维护的，与Kubernetes二进制文件一起构建、编译、发布，当前Kubernetes已不再接受新的In-Tree卷类型。 例如Kubernetes原生支持的HostPath、EmptyDir、Secret和ConfigMap等，均为早期纳入的In-Tree卷类型。 而PVC（PersistentVolumeClaim）可以说是一种特殊的In-Tree卷，Kubernetes使用这种类型的卷从In-Tree模式向Out-of-Tree模式进行转换，这种类型的卷允许使用者在不同的存储供应商环境中"申请"使用底层存储创建的PV（PersistentVolume）。 |
| Out-of-Tree | Out-of-Tree卷包括容器存储接口（CSI）和FlexVolume（已弃用），存储供应商只需遵循一定的规范即可创建自定义存储插件，创建可供Kubernetes使用的PV，而无需将插件源码添加到Kubernetes代码仓库。例如SFS、OBS等云存储都是通过在集群中安装存储驱动的形式使用的，需要在集群中创建对应的PV，然后使用PVC挂载到Pod中。                                                                                                                                                                                               |
   
#### PV与PVC
Kubernetes抽象了PV（PersistentVolume）和PVC（PersistentVolumeClaim）来定义和使用存储，从而让使用者不用关心具体的基础设施，当需要存储资源的时候，只要像CPU和内存一样，声明要多少即可。
- PV：PV是PersistentVolume的缩写，译为持久化存储卷，描述的是一个集群里的持久化存储卷，它和节点一样，属于集群级别资源，其对象作用范围是整个Kubernetes集群。PV可以有自己的独立生命周期，不依附于Pod。
- PVC：PVC是PersistentVolumeClaim的缩写，译为持久化存储卷声明，描述的是负载对存储的申领。为应用配置存储时，需要声明一个存储需求（即PVC），Kubernetes会通过最佳匹配的方式选择一个满足需求的PV，并与PVC绑定。PVC与PV是一一对应关系，在创建PVC时，需描述请求的持久化存储的属性，比如，存储的大小、可读写权限等等。
在Pod中可以使用Volume关联PVC，即可让Pod使用到存储资源，它们之间的关系如下图所示。
图1PVC绑定PV   
![](https://support.huaweicloud.com/usermanual-cce/zh-cn_image_0000001488843610.png "点击放大")
 #### CSI
CSI（Container Storage Interface，容器存储接口）是容器标准存储接口规范，也是Kubernetes社区推荐的存储插件实现方案。[CCE容器存储（Everest）](https://support.huaweicloud.com/usermanual-cce/cce_10_0066.html)是CCE基于CSI开发的自研存储插件，能够为容器提供不同类型的持久化存储功能。
 #### 存储卷访问模式
存储卷只能以底层存储资源所支持的方式挂载到宿主系统上。例如，文件存储可以支持多个节点读写，云硬盘只能被一个节点读写。
- ReadWriteOnce：存储卷可以被一个节点以读写方式挂载。
- ReadWriteMany：存储卷可以被多个节点以读写方式挂载。
表1存储卷支持的访问模式 
| 存储类型          | ReadWriteOnce | ReadWriteMany |
|:---|:---|:---|
| 云硬盘EVS        | √             | ×            |
| 文件存储SFS         | ×             | √             |
| 对象存储OBS       | ×             | √            |
| 极速文件存储SFS Turbo | ×               | √             |
| 本地持久卷LocalPV   | √                 | ×                |
| 专属存储DSS       | √            | ×              |
   
#### 存储卷挂载方式
通常在使用存储卷时，可以通过以下方式挂载：
表2挂载存储卷的方式 
| 挂载方式                       | 说明                                                                                                                                                                                                                                                                                                                     | 支持的存储卷类型                  | 其他限制       |
|:---|:---|:---|:---|
| 静态创建存储卷（使用已有存储）         | 指基于已有的存储资源（例如云硬盘、文件存储等），手动创建PV和PVC。Kubernetes会根据PVC的资源需求，自动将其与匹配的PV绑定。绑定完成后，PVC可挂载至工作负载，使工作负载获得访问存储资源的能力，进而实现业务数据的持久化存储。 若存储卷的访问模式为**ReadWriteOnce** ，则创建工作负载时，工作负载的**Pod实例数量只能设置为1**。                                                                                                      | 所有存储卷均支持                   | 无         |
| 动态创建存储卷（自动创建存储）           | 指在PVC中指定[存储类（StorageClass）]，由存储Provisioner根据PVC中声明的资源需求动态创建底层存储资源和对应的PV，并自动完成该PV与PVC的绑定，从而实现存储资源的自动化供给。 若存储卷的访问模式为**ReadWriteOnce** ，则创建工作负载时，工作负载的**Pod实例数量只能设置为1**。                                                                               | 云硬盘存储、对象存储、文件存储、本地持久卷、专属存储 | 无         |
| 动态挂载（VolumeClaimTemplate） | 动态挂载能力通过卷申领模板（[volumeClaimTemplates](https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/statefulset/#volume-claim-templates)字段）实现，并依赖于StorageClass的动态创建PV能力。动态挂载可以为每一个Pod关联一个独有的PVC及PV，当Pod被重新调度后，仍然能够根据该PVC名称挂载原有的数据。 即使存储卷的访问模式为**ReadWriteOnce**，工作负载的Pod实例数量也可以设置为多个。 | 仅云硬盘存储、本地持久卷、专属存储支持          | 仅有状态工作负载支持 |
   
 #### PV回收策略
PV回收策略用于指定删除PVC时，底层卷的回收策略，支持设定Delete、Retain回收策略。
- Delete：删除PVC的动作会将PV对象从Kubernetes中移除，同时也会从外部基础设施中移除所关联的底层存储资产。
  ![](https://support.huaweicloud.com/usermanual-cce/public_sys-resources/note_3.0-zh-cn.png)
  包周期云硬盘资源支持通过Delete回收策略进行级联删除，而其他类型的包周期资源则无法通过Delete回收策略实现级联删除。
  
- Retain：当PVC对象被删除时，PV对象与底层存储资源均不会被删除，需要手动删除回收。PVC删除后PV资源状态为"已释放（Released）"，且不能直接再次被PVC绑定使用。 您可以通过以下步骤来手动删除回收：
  1. 手动删除PersistentVolume对象。
  
  2. 根据情况，手动清除所关联的底层存储资源上的数据。
  
  3. 手动删除所关联的底层存储资源。
  
  
  若希望继续使用该底层存储资源，需先删除PV，再新建PV、PVC和底层存储关联。
  
CCE还支持一种删除PVC时不删除底层存储资源的使用方法，当前仅支持使用YAML创建：PV回收策略设置为Delete，并添加annotations"everest.io/reclaim-policy: retain-volume-only"。这样在删除PVC时，PV会被删除，但底层存储资源会保留。
以云硬盘为例，YAML示例如下：
```
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test
  namespace: default
  annotations:
    volume.beta.kubernetes.io/storage-provisioner: everest-csi-provisioner
    everest.io/disk-volume-type: SAS
  labels:
    failure-domain.beta.kubernetes.io/region: <your_region>   # 替换为您待部署应用的节点所在的区域
    failure-domain.beta.kubernetes.io/zone: <your_zone>       # 替换为您待部署应用的节点所在的可用区
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: csi-disk
  volumeName: pv-evs-test
---
apiVersion: v1
kind: PersistentVolume
metadata:
  annotations:
    pv.kubernetes.io/provisioned-by: everest-csi-provisioner
    everest.io/reclaim-policy: retain-volume-only
  name: pv-evs-test
  labels:
    failure-domain.beta.kubernetes.io/region: <your_region>   # 替换为您待部署应用的节点所在的区域
    failure-domain.beta.kubernetes.io/zone: <your_zone>       # 替换为您待部署应用的节点所在的可用区
spec:
  accessModes:
    - ReadWriteOnce
  capacity:
    storage: 10Gi
  csi:
    driver: disk.csi.everest.io
    fsType: ext4
    volumeHandle: 2af98016-6082-4ad6-bedc-1a9c673aef20   #云硬盘ID
    volumeAttributes:
      storage.kubernetes.io/csiProvisionerIdentity: everest-csi-provisioner
      everest.io/disk-mode: SCSI
      everest.io/disk-volume-type: SAS
  persistentVolumeReclaimPolicy: Delete
  storageClassName: csi-disk
```
 #### 存储类（StorageClass）
StorageClass是Kubernetes中用于定义存储类别的资源对象，提供动态分配存储卷的能力。通过设置不同的参数，StorageClass能够根据业务需求自动供应和调整存储资源。在PVC中指定StorageClassName字段后，若在集群中未找到匹配的PV，Kubernetes将根据所引用的StorageClass调用对应的Provisioner动态创建符合要求的PV及底层存储资源，从而简化了手动创建和维护PV的流程。StorageClass中的参数作为默认配置项使用，**当PVC中存在与之冲突的配置时，以PVC中的设置为准**。
CCE集群中默认提供以下StorageClass供您使用，默认StorageClass不支持修改。如果默认的StorageClass无法满足业务需求，您可以自定义创建新的StorageClass，设置回收策略、绑定模式等参数，具体请参见[自定义存储类（StorageClass）](https://support.huaweicloud.com/usermanual-cce/cce_10_0380.html)。
csi-local-topology和csi-disk-topology分别较csi-local和csi-disk支持存储卷延时创建，即存储卷关联pod被调度到指定节点后，存储卷才会被创建，避免出现存储卷供应与其他pod调度约束冲突，详见[Kubernetes 拓扑感知卷配置](https://kubernetes.io/zh-cn/blog/2018/10/11/topology-aware-volume-provisioning-in-kubernetes/)。因此优先推荐您使用支持延时绑定的存储类。
 表3默认StorageClass 
| StorageClass名称      | 对应存储类型                 | Provisioner                | 回收策略   | 绑定模式                 | 是否支持扩容 |
|:---|:---|:---|:---|:---|:---|
| csi-disk              | 云硬盘                    | everest-csi-provisioner | Delete | Immediate             | 支持      |
| csi-disk-topology    | 延迟创建的云硬盘              | everest-csi-provisioner   | Delete  | WaitForFirstConsumer | 支持     |
| csi-nas            | SFS容量型文件存储           | everest-csi-provisioner  | Delete  | Immediate            | 支持    |
| csi-sfs            | 通用文件系统（原SFS 3.0 容量型） | everest-csi-provisioner   | Delete  | Immediate            | 不支持   |
| csi-sfsturbo       | 极速文件存储              | everest-csi-provisioner | Delete | Immediate           | 支持   |
| csi-obs             | 对象存储                 | everest-csi-provisioner | Delete  | Immediate            | 不支持    |
| csi-disk-dss       | 专属存储                   | everest-csi-provisioner   | Delete   | Immediate            | 支持     |
| csi-local           | 本地持久卷                | everest-csi-provisioner  | Delete | Immediate             | 不支持    |
| csi-local-topology | 延时创建的本地持久卷          | everest-csi-provisioner | Delete | WaitForFirstConsumer | 支持    |
   
[表3]中参数介绍如下：
- **对应存储类型**：表示该存储类创建的底层存储类型。
- **Provisioner**：表示存储资源提供商。
- **回收策略** ：用于指定删除PVC时底层存储的回收策略，详情请参见[PV回收策略]。
- **绑定模式** ：表示卷绑定模式，即动态创建PV的时间，分为立即创建（Immediate）和延迟创建（WaitForFirstConsumer）。
  - **Immediate**：PVC创建后，会立即创建底层存储资源及存储卷PV，并与PVC绑定。
  
  - **WaitForFirstConsumer**：PVC创建后，不会立即与存储卷PV绑定，而是等需要挂载该PVC的Pod被调度后，再创建底层存储资源及存储卷PV，并与PVC绑定。
   
- **是否支持扩容**：表示由该存储类创建的PV是否支持动态扩容。
 
如果需要了解默认StorageClass的更多信息，请[通过kubectl连接集群](https://support.huaweicloud.com/usermanual-cce/cce_10_0107.html)，并执行**kubectl describe sc \<storageclass-name\>**命令查询详细信息。
#### 相关文档
- 更多关于Kubernetes存储的信息，请参见[Storage](https://kubernetes.io/zh-cn/docs/concepts/storage/dynamic-provisioning/)。
- 更多关于CCE容器存储的信息，请参见[存储概述](https://support.huaweicloud.com/usermanual-cce/cce_10_0307.html)。
 
