# PVC-持久卷声明

PVC的出现，可以使得创建Pod时，不用再去管底层的存储类型，只需要声明该Pod需要的存储空间大小即可，即将Pod与底层存储技术解耦。

PVC（持久卷声明）和PV（持久卷）是成对出现的，PV与底层存储技术密切相关。Pod通过挂载PVC资源，令PVC申请满足条件的PV作为Pod的存储卷。

{% hint style="warning" %}
**Tips:** 若使用**StorageClass**自动创建PV的话，则无需显示的创建PV了。
{% endhint %}

PV资源**不属于**任何一个命名空间，它是集群层面的资源。

PVC和PV使用时如下：

![Pod中的容器挂载PVC，从而间接挂载PV](https://2906552408-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M6Ub8CloS5kJszh6xSR%2Fsync%2Ff215e98d50ef714f5fab6c437403fb470ceed49c.png?generation=1588594617618071\&alt=media)

{% hint style="info" %}
**Tips:**&#x20;

1. PVC和PV都是需要单独创建的资源对象；
2. Pod在`spec.volumes.persistentVolumeClaim.claimName`中指定已经创建好的PVC的名字即可；
   {% endhint %}

## 使用PVC

Pod使用PVC申请PV，总共需要以下两步：

1. 集群管理员根据底层存储技术，创建PV资源；
2. 集群使用者/管理员，根据所需存储卷的读写权限、大小等创建PVC资源；
3. 集群使用者在Pod中挂载PVC资源，间接挂载PV卷；

### 创建PV

PV与底层的存储技术是息息相关的，因此在创建PV资源时，需要在`spec`中确定底层的存储技术。

PV资源不属于任何一个命名空间，是集群层面的资源。

在`spec.accessModes`中可以指定PV卷的访问模式，共有以下三种：

* ReadWriteOnce（RWO）：PV能以**read-write**模式挂载到**单个Node**；
* ReadWriteMany（RWX）：PV能以**read-write**模式挂载到**多个Node**；
* ReadOnlyMany（ROX）：PV能以**read-only**模式挂载到**多个Node**；

在`spec.persistentVolumeReclaimPolicy`中可以指定PV卷被PVC资源释放时的回收操作，即当PVC释放PV时，该PV卷的操作：

* Retain：删除PVC时，PV和底层数据都不会被删除，需要管理员手工回收；当删除PVC后，PV仅呈现为Released状态，尝试使用PVC重新绑定也不行，需要重新部署PV才行，且数据不会被清除；（因为PV中可能包含前一个Pod留下的数据，为了安全，所以暂时不能被使用）
* Recycle：删除PVC时，PV不会被删除，但底层数据被删除，效果相当于执行rm -rf /thevolume/\*；k8s会启动一个容器负责清除数据的操作。通过这种方式，PV可以被不同的PVC绑定，和不同的Pod反复使用。
* Delete：删除PVC时，PV被删除，但底层数据不被删除；

例如，在下面这个例子中，创建一个名为`mypv1`的nfs类型的PV，其存储容量为`1Gi`，其访问模式有`被单个Node挂载为读写模式`和`被多个Node挂载为只读模式`两种，并且该PV的回收模式为`Recycle`，其所属的StorageClass为`nfs`（[StorageClass](https://yangsijie151104.gitbook.io/k8s-note/juan/pvc/storageclass-zi-dong-chuang-jian-juan)是被用来动态创建PV的）：

```yaml
apiVersion: v1
kind: PersistentVolume    # 资源类型为PV
metadata:
  name: mypv1
spec:    # PV的规格
  capacity:    # PV的容量
    storage: 1Gi
  accessModes:    # 访问模式(下面两个合起来的意思是：可以被单个Node挂载为读写模式或者被多个Node挂载为只读模式)
  - ReadWriteOnce
  - ReadOnlyMany
  persistentVolumeReclaimPolicy: Recycle    # PV的回收策略，一般有Retain、Recycle和Delete三种
  storageClassName: nfs    # PV所属的storageClass的名字(这里根据是否需要定义在动态供给中会具体解释)，注意这里即使写了，如果nfs这个SC不存在，也不会创建nfs这个SC
  nfs:    # 创建nfs类型的持久卷
    path: /nfsdata/pv1
    server: 192.168.56.105
```

{% hint style="info" %}
**Tips:** 若`spec.storageClassName`不指定的话，则该PV资源对象不属于任何一个StorageClass资源。
{% endhint %}

### 创建PVC

PVC资源需要先创建，然后才能被挂载至Pod中。

在创建PVC时，需要指定申请的PV卷的容量大小、访问模式等，相当于筛选条件，进而从StorageClass中申请PV资源。

{% hint style="info" %}
**Tips:** 若StorageClass中没有手动创建的PV卷资源，则会依靠StorageClass资源动态创建PV卷，这个在对应章节中另外仔细介绍。
{% endhint %}

下面介绍一个创建PVC资源的例子，

```yaml
apiVersion: v1
kind: PersistentVolumeClaim    # 资源类型为PVC
metadata:
  name: mongodb-pvc    # PVC资源名字，之后需要在Pod中的spec.volumes.persistentVolumeClaim.claimName中指定，然后在spec.containers.volumeMounts中挂载
spec:
  resources:    # 申请的资源规格，包括卷大小等
    requests:
      storage: 1Gi
  accessModes:    # 申请的资源的访问模式
  - ReadWriteOnce
  storageClassName: nfs    # 申请的卷所属的StorageClass名
```

{% hint style="info" %}
**Tips:**&#x20;

1. 若`storageClassName=""`，则不会从任何的StorageClass中申请PV资源，因此若集群在任何StorageClass外都没有PV资源的话（即不指定PV资源的`spec.storageClassName`），则这里会申请失败；
2. 若没有指定`storageClassName`，则会从集群的默认的StorageClass中申请PV资源；
3. 若名为`nfs`的StorageClass中没有符合的预创建的PV卷，则该StorageClass资源会动态创建PV卷；
   {% endhint %}

### 在Pod中挂载PVC

在Pod中挂载PVC，同样需要两步：

1. 定义卷：此处定义的是PVC资源，即在`spec.volumes.persistentVolumeClaim`中指定`claimName`为预先创建好的PVC资源的名字，即与PVC相关联；
2. 挂载卷：在Pod的`spec.container.volumeMounts`中指定挂载点和挂载卷名；

下面举一个例子，创建一个名为`mongodb`的Pod，其在内部的名为`mongodb`的容器的`/data/db`下挂载一个名为`mongodb-data`的PVC卷，该卷与名为`mongodb-pvc`的PVC资源关联：

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: mongodb 
spec:
  containers:
  - image: mongo
    name: mongodb
    volumeMounts:    # 定义挂载点
    - name: mongodb-data    # （必须）需要挂载的卷名
      mountPath: /data/db    # （必须）挂载至容器的文件系统中的路径
    ports:
    - containerPort: 27017
      protocol: TCP
  volumes:    # 定义卷
  - name: mongodb-data    # （必须）卷名
    persistentVolumeClaim:    # 定义一个PVC卷
      claimName: mongodb-pvc    # 与预先创建好的PVC资源名一致
```
