上节课我们学习了 PV 的使用,但是在我们真正使用的时候是使用的 PVC,就类似于我们的服务是通过 Pod 来运行的,而不是 Node,只是 Pod 跑在 Node 上而已,所以这节课我们就来给大家讲解下 PVC 的使用方法。

准备工作

在使用 PVC 之前,我们还得把其他节点上的 nfs 客户端给安装上,比如我们这里:

$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready master 61d v1.10.0
node01 Ready <none> 61d v1.10.0
node03 Ready <none> 41d v1.10.0

我们需要在所有节点安装 nfs 客户端程序,安装方法和上节课的安装方法一样的。必须在所有节点都安装 nfs 客户端,否则可能会导致 PV 挂载不上的问题

新建 PVC

同样的,我们来新建一个数据卷声明,我们来请求 1Gi 的存储容量,访问模式也是 ReadWriteOnce,YAML 文件如下:(pvc-nfs.yaml)

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc-nfs
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi

我们可以看到我们这里的声明方法几乎和新建 PV 是一样的,在新建 PVC 之前,我们可以看下之前创建的 PV 的状态:

kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-nfs 1Gi RWO Recycle Available 19m

我们可以看到当前 pv-nfs 是在 Available 的一个状态,所以这个时候我们的 PVC 可以和这个 PV 进行绑定:

$ kubectl create -f pvc-nfs.yaml
persistentvolumeclaim "pvc-nfs" created
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-nfs Bound pv-nfs 1Gi RWO 12s

我们可以看到 pvc-nfs 创建成功了,状态是 Bound 状态了,这个时候我们再看下 PV 的状态呢:

$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-nfs 1Gi RWO Recycle Bound default/pvc-nfs 23m

同样我们可以看到 PV 也是 Bound 状态了,对应的声明是 default/pvc-nfs,就是 default 命名空间下面的 pvc-nfs,证明我们刚刚新建的 pvc-nfs 和我们的 pv-nfs 绑定成功了。

有的同学可能会觉得很奇怪,我们并没有在 pvc-nfs 中指定关于 pv 的什么标志,它们之间是怎么就关联起来了的呢?其实这是系统自动帮我们去匹配的,他会根据我们的声明要求去查找处于 Available 状态的 PV,如果没有找到的话那么我们的 PVC 就会一直处于 Pending 状态,找到了的话当然就会把当前的 PVC 和目标 PV 进行绑定,这个时候状态就会变成 Bound 状态了。比如我们新建一个 PVC,如下:(pvc2-nfs.yaml)

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: pvc2-nfs
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
selector:
matchLabels:
app: nfs

我们这里声明一个 PV 资源的请求,邀请访问模式是 ReadWriteOnce,存储容量是 2Gi,最后我们还要求匹配具有标签 app=nfs 的 PV,这样要求的 PV 有吗?我们先查看下当前系统的所有 PV:

$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-nfs 1Gi RWO Recycle Bound default/pvc-nfs 43m
pv001 1Gi RWO Recycle Bound default/www-web-0 13d
pv002 1Gi RWO Recycle Bound default/www-web-1 13d

都是 Bound 状态,并没有 Available 状态的 PV,所以我们可以想象到我们上面新建的 PVC 是没办法选择到合适的 PV 的,我们创建一下看看:

$ kubectl create -f pvc2-nfs.yaml
persistentvolumeclaim "pvc2-nfs" created
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-nfs Bound pv-nfs 1Gi RWO 23m
pvc2-nfs Pending 14s

很显然是 Pending 状态,因为并没有合适的 PV 给你使用,现在我们来新建一个 PV,让上面的 PVC 有合适的 PV 使用:(pv2-nfs.yaml)

apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2-nfs
labels:
app: nfs
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Recycle
nfs:
server: 10.151.30.57
path: /data/k8s

我们这里新建一个名为 pv2-nfs 的 PV,具有标签 app=nfs,容量也是 2Gi,访问模式是 ReadWraiteOnce,看上去这一切都很适合上面的 PVC,新建试一试:

$ kubectl create -f pv2-nfs.yaml
persistentvolume "pv2-nfs" created
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv-nfs 1Gi RWO Recycle Bound default/pvc-nfs 51m
pv2-nfs 2Gi RWO Recycle Bound default/pvc2-nfs 12s

创建完 pv2-nfs 后,是不是很快就发现该 PV 是 Bound 状态了,对应的 PVC 是 default/pvc2-nfs,证明上面的 pvc2-nfs 终于找到合适的 PV 进行绑定上了:

$ kubectl get pvc
kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc-nfs Bound pv-nfs 1Gi RWO 30m
pvc2-nfs Bound pv2-nfs 2Gi RWO 7m

成功了,对吧!有的同学可能又会说了,我们的 pv2-nfs 声明的容量是 2Gi,如果我 pvc2-nfs 这里声明的容量是 1Gi 的话呢?还能正常绑定吗?如果可以正常绑定的话,那剩下的 1Gi 容量还能使用吗?其实我也不清楚,怎么办?我们去实际测试下就知道了吧,先删除上面的 pvc2-nfs,然后我们把该 PVC 里面的容量改成 1Gi,再新建试一试呢:

$ kubectl delete pvc pvc2-nfs
persistentvolumeclaim "pvc2-nfs" deleted
$ cat pvc2-nfs.yaml
...
resources:
requests:
storage: 1Gi
...
$ kubectl create -f pvc2-nfs.yaml
persistentvolumeclaim "pvc2-nfs" created
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
pvc2-nfs Bound pv2-nfs 2Gi RWO 7s

我们可以看到上面的 PVC 依然可以正常的绑定,仔细看 CAPACITY 这一列的数据:2Gi,也就是说我们声明的 1Gi 是没什么用的,我 PV 是 2Gi,你这里声明 1Gi 是不行的,你必须得使用 2Gi。

如果我们这里容量声明是 3Gi 呢?还可以正常绑定吗?大家可以思考一下,如果声明的容量大于了 PV 里面的容量的话,是没办法进行绑定的,大家可以下去自己测试一下。

使用 PVC

上面我们已经知道怎么创建 PV 和 PVC 了,现在我们就来使用下我们的 PVC,这里我们同样使用之前的 nginx 的镜像来测试下:(nfs-pvc-deploy.yaml)

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nfs-pvc
spec:
replicas: 3
template:
metadata:
labels:
app: nfs-pvc
spec:
containers:
- name: nginx
image: nginx:1.7.9
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumes:
- name: www
persistentVolumeClaim:
claimName: pvc2-nfs --- apiVersion: v1
kind: Service
metadata:
name: nfs-pvc
labels:
app: nfs-pvc
spec:
type: NodePort
ports:
- port: 80
targetPort: web
selector:
app: nfs-pvc

我们这里使用 nginx 镜像,将容器的 /usr/share/nginx/html 目录通过 volume 挂载到名为 pvc2-nfs 的 PVC 上面,然后创建一个 NodePort 类型的 Service 来暴露服务:

$ kubectl create -f nfs-pvc-deploy.yaml
deployment.extensions "nfs-pvc" created
service "nfs-pvc" created
$ kubectl get pods
kubectl get pods
NAME READY STATUS RESTARTS AGE
...
nfs-pvc-57c9945bd9-5r4r6 1/1 Running 0 19s
nfs-pvc-57c9945bd9-gz6p9 1/1 Running 0 19s
nfs-pvc-57c9945bd9-x6mvc 1/1 Running 0 19s
...
$ kubectl get svc
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
...
nfs-pvc NodePort 10.98.246.155 <none> 80:30769/TCP 1m
...

然后我们就可以通过任意节点的 IP:30769 端口来访问我们这里的 Nginx 服务了,但是这个时候我们来访问会出现403,这是为什么?我们再去看看 nfs 共享数据目录下面有没有数据呢?

nginx 403

$ ls /data/k8s

我们发现并没有任何数据,这是因为我们把容器目录/user/share/nginx/html和挂载到了pvc2-nfs这个 PVC 上面,这个 PVC 就是对应着我们上面的 nfs 的共享数据目录的,该目录下面还没有任何数据,所以我们访问就出现了403,现在我们在/data/k8s这个目录下面新建一个 index.html 的文件:

$ echo "<h1>Hello Kubernetes~</h1>" >> /data/k8s/index.html
$ ls /data/k8s/
index.html

我们可以看到共享数据目录中已经有一个 index.html 的文件了,由于我们挂载了 pvc2-nfs 到上面的 nginx 容器中去,是不是这个时候容器目录/user/share/nginx/html下面也有index.html这个文件了啊?所以这个时候我们再来访问下服务,任一节点IP:30769:

nginx 200

现在是不是正常了啊,但是我们可以看到我们容器中的数据是直接放到共享数据目录根目录下面的,如果以后我们又有一个新的 nginx 容器也做了数据目录的挂载,是不是就会有冲突了啊,所以这个时候就不太好区分了,这个时候我们可以在 Pod 中使用一个新的属性:subPath,该属性可以来解决这个问题,我们只需要更改上面的 Pod 的 YAML 文件即可:

...
volumeMounts:
- name: www
subPath: nginxpvc-test
mountPath: /usr/share/nginx/html
...

更改完 YAML 文件后,我们重新更新即可:

$ kubectl apply -f nfs-pvc-deploy.yaml
Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
deployment.extensions "nfs-pvc" configured
Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
service "nfs-pvc" configured

更新完后,我们再去看看 nfs 的数据共享目录:

$ ls /data/k8s/
index.html nginxpvc-test
$ ls /data/k8s/nginxpvc-test/

我们可以预想到现在我们访问上面的服务,是不是又会得到403的结果啊,因为nginxpvc-test目录下面还没有任何文件呢,我们把根目录下面的 index.html 文件移动到 nginxpvc-test 目录下面去是不是又可以访问了:

$ mv /data/k8s/index.html /data/k8s/nginxpvc-test/

现在快去验证下吧,看看能不能得到正确结果。

到这里我们就算完整的使用了一次 PVC 了,现在我们再来验证下我们的数据是否会丢失,怎么验证?首先我们把上面的 Deployment 删除掉,这样是不是他下面管理的3个 Pod 也会被一起删除掉啊:

$ kubectl delete deployment nfs-pvc
deployment.extensions "nfs-pvc" deleted

Deployment 被删除掉了,但是 nfs 的数据共享目录下面的数据呢?

$ ls /data/k8s/nginxpvc-test/
index.html

还在吧?当然了如果不在了,我们用他就没有任何意义了吧,现在我们再来重新创建上面的 Deployment,看看访问服务还能得到上面的正常输出结果吗:

$ kubectl create -f nfs-pvc-deploy.yaml
deployment.extensions "nfs-pvc" created
Error from server (AlreadyExists): error when creating "nfs-pvc-deploy.yaml": services "nfs-pvc" already exists

可以看到 nfs-pvc 这个 Deployment 创建成功了,由于 Service 我们之前没有删除掉,所以这里提示已经存在,我们忽略就可以了,现在同样我们用任一节点 IP:30769 来访问我们这里的服务,是不是依然可以在页面上看到Hello Kubernetes~这里的输出信息啊,这证明我们的数据持久化是成功的吧!

注意事项

上面我们演示了数据持久化,如果这个时候我们把 PV 给删除了,上面持久化的数据还会存在吗?如果是删除的 PVC 呢?在实际使用的工程中,是很有可能出现这种情况的吧?下面我们来实际验证下。

我们先删除上面使用的 PV:

$ kubectl delete pv pv2-nfs
persistentvolume "pv2-nfs" deleted

然后再看看之前创建的 PVC 还会存在吗:

$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
...
pvc2-nfs Bound pv2-nfs 2Gi RWO 1h
...

是不是觉得很奇怪,pvc2-nfs 仍然是 Bound 的状态,也就意外着我们还可以正常使用这个 PVC,但是如果我们有一个新的 Pod 来使用这个 PVC 会是怎样的情况呢?大家下去自己验证下

如有 Pod 正在使用此 pvc2-nfs 这个 PVC 的话,那么新建的 Pod 则仍可使用,如无 Pod 使用,则创建 Pod 挂载此 PVC 时会出现失败。大家自己去验证下吧

现在我们在恢复到最开始的状态,把 PV 和 PVC 添加回来,如果现在我们把使用 pvc2-nfs 关联的 Pod 都删除,然后再删除该 PVC 的话,那么我们的持久化数据还存在吗?

$ kubectl delete -f nfs-pvc-deploy.yaml
deployment.extensions "nfs-pvc" deleted
service "nfs-pvc" deleted
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
...
pvc2-nfs Bound pv2-nfs 2Gi RWO 5m
...
$ kubectl delete pvc pvc2-nfs
persistentvolumeclaim "pvc2-nfs" deleted
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
...
pv2-nfs 2Gi RWO Recycle Released default/pvc2-nfs 6m
...
$ ls /data/k8s/

我们可以看到 pv2-nfs 这个 PV 的状态已经变成了 Released 状态了,这个状态是不是表示 PVC 已经被释放了,现在可以被重新绑定了,由于我们设置的 PV 的回收策略是 Recycle,所以我们可以很明显的发现 nfs 的共享数据目录下面已经没有了数据了,这是因为我们把 PVC 给删除掉了,然后回收了数据。

不过大家要注意,并不是所有的存储后端的表现结果都是这样的,我们这里使用的是 nfs,其他存储后端肯能会有不一样的结果。

大家在使用 PV 和 PVC 的时候一定要注意这些细节,不然一不小心就把数据搞丢了。

k8s-PV和PVC使用的更多相关文章

  1. 【k8s】k8s pv、pvc无法删除问题。

    一般删除步骤为:先删除pod再删除pvc最后删除pv 遇到的问题 但是遇到pv使用处于"Terminating"状态,而且删不掉.如下图: 解决办法 直接删除k8s中的记录: ku ...

  2. K8s PV and PVC and StorageClass

    PVC和PV之间没有依靠ID.名称或者label匹配,而是靠容量和访问模式,PVC的容量和访问模式需要是某个PV的子集才能自动匹配上.注意:PVC和PV是一对一的,也即一个PV被一个PVC自动匹配后, ...

  3. K8S 使用NFS 创建PV和PVC的例子 学习From https://blog.csdn.net/xts_huangxin/article/details/51494472

    1. 获取资料 网址: https://blog.csdn.net/xts_huangxin/article/details/51494472  感谢原作者 这里面 按照自己的机器情况进行了学习模仿 ...

  4. k8s的pv和pvc简述

    pvc:资源需要指定:1.accessMode:访问模型:对象列表:    ReadWriteOnce – the volume can be mounted as read-write by a s ...

  5. 容器编排系统K8s之PV、PVC、SC资源

    前文我们聊到了k8s中给Pod添加存储卷相关话题,回顾请参考:https://www.cnblogs.com/qiuhom-1874/p/14180752.html:今天我们来聊一下持久存储卷相关话题 ...

  6. 4.k8s存储之Volume、PV、PVC和StatefulSet

    3.Volume 容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题.首先,当容器崩溃时,kubelet 会重启它,但是容器中的文件将丢失--容器以干净的状态(镜像最初的 ...

  7. k8s之PV、PVC、StorageClass详解

    导读 上一篇写了共享存储的概述以及一个简单的案例演示.这一篇就写一下PV和PVC. PV是对底层网络共享存储的抽象,将共享存储定义为一种"资源",比如Node也是容器应用可以消费的 ...

  8. 7.2 k8s 基于PV、PVC搭建zookeeper 3节点集群

    1.PV,PVC介绍 1.1.StorageClass & PV & PVC关系图 Volumes 是最基础的存储抽象,其支持多种类型,包括本地存储.NFS.FC以及众多的云存储,我们 ...

  9. k8s之PV、PVC

    目录 一.PVC和PV 1.1 PV概念 1.2 PVC概念 1.3 PV与PVC之间的关系 1.4 两种PV的提供方式 二.基于nfs创建静态PV资源和PVC资源 2.1 配置nfs存储(192.1 ...

  10. k8s数据持久化,pv和pvc

    k8s中为什么使用存储: k8s中的副本控制器保证了pod的始终存储,却保证不了pod中的数据.只有启动一个新pod的,之前pod中的数据会随着容器的删掉而丢失! pv和pvc的概念: Persist ...

随机推荐

  1. POJ 1051 Jury Compromise ——(暴力DP)

    题目不难,暴力地dp一下就好,但是不知道我WA在哪里了,对拍了好多的数据都没找出错误= =.估计又是哪里小细节写错了QAQ..思路是用dp[i][j]表示已经选了i个,差值为j的最大和.转移的话暴力枚 ...

  2. Maven:禁止编码指定类型的资源文件

    [参考文章]:项目编译后dll文件调用出错 maven 在编译或项目时,可能会对资源文件进二次编码(编译前后的文件大小对比即可发现该问题),有些文件(例如:文本文件)可能不会影响我们是用,但是有些文件 ...

  3. udp如何实现可靠性传输?

    版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[+]   1udp与tcp的区别 TCP(TransmissionControl Protocol 传输控制协议)是一种面向连接的 ...

  4. Sql中truncate,delete以及drop的比较

    相同点: 1.truncate和不带where子句的delete.以及drop都会删除表内的数据. 2.drop.truncate都是DDL语句(数据定义语言),执行后会自动提交. 不同点: 1. t ...

  5. LC 986. Interval List Intersections

    Given two lists of closed intervals, each list of intervals is pairwise disjoint and in sorted order ...

  6. as 什么意思?

    You can denote particular console messages and variable values as having different types using four ...

  7. 001-多线程-JUC线程池-线程池架构-Executor、ExecutorService、ThreadPoolExecutor、Executors

    一.概述 1.1.线程池架构图 1. Executor 它是"执行者"接口,它是来执行任务的.准确的说,Executor提供了execute()接口来执行已提交的 Runnable ...

  8. C++ STL copy copy_backward

    #include <iostream>#include <algorithm>#include <vector>#include <functional> ...

  9. thinkphp5 更改入口文件在主目录

    默认thinkPHP入口文件在public/index.php,而在虚拟主机部署时,不能设置访问路径,因此需要将入口文件放置在主目录上. 一.主目录下新建index.php 复制以下内容 // 定义应 ...

  10. js取url问号后的参数方法封装

    工具方法: function getRequest() { var url = location.search; // 获取url中?后面的字符串 var theRequest = new Objec ...