K8S之Storage
- 一、Storage
- 1.1、Volume
- 1.2、PersistentVolume(PV)
- 1.3、PersistentVolumeClaim(PVC)
- 1.4、StorageClass
- 1.5、PV的状态和回收策略
kubernetes使用NFS共享存储有两种方式:
- (1)手动方式静态创建所需要的PV和PVC(见本文1.3.2小节)。
- (2)通过创建PVC动态地申请PV(见本文1.4.2小节)。
一、Storage
Volume地址
:https://kubernetes.io/docs/concepts/storage/
1.1、Volume
1.1.1、Volume简介
volume(/ˈvɒljuːm/,IT词汇–百度百科): 在电脑中, volume(文件集)是一个可辨认的数据存储(storage)单元。
Volume地址
:https://kubernetes.io/docs/concepts/storage/volumes/容器中的磁盘文件是临时的,这对于在容器中运行的非平凡(重大)应用程序会带来一些问题:
- (1)容器崩溃时文件丢失。
- (2)Kubelet重新启动容器后为clean状态。
在Pod中一起运行的容器之间共享文件时,会出现第二个问题。
Kubernetes的Volume抽象地解决了这两个问题。
1.2.1、Host类型volume实战
思路: 查看volume挂载目录是否同步。
定义一个Pod,其中包含两个Container,都使用Pod的Volume。
(1)创建volume-pod.yaml
创建volume-pod.yaml:
[root@m test]# mkdir volume
[root@m test]# cd volume
[root@m volume]# vi volume-pod.yaml
内容:
apiVersion: v1
kind: Pod
metadata:
name: volume-pod
spec:
containers:
- name: nginx-container
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: volume-pod
mountPath: /nginx-volume
- name: busybox-container
image: busybox
command: ['sh', '-c', 'echo The app is running! && sleep 3600']
volumeMounts:
- name: volume-pod
mountPath: /busybox-volume
volumes:
- name: volume-pod
hostPath:
path: /tmp/volume-pod
(2)创建pod
[root@m volume]# kubectl apply -f volume-pod.yaml
pod/volume-pod created
[root@m volume]#
[root@m volume]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
volume-pod 2/2 Running 0 82s 192.168.80.248 w2 <none> <none>
[root@m volume]#
(3)查看pod的容器ID
在volume-pod所在的集群节点,查看容器ID:
[root@w2 ~]# docker ps | grep volume
容器busybox-volume的ID为:a91b394009ff
容器nginx-volume的ID为:3a7201c4a656
(4)测试volume挂载目录是否同步
① 在宿主系统中/tmp/volume-pod目录创建1.txt:
[root@w2 ~]# cd /tmp/volume-pod
[root@w2 volume-pod]# vi 1.txt
[root@w2 volume-pod]# ls
1.txt
[root@w2 volume-pod]#
② 进入nginx-volume容器查看/nginx-volume目录
[root@w2 volume-pod]# docker exec -it a91b394009ff sh
/ # ls /busybox-volume
1.txt
/ # cat /busybox-volume/1.txt
1234
/ # exit
[root@w2 volume-pod]#
③ 进入busybox-volume容器查看/busybox-volume目录
[root@w2 volume-pod]# docker exec -it 3a7201c4a656 sh
# ls /nginx-volume
1.txt
# cat /nginx-volume/1.txt
1234
# exit
[root@w2 volume-pod]#
(5)测试结果
总结:
volume挂载目录是同步的
。
(6)删除测试资源
[root@m volume]# kubectl delete -f volume-pod.yaml
[root@m volume]# rm volume-pod.yaml
1.2.3、查看pod中的容器里面的hosts文件
查看nginx-volume容器hosts
[root@w2 volume-pod]# docker exec -it 3a7201c4a656 cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
192.168.80.248 volume-pod
[root@w2 volume-pod]#
查看busybox-volume容器的hosts文件
[root@w2 volume-pod]# docker exec -it a91b394009ff cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
192.168.80.248 volume-pod
[root@w2 volume-pod]#
结论:
发现两个容器的hosts是一样的,并且都是由pod管理的。所以一般container中的存储或网络内容,不要在container层面直接修改,而是在pod中进行修改。
1.2、PersistentVolume(PV)
官网
:https://kubernetes.io/docs/concepts/storage/persistent-volumes/
PersistentVolume(PV)是已经由管理员提供或者动态使用供应的集群中的一块存储的存储类。它是集群中的资源,就像节点是集群资源一样。PV是类似于Volumes的卷插件,但是其生命周期独立于使用PV的任何单个Pod。
yaml示例:
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv
spec:
capacity:
storage: 5Gi # 存储空间大小
volumeMode: Filesystem
accessModes:
- ReadWriteOnce # 只允许一个Pod进行独占式读写操作
persistentVolumeReclaimPolicy: Recycle
storageClassName: slow
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /tmp # 远端服务器的目录
server: 172.17.0.2 # 远端的服务器
PV是K8s中的资源,是volume的plugin实现,生命周期独立于Pod,封装了底层存储卷实现的细节。
注意
:PV的维护通常是由运维人员、集群管理员进行维护的。
1.3、PersistentVolumeClaim(PVC)
1.3.1、PVC简介
官网
:https://kubernetes.io/docs/concepts/storage/persistent-volumes/#persistentvolumeclaims有了PV,那Pod如何使用呢?为了方便使用,我们可以设计出一个PVC来绑定PV,然后把PVC交给Pod来使用即可,且听下面的分析。
PersistentVolumeClaim(PVC)是由用户进行存储的请求,它类似于pod,容器消耗节点资源,PVC消耗PV资源,Pod可以请求特定级别的资源(CPU和内存),声明可以请求特定的大小和访问模式(例如,可以将它们安装为ReadWriteOnce,ReadOnlyMany或ReadWriteMany)。
yaml示例:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 8Gi
storageClassName: slow
selector:
matchLabels:
release: "stable"
matchExpressions:
- {key: environment, operator: In, values: [dev]}
Pod中如何使用PVC示例:
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: myfrontend
image: nginx
volumeMounts:
- mountPath: "/var/www/html"
name: mypd
volumes:
- name: mypd
persistentVolumeClaim:
claimName: myclaim
PVC会匹配满足要求的PV[是根据size和访问模式进行匹配的],进行一一绑定,然后它们的状态都会变成Bound,即PVC负责请求PV的大小和访问方式,然后Pod中就可以直接使用PVC了。
注意
:PVC通常由开发小伙伴维护,开发小伙伴无需关注与存储细节。
pod、pvc、pv关系图:
1.3.2、Pod中使用PVC实战
背景
:使用nginx持久化存储演示
(1) 共享存储使用nfs,比如选择在master节点。
(2) 创建pv和pvc。
(3) nginx pod中使用pvc。
(1)master节点搭建nfs
nfs(network file system)网络文件系统,是FreeBSD支持的文件系统中的一种,允许网络中的计算机之间通过TCP/IP网络共享资源。
在master节点上搭建一个NFS服务器,目录为/nfs/data
01 选择k8s集群的master节点作为nfs的server,所以在master节点上
# 安装nfs
yum install -y nfs-utils
# 创建nfs目录
mkdir -p /nfs/data/
mkdir -p /nfs/data/mysql
# 授予权限
chmod -R 777 /nfs/data
# 编辑export文件
vi /etc/exports
内容:/nfs/data *(rw,no_root_squash,sync)
# 使得配置生效
exportfs -r
# 查看生效
exportfs
# 启动rpcbind、nfs服务(并设置开机启动)
systemctl restart rpcbind && systemctl enable rpcbind
systemctl restart nfs && systemctl enable nfs
# 查看rpc服务的注册情况
rpcinfo -p localhost
# showmount测试
showmount -e
02 所有k8s集群node上都需要安装客户端,并启动
yum -y install nfs-utils
systemctl start nfs && systemctl enable nfs
(2)为nfs服务器创建测试目录(/nfs/data/nginx)
[root@m /]# mkdir -p /nfs/data/nginx
(3)创建nginx-pv-demo.yaml
在master节点,创建nginx-pv-demo.yaml,定义PV、PVC、Nginx:
[root@m /]# vi nginx-pv-demo.yaml
yaml内容(修改nfs服务器IP
):
# 定义PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: nginx-pv
spec:
accessModes:
- ReadWriteMany
capacity:
storage: 2Gi
nfs:
path: /nfs/data/nginx
server: 192.168.116.170
---
# 定义PVC,用于消费PV
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nginx-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 2Gi
---
# 定义Pod,指定需要使用的PVC
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: nginx
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: mysql
ports:
- containerPort: 80
volumeMounts:
- name: nginx-persistent-storage
mountPath: /usr/share/nginx/html
volumes:
- name: nginx-persistent-storage
persistentVolumeClaim:
claimName: nginx-pvc
(4)创建PV&PVC&Nginx
根据nginx-pv-demo.yaml配置创建资源,并查看资源:
#(1)创建资源
[root@m /]# kubectl apply -f nginx-pv-demo.yaml
persistentvolume/nginx-pv created
persistentvolumeclaim/nginx-pvc created
deployment.apps/nginx created
[root@m /]#
#(2)查看pod
[root@m /]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-c4998fd44-8bwt2 1/1 Running 0 24s 192.168.80.249 w2 <none> <none>
[root@m /]#
#(3)查看pv和pvc
[root@m /]# kubectl get pv,pvc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/nginx-pv 2Gi RWX Retain Bound default/nginx-pvc 28s
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/nginx-pvc Bound nginx-pv 2Gi RWX 28s
[root@m /]#
(5)测试目录同步
说明: 这里创建的nginx的pod的IP,在k8s的其他集群节点,ping不通,所以只能在w2(worker02)集群节点执行curl测试。
#(1)创建任意测试文件test.html(内容:abcd1234)
[root@m /]# vi /nfs/data/nginx/test.html
#(2)查看pod的IP
[root@m /]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-c4998fd44-8bwt2 1/1 Running 0 19m 192.168.80.249 w2 <none> <none>
[root@m /]#
#(3)curl访问pod测试(需要在w2(worker02)集群节点执行)
[root@w2 ~]# curl 192.168.80.249/test.html
abcd1234
[root@w2 ~]#
#(4)进入nginx容器,查看同步目录
[root@m /]# kubectl exec -it nginx-c4998fd44-8bwt2 -- bash
root@nginx-c4998fd44-8bwt2:/# cat /usr/share/nginx/html/test.html
abcd1234
root@nginx-c4998fd44-8bwt2:/# exit
exit
[root@m /]#
(6)测试持久化存储
#(1)删除pod(deployment会自动维护创建新pod)
[root@m /]# kubectl delete pod nginx-c4998fd44-8bwt2
pod "nginx-c4998fd44-8bwt2" deleted
[root@m /]#
#(2)查看新pod的IP
[root@m /]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-c4998fd44-j7mlf 1/1 Running 0 42s 192.168.80.250 w2 <none> <none>
[root@m /]#
#(3)curl再次访问pod测试(需要在w2(worker02)集群节点执行)
[root@w2 ~]# curl 192.168.80.250/test.html
abcd1234
[root@w2 ~]#
(7)删除测试资源
[root@m /]# kubectl delete -f nginx-pv-demo.yaml
[root@m /]# rm nginx-pv-demo.yaml
1.4、StorageClass
上面手动管理PV的方式还是有点low,能不能更加灵活一点呢?
1.4.1、StorageClass简介
官网
:https://kubernetes.io/docs/concepts/storage/storage-classes/
nfs github
:https://github.com/kubernetes-incubator/external-storage/tree/master/nfs
StorageClass为管理员提供了一种描述它们提供的存储“classes”的方法。不同的classes可以映射到服务质量级别、备份策略或集群管理员确定的任意策略。
每个StorageClass都包含fields provisioner、parameters和reclaimPolicy,当需要dynamically provisioned属于该类的PersistentVolume(PV)时,就使用这些字段。
StorageClass对象的名称很重要的,它是用户请求特定类的方式。管理员在首次创建StorageClass对象时设置类的名称和其他参数,创建对象后就不能再次更新对象。
StorageClass声明存储插件,用于自动创建PV。
创建PV的模板,其中有两个重要部分:PV属性和创建此PV所需要的插件,这样PVC就可以按“Class”来匹配PV。也可以为PV指定storageClassName属性,标识PV归属于哪一个Class。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp2
reclaimPolicy: Retain
allowVolumeExpansion: true
mountOptions:
- debug
volumeBindingMode: Immediate
(1)对于PV或者StorageClass只能对应一种后端存储
(2)对于手动的情况,一般我们会创建很多的PV,等有PVC需要使用的时候就可以直接使用了
(3)对于自动的情况,那么就由StorageClass来自动管理创建
(4)如果Pod想要使用共享存储,一般会在创建PVC,PVC中描述了想要什么类型的后端存储、空间等,K8s从而会匹配对应的PV,如果没有匹配成功,Pod就会处于Pending状态。Pod中使用只需要像使用volumes一样,指定名字就可以使用了
(5)一个Pod可以使用多个PVC,一个PVC也可以给多个Pod使用
(6)一个PVC只能绑定一个PV,一个PV只能对应一种后端存储
有了StorageClass之后的PVC如下:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim1
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi
storageClassName: nfs
StorageClass之所以能够动态供给PV,是因为Provisioner,也就是Dynamic Provisioning。但是NFS这种类型,K8s中默认是没有Provisioner插件的,需要自己创建。
1.4.2、StorageClass实战
github
:https://github.com/kubernetes-incubator/external-storage/tree/master/nfs
StorageClass之所以能够动态供给PV,是因为Provisioner,也就是Dynamic Provisioning。但是NFS这种类型,K8s中默认是没有Provisioner插件的,需要自己创建。
(1)nfs准备
nfs(network file system),已搭建在k8s集群的master节点。
nfs所在节点IP:192.168.116.170
(2)为nfs创建持久化目录(/nfs/data/jack)
[root@m /]# mkdir -p /nfs/data/jack
(3)根据rbac.yaml(身份验证)创建资源
① 创建yaml文件
[root@m test]# vi rbac.yaml
内容:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-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: ["create", "update", "patch"]
- apiGroups: [""]
resources: ["services", "endpoints"]
verbs: ["get"]
- apiGroups: ["extensions"]
resources: ["podsecuritypolicies"]
resourceNames: ["nfs-provisioner"]
verbs: ["use"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-provisioner
subjects:
- kind: ServiceAccount
name: nfs-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: ClusterRole
name: nfs-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-provisioner
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-provisioner
subjects:
- kind: ServiceAccount
name: nfs-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: Role
name: leader-locking-nfs-provisioner
apiGroup: rbac.authorization.k8s.io
② 根据yaml创建资源
[root@m test]# kubectl apply -f rbac.yaml
(4)根据deployment.yaml文件创建资源
① 创建yaml文件
[root@m test]# vi deployment.yaml
内容(注意修改nfs的IP地址
):
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-provisioner
---
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
name: nfs-provisioner
spec:
replicas: 1
strategy:
type: Recreate
template:
metadata:
labels:
app: nfs-provisioner
spec:
serviceAccount: nfs-provisioner
containers:
- name: nfs-provisioner
image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: example.com/nfs
- name: NFS_SERVER
value: 192.168.116.170
- name: NFS_PATH
value: /nfs/data/jack
volumes:
- name: nfs-client-root
nfs:
server: 192.168.116.170
path: /nfs/data/jack
② 根据yaml创建并查看资源
[root@m test]# kubectl apply -f deployment.yaml
[root@m test]# kubectl get pod -o wide
(5)根据class.yaml创建资源
① 创建yaml文件
[root@m test]# vi class.yaml
内容:
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: example-nfs
provisioner: example.com/nfs
② 根据yaml创建资源
[root@m test]# kubectl apply -f class.yaml
(5)根据pvc.yaml创建资源
① 创建yaml文件
[root@m test]# vi pvc.yaml
内容:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi
# 这个名字要和上面class.yaml中创建的storageclass名称一致
storageClassName: example-nfs
② 根据yaml创建并查看资源
[root@m test]# kubectl apply -f pvc.yaml
[root@m test]# kubectl get pvc
状态已经变成了Bound状态,已经动态创建了:
(6)根据nginx-pod创建资源
① 创建yaml文件
[root@m test]# vi nginx-pod
内容:
kind: Pod
apiVersion: v1
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: my-pvc
mountPath: "/usr/jack"
restartPolicy: "Never"
volumes:
- name: my-pvc
persistentVolumeClaim:
claimName: my-pvc
② 根据yaml创建资源
[root@m test]# kubectl apply -f nginx-pod
[root@m test]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nfs-provisioner-8688c9fd67-42g2k 1/1 Running 0 9m21s 192.168.190.106 w1 <none> <none>