一、介绍
好的!我用一个简单的比喻来解释 Kubernetes 中的 **PV(Persistent Volume)** 和 **PVC(Persistent Volume Claim)**,帮你轻松理解它们的作用和关系。
---
### ️ **打个比方:PV 和 PVC 就像“仓库”和“租客”**
1. **PV(Persistent Volume)**:好比是 **一个真实的仓库**
- 它是集群中的一块**实际存储空间**(比如云硬盘、NFS、本地磁盘等)。
- 就像仓库有大小、位置、类型(冷库/常温库)一样,PV 也有容量、访问模式(读写权限)、存储类型等属性。
- *例子:* 你有一个 100GB 的云硬盘,这就是一个 PV。
2. **PVC(Persistent Volume Claim)**:好比是 **租户的申请单**
- 它是用户(或 Pod)**对存储需求的声明**,比如“我需要 10GB 的存储,能读写”。
- Kubernetes 会根据 PVC 的要求,**自动找到匹配的 PV** 并绑定(就像仓库管理员分配符合条件的仓库给租客)。
- *例子:* 你的应用需要 10GB 空间,提交一个 PVC,K8s 就会从现有的 PV 中分配一个合适的给它。
---
### **核心特点**
- **PV 是资源,PVC 是请求**:
PV 像硬盘,是实物;PVC 像申请单,是需求。
- **动态分配**:
如果现有 PV 不满足 PVC,K8s 可以动态创建新的 PV(需要配置 `StorageClass`,类似“仓库供应商”)。
- **解耦设计**:
用户只需关心 PVC(要多大存储),无需手动管理 PV(不用管硬盘具体在哪)。
---
### **举个实际流程**
1. **管理员创建 PV**:
```yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv
spec:
capacity: { storage: 100Gi }
accessModes: [ ReadWriteOnce ]
storageClassName: ssd
```
(定义了一个 100GB 的 SSD 存储卷)
2. **用户提交 PVC**:
```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc
spec:
resources:
requests: { storage: 10Gi }
storageClassName: ssd
```
(申请 10GB 的 SSD 存储)
3. **K8s 自动绑定**:
系统将 `my-pvc` 和 `my-pv` 绑定,Pod 通过 PVC 使用存储。
---
### **总结**
- **PV**:实际的存储资源(像硬盘)。
- **PVC**:用户对存储的需求(像申请单)。
- **好处**:**用户不用关心存储细节**,K8s 自动匹配和管理,就像租仓库只需提交需求,不用自己盖房子。
这样是不是清楚多了?
二、实战应用
[root@master nfs-pv]# exportfs -arv
exporting *:/root/nfs-pv/v10
exporting *:/root/nfs-pv/v9
exporting *:/root/nfs-pv/v8
exporting *:/root/nfs-pv/v7
exporting *:/root/nfs-pv/v6
exporting *:/root/nfs-pv/v5
exporting *:/root/nfs-pv/v4
exporting *:/root/nfs-pv/v3
exporting *:/root/nfs-pv/v2
exporting *:/root/nfs-pv/v1
[root@master nfs-pv]# pwd
/root/nfs-pv
[root@master nfs-pv]# ls
v1 v10 v2 v3 v4 v5 v6 v7 v8 v9
[root@master nfs-pv]# kubectl apply -f pv.yaml
persistentvolume/v1 unchanged
persistentvolume/v2 unchanged
persistentvolume/v3 created
[root@master nfs-pv]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
v1 1Gi RWX Retain Available
v2 1Gi RWO Retain Available
v3 1Gi ROX Retain Available
[root@master nfs-pv]# cat pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: v1
labels:
app: v1
spec:
nfs:
server: 192.168.40.180
path: /root/nfs-pv/v1
accessModes:
- ReadWriteMany
capacity:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v2
labels:
app: v2
spec:
nfs:
server: 192.168.40.180
path: /root/nfs-pv/v2
accessModes:
- ReadWriteOnce
capacity:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v3
labels:
app: v3
spec:
nfs:
server: 192.168.40.180
path: /root/nfs-pv/v3
accessModes:
- ReadOnlyMany
capacity:
storage: 1Gi
[root@master nfs-pv]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
pvc-v1 Bound v1 1Gi RWX
pvc-v2 Bound v2 2Gi RWO
pvc-v3 Bound v3 3Gi ROX
[root@master nfs-pv]# cat pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-v1
spec:
accessModes:
- ReadWriteMany
selector:
matchLabels:
app: v1
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-v2
spec:
selector:
matchLabels:
app: v2
resources:
requests:
storage: 2Gi
accessModes:
- ReadWriteOnce
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-v3
spec:
selector:
matchLabels:
app: v3
resources:
requests:
storage: 3Gi
accessModes:
- ReadOnlyMany
[root@master nfs-pv]# kubectl apply -f pod-pvc.yaml
deployment.apps/nginx created
[root@master nfs-pv]# cat pod-pvc.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx-3
template:
metadata:
name: nginx-3
labels:
app: nginx-3
spec:
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: pvc-v1
mountPath: /usr/share/nginx/html
volumes:
- name: pvc-v1
persistentVolumeClaim:
claimName: pvc-v1
[root@master nfs-pv]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-54854d6776-6p4fq 1/1 Running 0 4s
nginx-54854d6776-kmsz8 1/1 Running 0 4s
nginx-54854d6776-rjp2g 1/1 Running 0 4s
[root@master nfs-pv]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-54854d6776-6p4fq 1/1 Running 0 28s 10.244.166.163 node1
nginx-54854d6776-kmsz8 1/1 Running 0 28s 10.244.104.21 node2
nginx-54854d6776-rjp2g 1/1 Running 0 28s 10.244.166.162 node1
[root@master nfs-pv]# kubectl exec -it nginx-54854d6776-6p4fq -- bash
root@nginx-54854d6776-6p4fq:/# cd /usr/share/nginx/html/
root@nginx-54854d6776-6p4fq:/usr/share/nginx/html# ls
root@nginx-54854d6776-6p4fq:/usr/share/nginx/html# touch cjr
root@nginx-54854d6776-6p4fq:/usr/share/nginx/html# exit
exit
[root@master nfs-pv]# kubectl exec -it nginx-54854d6776-kmsz8 -- bash
root@nginx-54854d6776-kmsz8:/# cd /usr/share/nginx/html/
root@nginx-54854d6776-kmsz8:/usr/share/nginx/html# ls
cjr
root@nginx-54854d6776-kmsz8:/usr/share/nginx/html# touch cjr2
root@nginx-54854d6776-kmsz8:/usr/share/nginx/html# ls
cjr cjr2
root@nginx-54854d6776-kmsz8:/usr/share/nginx/html# exit
exit
[root@master nfs-pv]# cd v1
[root@master v1]# ls
cjr cjr2
[root@master v1]# touch cjr3
[root@master v1]# cd ../
[root@master nfs-pv]# kubectl exec -it nginx-54854d6776-kmsz8 -- bash
root@nginx-54854d6776-kmsz8:/# cd /usr/share/nginx/html/
root@nginx-54854d6776-kmsz8:/usr/share/nginx/html# ls
cjr cjr2 cjr3
root@nginx-54854d6776-kmsz8:/usr/share/nginx/html# exit
exit
[root@master nfs-pv]# cat pod-pvc.yaml && cat pvc.yaml && cat pv.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx-3
template:
metadata:
name: nginx-3
labels:
app: nginx-3
spec:
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
volumeMounts:
- name: pvc-v1
mountPath: /usr/share/nginx/html
volumes:
- name: pvc-v1
persistentVolumeClaim:
claimName: pvc-v1
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-v1
spec:
accessModes:
- ReadWriteMany
selector:
matchLabels:
app: v1
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-v2
spec:
selector:
matchLabels:
app: v2
resources:
requests:
storage: 2Gi
accessModes:
- ReadWriteOnce
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-v3
spec:
selector:
matchLabels:
app: v3
resources:
requests:
storage: 3Gi
accessModes:
- ReadOnlyMany
apiVersion: v1
kind: PersistentVolume
metadata:
name: v1
labels:
app: v1
spec:
nfs:
server: 192.168.40.180
path: /root/nfs-pv/v1
accessModes:
- ReadWriteMany
capacity:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v2
labels:
app: v2
spec:
nfs:
server: 192.168.40.180
path: /root/nfs-pv/v2
accessModes:
- ReadWriteOnce
capacity:
storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: v3
labels:
app: v3
spec:
nfs:
server: 192.168.40.180
path: /root/nfs-pv/v3
accessModes:
- ReadOnlyMany
capacity:
storage: 3Gi
# 解释 Kubernetes NFS Persistent Volume 配置
这段代码演示了如何在 Kubernetes 中使用 NFS 作为持久化存储的配置。下面我将分部分解释:
## 1. NFS 服务器设置
首先,你在 NFS 服务器上设置了共享目录:
```bash
exportfs -arv
exporting *:/root/nfs-pv/v1
...
exporting *:/root/nfs-pv/v10
```
这表示你将 `/root/nfs-pv/` 下的 v1-v10 目录通过 NFS 共享出去,允许所有客户端(*)访问。
## 2. PersistentVolume (PV) 定义
`pv.yaml` 文件定义了三个 PersistentVolume 资源:
1. **v1**:
- 使用 NFS 存储,路径为 `/root/nfs-pv/v1`
- 访问模式为 ReadWriteMany (RWX) - 可被多个节点同时读写
- 容量为 1Gi
2. **v2**:
- 路径为 `/root/nfs-pv/v2`
- 访问模式为 ReadWriteOnce (RWO) - 只能被单个节点读写
- 容量为 2Gi
3. **v3**:
- 路径为 `/root/nfs-pv/v3`
- 访问模式为 ReadOnlyMany (ROX) - 可被多个节点同时只读
- 容量为 3Gi
## 3. PersistentVolumeClaim (PVC) 定义
`pvc.yaml` 文件定义了三个 PVC,它们会绑定到对应的 PV:
1. **pvc-v1**:
- 请求 1Gi 存储
- 需要 RWX 访问模式
- 通过标签选择器匹配 app=v1 的 PV
2. **pvc-v2**:
- 请求 2Gi 存储
- 需要 RWO 访问模式
- 匹配 app=v2 的 PV
3. **pvc-v3**:
- 请求 3Gi 存储
- 需要 ROX 访问模式
- 匹配 app=v3 的 PV
## 4. Deployment 使用 PVC
`pod-pvc.yaml` 定义了一个 Deployment,它创建了 3 个 Nginx 副本,所有副本都使用 pvc-v1:
- 每个 Pod 将 pvc-v1 挂载到 `/usr/share/nginx/html` 目录
- 由于 pvc-v1 使用 RWX 模式,所有 Pod 可以同时读写同一个 NFS 目录
## 5. 验证测试
你通过以下操作验证了存储功能:
1. 在一个 Pod 中创建文件 `cjr`,在其他 Pod 中可以看到
2. 在另一个 Pod 中创建文件 `cjr2`,其他 Pod 也能看到
3. 直接在 NFS 服务器上创建文件 `cjr3`,所有 Pod 也能立即看到
这证明了多个 Pod 可以共享同一个 NFS 存储,并且读写是同步的。
## 总结
这个配置展示了 Kubernetes 如何通过 PV/PVC 机制使用 NFS 作为共享存储:
- PV 定义了实际的存储资源
- PVC 是 Pod 对存储的请求
- Deployment 中的 Pod 通过 PVC 使用存储
- 使用 RWX 模式时,多个 Pod 可以共享同一个存储卷