15 K8S 存储 卷


将磁盘挂载到容器

1
2
1. 容器重新启动时,卷的内容将保持不变,新容器可以识别前一个容器写入卷的所有文件
2. 一个pod中有多个容器,这个卷可以被所有容器使用

emptyDir 卷

1
2
3
4
1. 创建一个空卷,挂载到Pod中的容器。
2. Pod删除该卷也会被删除,随着pod的生命周期 而存在
3. 应用场景:Pod中容器之间数据共享,一个pod中有多个容器,他们之间完成数据共享,不使用数据卷容器之间的文件系统是隔离的,只能看到自己的
4. 使用数据卷让容器之间某个目录达到共享
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
1. emptyDir 临时将数据写入磁盘

[root@k8s-master1 demo]# cat fortune-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: fortune-nodeport
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30123
selector:
app: fortune


[root@k8s-master1 demo]# cat fortune-pod.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: fortune
spec:
selector:
matchLabels:
app: fortune
template:
metadata:
labels:
app: fortune
spec:
containers:

- image: centos:7 # 容器1 写数据到 /var/htdocs/index.html
name: html-generator
command: ["bash","-c","for i in {1..100};do echo $i >> /var/htdocs/index.html;sleep 10;done"]
volumeMounts: # html卷挂载到/var/htdocs
- name: html
mountPath: /var/htdocs

- image: nginx:1.16 # 容器2 web服务
name: web-server
volumeMounts: # html卷挂载到/usr/share/nginx/html
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
ports:
- containerPort: 80
protocol: TCP
volumes: # 设定一个名字叫 html的emptyDir卷,挂载到上面的两个容器里
- name: html
emptyDir: {}
1
2
3
1. pod 包含两个容器和1个挂载到两个容器的共用卷,在不同的路径上
2. 当html-generator 容器启动时 每10秒循环一个数据输出到 /var/htdocs/index.html
3. web-server 是 nginx服务 他的静态目录为 /usr/share/nginx/html

hostPath 卷

1
2
3
4
5
# 1. hostPath卷指向节点文件系统上的目录
# 2. 在同一个节点上运行并且hostPath卷使用相同路径的POD 可以看到相同的文件
# 3. hostPath 卷 是持久性存储
# 4. emptyDir 卷 的内容会随着pod被删除时而删除
# 5. hostPath 用来访问节点上的数据,常用于单节点的持久化存储,不要用它来做跨pod的持久化数据
1
2
3
4
5
6
7
8
[root@k8s-master1 ~]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-6d8cfdd59d-w2xnx 1/1 Running 5 7d18h
kube-flannel-ds-amd64-q29r8 1/1 Running 10 9d
kube-flannel-ds-amd64-xh7c4 1/1 Running 9 9d
kube-flannel-ds-amd64-xq7gq 1/1 Running 10 9d

[root@k8s-master1 ~]# kubectl describe pod kube-flannel-ds-amd64-q29r8 -n kube-system

持久化存储

持久化存储的作用

1
2
1. 当运行在一个Pod中的应用程序需要将数据保存到磁盘上,并且该Pod重新调度到另外一个节点时,也要求具有相同的数据可用。
2. 数据需要可以从任何集群节点访问,因此必须将数据存储在某种类型的网络存储(NAS)中

使用NFS作为底层存储

1
1. 集群并未运行在云服务器上 而是自建环境,需要有自己的存储集群
1
2
3
4
5
# 选择 master2 作为服务端
# 关键:客户端,也就是node上也要安装
[root@k8s-node1 ~]# yum install -y nfs-utils
[root@k8s-node2 ~]# yum install -y nfs-utils
[root@k8s-master2 ~]# yum install -y nfs-utils
1
2
3
4
5
6
7
8
9
10
# 配置服务端的访问路径 启动服务端守护进程
# 得有这个目录才能挂载
[root@k8s-master2 ~]# mkdir -p /data/nfs

[root@k8s-master2 ~]# vim /etc/exports
/data/nfs *(rw,no_root_squash)

[root@k8s-master2 ~]# systemctl start nfs

[root@k8s-master2 ~]# ps -ef|grep nfs

创建 mongodb 数据库 pod 测试持久化存储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 配置启动

[root@k8s-master1 demo]# vim mongodb-pod.yaml

apiVersion: v1
kind: Pod
metadata:
name: mongodb
spec:
volumes: # 定义卷
- name: mongodb-data # 卷名 在挂载的时候需要用到
nfs: # 使用nfs共享作为卷的外部存储
server: 172.31.228.68 # nfs 服务器地址
path: /data/nfs # nfs 服务挂载路径
containers:
- image: mongo
name: mongodb
volumeMounts: # mongodb-data 卷挂载到 /data/db
- name: mongodb-data # 挂载卷的时候引用卷名
mountPath: /data/db # mongodb 默认的数据存放路径
ports:
- containerPort: 27017 # mongodb 服务端口
protocol: TCP

向 MongoDB pod 里面插入数据

1
2
3
4
5
6
7
8
9
10
11
12
[root@k8s-master1 demo]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mongodb 1/1 Running 0 73s 10.244.0.31 k8s-node1 <none> <none>

[root@k8s-master1 demo]# kubectl exec -it mongodb mongo

> use mystore
switched to db mystore
> db.doo.insert({name:'leo'})
WriteResult({ "nInserted" : 1 })
> db.doo.find()
{ "_id" : ObjectId("5e72e43d3bb458e3186a18c5"), "name" : "leo" }

测试持久化存储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 删除 Pod 
[root@k8s-master1 demo]# kubectl delete pod mongodb
pod "mongodb" deleted

# 重新创建到其他节点
# 如果一直在node1创建,使用节点选择器指定node上创建
[root@k8s-master1 demo]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mongodb 1/1 Running 0 83s 10.244.1.40 k8s-node2 <none> <none>

# 查看数据
[root@k8s-master1 demo]# kubectl exec -it mongodb mongo
> use mystore
switched to db mystore
> db.doo.find()
{ "_id" : ObjectId("5e72e43d3bb458e3186a18c5"), "name" : "leo" }


# 1. 删除了Pod并重建,数据仍然存在,后端使用持久化存储,多个Pod可以将数据写入持久化存储来保存数据
# 2. 清理该Pod,但是先不清理nfs中的数据,后面继续测试

其他存储技术

1
2
3
4
5
6
7
8
9
1. 参考: 官方文档
https://kubernetes.io/docs/concepts/storage/volumes/

2. Kubernetes中的Volume提供了在容器中挂载外部存储的能力
Pod需要设置卷来源(spec.volume)和挂载点(spec.containers.volumeMounts)两个信息后才可以使用相应的Volume

3.本地数据卷
hostPath 持久化 挂载node中的目录到pod中
emptyDir 非持久化 随着pod删除数据也一并删除

从底层存储技术 解耦 Pod

持久卷和持久卷声明

1
2
3
1. 开发人员需要一定数量的持久化存储 向K8S请求 类似创建Pod时请求CPU和内存 
2. 系统管理员配置存储服务 让 开发人员能使用
3. 之前我们一直使用Pod常规卷,下面学习 持久卷

持久卷

1
2
3
4
5
6
1. PersistenVolume(PV) 
2. 专业的存储人员来做底层存储,然后通过k8s注册持久卷
3. 创建持久卷的时候,可以指定大小和访问模式
4. 集群用户需要在Pod中使用使用持久卷(PV)时,先创建持久卷声明(PVC),指定需要的容量和访问方式
5. 持久卷声明 会发送给 k8s的api,k8s找到匹配的持久卷PV并绑定到PVC上
6. 持久卷声明 可以当做Pod的一个卷来使用,其他用户不能使用相同的持久卷,除非先通过删除PVC绑定来释放
1
2
3
4
5
6
7
持久卷由集群管理员创建,并被Pod通过持久卷生命消费
# PV 和 PVC 流程
1. 集群管理员创建某类型的网络存储(NFS或者Ceph)
2. 集群管理员向k8s api 发送请求 创建持久卷PV
3. 用户(使用者) 创建持久卷声明(PVC)
4. k8s找到一个匹配足够容量的PV并设置访问模式,最终将这个PVC绑定给这个PV
5. 用户创建一个Pod,并通过配置卷引用PVC

创建 PV 持久卷

1
2
3
4
1. PersistenVolume(PV):对存储资源创建和使用的抽象,使得存储作为集群中的资源管理。(专业的存储人员来做)
2. PV 可以是存储人员定义,他们会创建很多pv等待pvc来挂载
静态: 手动创建资源
动态: 自动创建PV
1
2
3
4
5
6
7
8
# 在Kubernetes集群中,PV 作为存储资源存在。PVC 是对PV资源的请求和使用,也是对PV存储资源的”提取证”,而Pod通过PVC来使用PV。
# PV 和 PVC 之间的交互过程有着自己的生命周期,这个生命周期分为5个阶段:

1. 供应(Provisioning):即PV的创建,可以直接创建PV(静态方式),也可以使用StorageClass 动态创建
2. 绑定(Binding):将PV分配给PVC
3. 使用(Using):Pod通过PVC使用该Volume
4. 释放(Releasing):Pod释放Volume并删除PVC
5. 回收(Reclaiming):回收PV,可以保留PV以便下次使用,也可以直接从云存储中删除
1
2
3
4
5
# 存储卷的存在下面的4种状态:
1. Available:可用状态,处于此状态表明PV以及准备就绪了,可以被PVC使用了。
2. Bound:绑定状态,表明PV已被分配给了PVC。
3. Released:释放状态,表明PVC解绑PV,但还未执行回收策略。
4. Failed:错误状态,表明PV发生错误。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[root@k8s-master1 demo]# kubectl explain PersistentVolume
KIND: PersistentVolume
VERSION: v1

[root@k8s-master1 demo]# vim nfs-pv.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
name: mongodb-pv
spec:
capacity: # 定义pv 容量大小
storage: 5Gi
accessModes: # 访问模式
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain # 持久化卷 回收策略 当声明释放后,pv将会被保留(不删除和释放)
nfs: # 存储类型 位置 和 其他属性
path: /data/nfs
server: 172.31.228.68


1. 访问模式的可选范围如下:
ReadWriteOnce: 该卷能够以 读写模式 被加载到一个节点上。
ReadOnlyMany: 该卷能够以 只读模式 加载到多个节点上。
ReadWriteMany: 该卷能够以 读写模式 被多个节点同时加载。
1
2
3
4
5
6
7
8
9
[root@k8s-master1 demo]# kubectl create -f nfs-pv.yaml 
persistentvolume/mongodb-pv created

[root@k8s-master1 demo]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mongodb-pv 5Gi RWX Retain Available 4s

# Retain 默认回收策略
# Available 可用状态

创建 PVC 持久卷声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@k8s-master1 demo]# kubectl explain PersistentVolumeClaim
KIND: PersistentVolumeClaim
VERSION: v1

[root@k8s-master1 demo]# vim nfs-pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mongodb-pvc # 声明(pvc)的名称,pod中会引用到
spec:
resources: # 申请5G的存储空间
requests:
storage: 5Gi
accessModes: # 访问模式
- ReadWriteMany
1
2
3
4
5
1. resources 资源; 财力
2. 注意:持久卷的容量必须满足声明的需求,并且卷的访问模式必须包含申明中指定的访问模式
否则pvc会无限期的处于未绑定状态,一旦存在匹配的PV,PVC绑定此PV,
就算集群中存在很多的50G的PV,需要100G容量的PVC也不会匹配满足需求的PV。直到集群中有100G的PV时,PVC才会被绑定。
3. 如果没有指定存储类和设置选取器,PVC会根据存储空间容量大小和访问模式匹配符合的PV
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@k8s-master1 demo]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mongodb-pvc Bound mongodb-pv 5Gi RWX 82s

# 在 CLI 下,访问模式缩写为: RWO、ROX、RWX涉及工作节点数量而不是Pod数量
RWO:ReadWriteOnce
ROX:ReadOnlyMany
RWX:ReadWriteMany

[root@k8s-master1 demo]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mongodb-pv 5Gi RWX Retain Bound default/mongodb-pvc 30m

# Bound 进入绑定状态 将PV分配给PVC
# mongodb-pv 持久卷绑定给 default/mongodb-pvc 持久卷声明
# default/mongodb-pvc default是持久卷声明的命名空间
# 持久卷PV是集群范围的,持久卷声明需要被同一命名空间的Pod创建

Pod 使用 PVC 持久卷声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@k8s-master1 demo]# vim mongodb-pod-pvc.yaml 

apiVersion: v1
kind: Pod
metadata:
name: mongodb
spec:
# nodeSelector:
# gpu: "true"
volumes:
- name: mongodb-data
persistentVolumeClaim:
claimName: mongodb-pvc # 引用持久卷声明 pvc name
containers:
- image: mongo
name: mongodb
volumeMounts:
- name: mongodb-data
mountPath: /data/db
ports:
- containerPort: 27017
protocol: TCP
1
2
3
4
5
6
7
8
9
10
11
12
13
[root@k8s-master1 demo]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mongodb 1/1 Running 0 19s 10.244.0.35 k8s-node1 <none> <none>

[root@k8s-master1 demo]# kubectl exec -it mongodb mongo

[root@k8s-master1 demo]# kubectl create -f mongodb-pod-pvc.yaml
pod/mongodb created

> use mystore
switched to db mystore
> db.doo.find()
{ "_id" : ObjectId("5e72e43d3bb458e3186a18c5"), "name" : "leo" }

使用 PV 和 PVC 的好处

1
2
1. 研发人员不用担心 底层的存储技术 只需要申请容量和访问模式
2. 其他的 pod 无法使用一个 被绑定的pvc

回收持久卷

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 删除 Pod 
[root@k8s-master1 demo]# kubectl delete pod mongodb

# 删除 PVC
[root@k8s-master1 demo]# kubectl delete pvc mongodb-pvc
persistentvolumeclaim "mongodb-pvc" deleted

# 重新创建 PVC
[root@k8s-master1 demo]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mongodb-pvc Pending 8s

# 为什么不绑定 查看 PV
[root@k8s-master1 demo]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mongodb-pv 5Gi RWX Retain Released default/mongodb-pvc 116m

# Released 释放状态,表明PVC解绑PV,但还未执行回收策略。
# 说明这个卷里面的数据 还没有被清理

手动回收持久卷

1
2
1. persistentVolumeReclaimPolicy: Retain ,该设置告诉k8s,PVC和PV解绑后,保留卷和数据
2. 手动回收 删除和更新创建持久卷资源,你可以自己决定是否删除底层存储中的文件,也可以不删除在下一个Pod中复用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# 测试不删除底层数据,然后手动删除持久卷,重建资源
[root@k8s-master1 demo]# kubectl delete pv mongodb-pv
persistentvolume "mongodb-pv" deleted

# 底层数据不清理
[root@k8s-master2 opt]# cd /data/nfs/
[root@k8s-master2 nfs]# ls -l
total 336
-rw------- 1 polkitd input 20480 Mar 19 17:27 collection-0-485674314440757453.wt
-rw------- 1 polkitd input 36864 Mar 19 17:27 collection-2-485674314440757453.wt
-rw------- 1 polkitd input 4096 Mar 19 11:18 collection-4-485674314440757453.wt
-rw------- 1 polkitd input 20480 Mar 19 17:27 collection-8-485674314440757453.wt
drwx------ 2 polkitd input 4096 Mar 19 17:27 diagnostic.data
-rw------- 1 polkitd input 20480 Mar 19 17:27 index-1-485674314440757453.wt
-rw------- 1 polkitd input 36864 Mar 19 17:27 index-3-485674314440757453.wt
-rw------- 1 polkitd input 4096 Mar 19 17:27 index-5-485674314440757453.wt
-rw------- 1 polkitd input 12288 Mar 19 17:27 index-6-485674314440757453.wt
-rw------- 1 polkitd input 20480 Mar 19 11:18 index-9-485674314440757453.wt
drwx------ 2 polkitd input 4096 Mar 19 17:15 journal
-rw------- 1 polkitd input 36864 Mar 19 17:27 _mdb_catalog.wt
-rw------- 1 polkitd input 0 Mar 19 17:27 mongod.lock
-rw------- 1 polkitd input 36864 Mar 19 17:27 sizeStorer.wt
-rw------- 1 polkitd input 114 Mar 19 11:14 storage.bson
-rw------- 1 polkitd input 47 Mar 19 11:14 WiredTiger
-rw------- 1 polkitd input 4096 Mar 19 17:27 WiredTigerLAS.wt
-rw------- 1 polkitd input 21 Mar 19 11:15 WiredTiger.lock
-rw------- 1 polkitd input 1188 Mar 19 17:27 WiredTiger.turtle
-rw------- 1 polkitd input 61440 Mar 19 17:27 WiredTiger.wt

# 重建持久卷 持久卷声明 Pod
[root@k8s-master1 demo]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mongodb-pv 5Gi RWX Retain Available 3s

[root@k8s-master1 demo]# kubectl create -f nfs-pvc.yaml
persistentvolumeclaim/mongodb-pvc created

[root@k8s-master1 demo]# kubectl create -f mongodb-pod-pvc.yaml
pod/mongodb created

# 数据复用
[root@k8s-master1 demo]# kubectl exec -it mongodb mongo
> use mystore
switched to db mystore
> db.doo.find()
{ "_id" : ObjectId("5e72e43d3bb458e3186a18c5"), "name" : "leo" }

测试删除 PV和底层数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[root@k8s-master1 demo]# kubectl delete pod mongodb
[root@k8s-master1 demo]# kubectl delete pvc mongodb-pvc
[root@k8s-master1 demo]# kubectl delete pv mongodb-pv
[root@k8s-master2 nfs]# rm -rf *

[root@k8s-master1 demo]# kubectl create -f nfs-pv.yaml

[root@k8s-master1 demo]# kubectl create -f nfs-pvc.yaml

[root@k8s-master1 demo]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mongodb-pv 5Gi RWX Retain Bound default/mongodb-pvc 7s

[root@k8s-master1 demo]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mongodb-pvc Bound mongodb-pv 5Gi RWX 8s

[root@k8s-master1 demo]# kubectl create -f mongodb-pod-pvc.yaml
pod/mongodb created

# 无法复用之前的数据,已经被手动清理
[root@k8s-master1 demo]# kubectl exec -it mongodb mongo
> use mystore
switched to db mystore
> db.doo.find()
> db.doo.insert({name:'Lex'})
WriteResult({ "nInserted" : 1 })
> db.doo.find()
{ "_id" : ObjectId("5e733ee540bc22899dc58827"), "name" : "Lex" }

# 数据已重新被创建
[root@k8s-master2 nfs]# ls -l
...

自动回收持久卷

1
2
3
4
5
6
7
8
9
# 当前的回收策略可选值包括:
Retain: 持久化卷被释放后,需要手工进行回收操作。

Recycle: 基础擦除(“rm-rf /thevolume/*”) 可用于再次声明,可被不同的持久卷声明和Pod引用
Delete: 相关的存储资产,例如AWSEBS或GCE PD卷一并删除。 彻底删除底层
目前,只有NFS和HostPath支持Recycle策略,AWSEBS、GCE PD支持Delete策略。

# 再创建持久卷PV的时候,一定要检查卷中的底层存储支持什么回收策略
# 可以更改当前使用的持久卷的回收策略,如果之前的delete或者Recycle,那么可改成Retain,手动回收来保持重要数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# 测试 nfs 的 Recycle 回收策略 是否删除了底层数据 和 被新的PVC复用

[root@k8s-master1 demo]# vim nfs-pv.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
name: mongodb-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
nfs:
path: /data/nfs
server: 172.31.228.68

[root@k8s-master1 demo]# kubectl apply -f nfs-pv.yaml

[root@k8s-master1 ~]# kubectl edit pv mongodb-pv

[root@k8s-master1 ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mongodb-pvc Bound mongodb-pv 5Gi RWX 11m

[root@k8s-master1 ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mongodb-pv 5Gi RWX Recycle Bound default/mongodb-pvc 12m

# 删除 pvc pv pod
[root@k8s-master1 demo]# kubectl delete pod mongodb

[root@k8s-master1 demo]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mongodb-pv 5Gi RWX Recycle Released default/mongodb-pvc 13m

# Released 释放状态,表明PVC解绑PV,但还未执行回收策略。
# 过了一会 变成 Available 说明 pv被自动回收了 查看数据
[root@k8s-master1 demo]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mongodb-pv 5Gi RWX Recycle Available 14m

# nfs 中的数据也被rm掉
[root@k8s-master2 nfs]# ls -l
total 0

# 重新创建并绑定
[root@k8s-master1 demo]# kubectl create -f nfs-pvc.yaml
persistentvolumeclaim/mongodb-pvc created

[root@k8s-master1 demo]# kubectl create -f mongodb-pod-pvc.yaml
pod/mongodb created

[root@k8s-master1 demo]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mongodb-pvc Bound mongodb-pv 5Gi RWX 8s

[root@k8s-master1 demo]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
mongodb-pv 5Gi RWX Recycle Bound default/mongodb-pvc 15m

# 绑定成功,数据被清理,新的pv
[root@k8s-master1 demo]# kubectl exec -it mongodb mongo

> use mystore
switched to db mystore
> db.doo.find()

PV 动态供给

1
2
3
1. 主要是针对容量问题,手动划分非常麻烦,如果pvc的容量匹配不上pv就无法绑定 
2. k8s的动态供给就是可以动态划分容量
3. StorageClass声明存储插件,用于自动创建PV。

k8s 支持持久卷的存储插件

1
https://kubernetes.io/docs/concepts/storage/persistent-volumes/

NFS PV 动态供给

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 创建RBAC授权
# 动态创建pv插件需要连接apiserver ,所以需要授权

[root@k8s-master1 nfs]# vim rbac.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner

---

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["list", "watch", "create", "update", "patch"]

---

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: default
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# 创建nfs的nfs-client-provisioner
# 该服务帮我们自动创建pv
# 参考地址:
https://github.com/kubernetes-incubator/external-storage
https://github.com/kubernetes-incubator/external-storage/tree/master/nfs-client/deploy

[root@k8s-master1 nfs]# vim deployment-nfs.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccount: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: lizhenliang/nfs-client-provisioner:v2.0.0
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: fuseim.pri/ifs
- name: NFS_SERVER
value: 172.31.228.68
- name: NFS_PATH
value: /data/nfs
volumes:
- name: nfs-client-root
nfs:
server: 172.31.228.68
path: /data/nfs
1
2
3
4
5
6
7
8
9
# StorageClass 定义

[root@k8s-master1 nfs]# vim storageclass-nfs.yaml

apiVersion: storage.k8s.io/v1beta1
kind: StorageClass
metadata:
name: managed-nfs-storage
provisioner: fuseim.pri/ifs
1
2
3
4
5
6
7
8
9
10
11
12
13
# 创建
[root@k8s-master1 nfs]# kubectl create -f rbac.yaml
serviceaccount/nfs-client-provisioner created

[root@k8s-master1 nfs]# kubectl create -f deployment-nfs.yaml
deployment.apps/nfs-client-provisioner created

[root@k8s-master1 nfs]# kubectl create -f storageclass-nfs.yaml
storageclass.storage.k8s.io/managed-nfs-storage created

[root@k8s-master1 nfs]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-56f4b98d47-v4nf6 1/1 Running 0 85s

nfs 自动供给 机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1. 动态存储卷供应使用StorageClass进行实现,其允许存储卷按需被创建。
2. 如果没有动态存储供应,Kubernetes集群的管理员将不得不通过手工的方式类创建新的存储卷。
3. 通过动态存储卷,Kubernetes将能够按照用户的需要,自动创建其需要的存储。

1)集群管理员预先创建存储类(StorageClass);
2)用户创建使用存储类的持久化存储声明(PVC:PersistentVolumeClaim);
3)存储持久化声明通知系统,它需要一个持久化存储(PV: PersistentVolume);
4)系统读取存储类的信息;
5)系统基于存储类的信息,在后台自动创建PVC需要的PV;
6)用户创建一个使用PVC的Pod;
7)Pod中的应用通过PVC进行数据的持久化;
8)而PVC使用PV进行数据的最终持久化处理。

fuseim.pri/ifs 为上面deployment上创建的PROVISIONER_NAME。

创建 PVC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
1. 在存储类被正确创建后,就可以创建PersistenetVolumeClaim来请求StorageClass,
2. 而StorageClass将会为PersistenetVolumeClaim自动创建一个可用PersistentVolume。
3. PersistenetVolumeClaim是对PersistenetVolume的声明,即PersistenetVolume为存储的提供者,而PersistenetVolumeClaim为存储的消费者。

[root@k8s-master1 nfs]# vim nfs-pvc-dp.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mongodb-pvc
spec:
storageClassName: managed-nfs-storage
resources:
requests:
storage: 5Gi
accessModes:
- ReadWriteMany

# storageClassName: managed-nfs-storage # pvc 请求自定义存储类 去创建pv

[root@k8s-master1 nfs]# kubectl create -f nfs-pvc-dp.yaml
persistentvolumeclaim/mongodb-pvc created

[root@k8s-master1 nfs]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mongodb-pvc Bound default-mongodb-pvc-pvc-9248dad6-b64b-48c3-a3a3-d9f576a0c412 5Gi RWX managed-nfs-storage 71s

[root@k8s-master1 nfs]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
default-mongodb-pvc-pvc-9248dad6-b64b-48c3-a3a3-d9f576a0c412 5Gi RWX Delete Bound default/mongodb-pvc managed-nfs-storage 74s

# Delete 是默认的回收策略 也就是当删除pvc与pv的绑定关系后 pv也会被删除 彻底删除底层
# 如果要修改 在 StorageClass 增加 reclaimPolicy: Retain 手动回收资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[root@k8s-master1 nfs]# vim  mongodb-pod-pvc.yaml 

apiVersion: v1
kind: Pod
metadata:
name: mongodb
spec:
# nodeSelector:
# gpu: "true"
volumes:
- name: mongodb-data
persistentVolumeClaim:
claimName: mongodb-pvc
containers:
- image: mongo
name: mongodb
volumeMounts:
- name: mongodb-data
mountPath: /data/db
ports:
- containerPort: 27017
protocol: TCP

[root@k8s-master1 nfs]# kubectl create -f mongodb-pod-pvc.yaml
[root@k8s-master1 nfs]# kubectl exec -it mongodb mongo
> use mystore
switched to db mystore
> db.doo.find()
WriteResult({ "nInserted" : 1 })
> db.doo.find()
{ "_id" : ObjectId("5e7384172fb631dbf214e42d"), "name" : "123" }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 删除 pod 和 pvc
[root@k8s-master1 nfs]# kubectl delete pod mongodb
pod "mongodb" deleted

[root@k8s-master1 nfs]# kubectl delete pvc mongodb-pvc
persistentvolumeclaim "mongodb-pvc" deleted

# pv 被完全删除了
[root@k8s-master1 nfs]# kubectl get pv
No resources found in default namespace.

# nfs 下的数据还存在
[root@k8s-master2 nfs]# ls -l
total 4
drwxrwxrwx 4 polkitd root 4096 Mar 19 22:42 archived-default-mongodb-pvc-pvc-9248dad6-b64b-48c3-a3a3-d9f576a0c412

# 在重新创建一个 pvc
[root@k8s-master1 nfs]# kubectl create -f mongodb-pod-pvc.yaml
pod/mongodb created

# 会发现绑定了一块新的 VOLUME
[root@k8s-master1 nfs]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mongodb-pvc Bound default-mongodb-pvc-pvc-9f9f3be5-8e1c-44db-bb36-fdb7219199d1 5Gi RWX managed-nfs-storage 2s

[root@k8s-master2 nfs]# ls -l
total 8
drwxrwxrwx 4 polkitd root 4096 Mar 19 22:42 archived-default-mongodb-pvc-pvc-9248dad6-b64b-48c3-a3a3-d9f576a0c412
drwxrwxrwx 4 polkitd root 4096 Mar 19 22:46 default-mongodb-pvc-pvc-9f9f3be5-8e1c-44db-bb36-fdb7219199d1

查看存储类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@k8s-master1 nfs]# kubectl get sc
NAME PROVISIONER AGE
managed-nfs-storage fuseim.pri/ifs 26m

[root@k8s-master1 nfs]# kubectl get sc managed-nfs-storage -o yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
creationTimestamp: "2020-03-19T14:22:39Z"
name: managed-nfs-storage
resourceVersion: "160399"
selfLink: /apis/storage.k8s.io/v1/storageclasses/managed-nfs-storage
uid: 08256c53-3ca7-4aa4-b0c2-620b94bd7110
provisioner: fuseim.pri/ifs
reclaimPolicy: Delete
volumeBindingMode: Immediate