一、前言
前面我们接触了Kubernetes的搭建,安装和核心组件,现在我们就聊聊Kubernetes的持久化存储方面的事情。
通过前面的文章我们知道,当pod挂了以后,deployment会根据pod的yml文件的配置重新创建一定数量的pod来维持数量。如果我们没有持久化的方案,那么pod中的数据就会丢失,新创建的pod无法共用之前的数据。
二、Volumes
Kubernetes作为一个成熟的产品,其肯定会考虑这个问题的。Kubernetes提供了Volume来用于持久化存储。关于Volume请看官网:https://kubernetes.io/docs/concepts/storage/volumes/。
通过官网的大致介绍,我们知道Volumes有不同的类型,下面我就介绍j几种基本和常用的Volumes。
(一)Host类型的Volume
通过官网的介绍我们可以看到,Host类型的Volumes是使用pod的所在节点的文件夹或者文件来跟pod中container的文件进行关联,其实也就是挂载的意思。
我们可以自己尝试实践一下,在Pod中定义Volume,然后pod中再定义两个container,都使用pod的vlolume。
1、创建pod
pod的yml文件如下:
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: testVolume-container
image: testVolume
volumeMounts:
- name: volume-pod
mountPath: /test-volume
volumes:
- name: volume-pod
hostPath:
path: /tmp/volume-pod
上面的yml应该都能看懂,设置了pod的vloume,名字叫做volume-pod,路径是pod所在主机的/tmp/volume-pod,然后定义的两个container都使用这个volume,对应的路径是container内部各自的路径。它们是通过volumes的name进行关联使用的。
运行命令:kubectl apply -f volume-pod.yml
2、创建文件并校验contianer中是否有
(1)在pod所属节点的/tmp/volume-pod路径下随便创建一个文件
(2)分别进入两个container中,然后分别检查/nginx-volume和/test-volume是否有这个文件,可以使用:docker exec -it containerid sh
进入到container中。
这里就说一下实践的结果,就不截图了,可以发现在这两个container中能看到pod的volume配置目录下新建的文件。
3、分别查看容器里的hosts文件
使用:docker exec -it containerid cat /etc/hosts
命令,可以看到pod的hosts文件和container的hosts文件都是一样的。
因此,一般修改container中存储或者网络方面的内容,不要在container中改,直接在pod中改就能同步。
4、结论
host类型的Volume本地配置的共享文件或者文件夹。
其实也可以使用deployment创建一个pod并配置vloume路径,然后启动pod以后添加文件然后手动销毁当前pod,deployment由于你设置了pod副本的数量,新建pod的volume还是有该文件,因为是共享的。
5、问题
上面我们发现pod中的container共享volume,即使pod挂了数据也不会丢失。但是问题来了,如果pod所在的机器挂了,数据还是会丢失。那我们的volume关联的目录能不能是其他机器或者是其他服务器,这样就算当前机器挂了数据也不会丢失,于是就有了PersistentVolume。
(二)PersistentVolume
官网的地址:https://kubernetes.io/docs/concepts/storage/persistent-volumes/
PersistentVolume又可以简称为PV,我们可以在pv的yml文件中定义我们需要的volume大小以及是否被读写等配置。配置文件如下:
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 # 远端的服务器
一般都是使用其他的服务器作为volume的目录,但是如果我们自己解决与其他服务器的通信等其他问题就太麻烦了。通过官网我们可以看到,官网通过插件的方式已经帮我们解决了其他不同类型服务器的通信等问题,但是也只是支持其列出来的那几种。
本质:其实说白了pv就是k8s中的资源,是volume的plugin实现,生命周期独立于pod,封装了底层存储卷实现的细节。pv通常是运维人员进行维护的。
问题:pv一般都是专业的运维人员维护的,跟不同的服务器打交道,那我们开发人员使用的时候不能自己去手动创建一个pv,然后再使用吧,因此就有了PersistentVolumeClaim。
(三)PersistentVolumeClaim
官网的地址:https://kubernetes.io/docs/concepts/storage/persistent-volumes/#persistentvolumeclaims
1、何为PersistentVolumeClaim
PersistentVolumeClaim又可以简称为PVC,我们可以设计一个pvc绑定pv,然后把pvc交给pod使用。运维同学会创建各种类型的pv,然后我们利用pvc匹配满足要求的pv(根据所需空间大小和访问模式进行匹配的),跟pv进行一一绑定,然后pv的状态就会变成Bound。
也就是pvc负责请求pv的大小和访问方式,然后pod中就可以直接使用pvc。
注意: pvc通常是由开发人员进行维护的。
下面就是官网给的pvc的yml文件:
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]}
2、Pod中如何使用PVC
可以看官网的介绍:https://kubernetes.io/docs/concepts/storage/persistent-volumes/#claims-as-volumes
具体的yml文件如下:
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
相信大家都能看懂吧,在volumes下配置persistentVolumeClaim的名字即可。
3、Pod中使用PVC的具体实践
我们就创建一个Nginx的pod然后使用nfs服务器上的文件,下面是具体的实践步骤:
3.1、搭建nfs服务器
nfs(network file system):网络文件系统,是FreeBSD支持的文件系统中的一种,允许网络中的计算机之间通过 TCP/IP网络共享资源。
上面我们pv选择了nfs服务器,那么我们就搭建nfs的服务器。
3.1.1、选择一个服务安装nfs,可以是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 master-ip
3.1.2、在所有使用nfs的节点上安装客户端
yum -y install nfs-utils
systemctl start nfs && systemctl enable nfs
3.2、创建pv&pvc&Nginx
3.2.1、在nfs服务器上创建所需要存储文件的目录
mkdir -p /nfs/data/nginx
3.2.2、定义PV、PVC、Nginx的文件
我们不用一个个去创建,在一个yml文件中就能创建三个,最好的步骤是先创建被调用方再创建调用方
# 定义PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: nginx-pv
spec:
accessModes:
- ReadWriteMany
capacity:
storage: 2Gi
nfs:
path: /nfs/data/nginx
server: xxx.xxx.xxx.xxx
---
# 定义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
3.2.3、根据yml文件创建资源并查看
kubectl apply -f nginx-pv-demo.yaml
kubectl get pv,pvc
kubectl get pods -o wide
3.2.4、测试持久化存储
(1)在nfs的服务器/nfs/data/nginx的路径下创建一个test.html,写入一些内容;
(2)运行:kubectl get pods -o wide
得到nginx-pod的ip地址;
(3)再运行:curl nginx-pod-ip/test.html
访问对应ip下的这个html就能看到返回的结果跟我们写入的是否一样;
(4)也可以运行:kubectl exec -it nginx-pod bash
然后进入入/usr/share/nginx/html目录查看其下是否有test.html以及其内容是否一致;
(5)也可以运行:kubectl delete pod nginx-pod
删除当前pod,然后deployment会创建新的pod;
(6)然后再查看新的新nginx-pod的ip并访问新的ip下的test.html看是否有;
3.3、流程图解
其实就是Pod访问PVC,然后PVC访问PV,PV访问存储服务器,我们创建的时候一般是从被调用者开始往调用者创建。
4、问题
问题:我们创建满足我们需求的pv然后跟pod关联,但是我们不能创建一大堆的pv,等我们需要的时候就会自动关联,这样太浪费资源了。因此就出现了StorageClass。
(四)StorageClass
1、何为StorageClass
官网:https://kubernetes.io/docs/concepts/storage/storage-classes/
nfs github:https://github.com/kubernetes-incubator/external-storage/tree/master/nfs
从官网的介绍我们可以看到:StorageClass声明存储插件用于自动创建PV。说白了就是创建PV的模板,其中有连个重要的部分:PV的属性和创建次PV需要的插件。这样PV就可以按照Class来匹配PV,可以为PV指定storageClassName属性,标识PV归属于哪一个Class。
下面就是StorageClass的yml文件:
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
有了StorageClass之后的PVC可以变成如下:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim1
spec:
accessModes: - ReadWriteMany
resources:
requests:
storage: 1Mi
storageClassName: nfs
2、何为Provisioner
StorageClass之所以能顾动态提供PV是因为Provisioner ,根据官网我们可以看出k8s提供了多种类型的Provisioner,但是发现我们上面使用的nfs的Provisioner是没有的,需要自己创建。nfs的Provisioner插件github有人开源提供了,等会我们实践的时候具体讲解。
上面我们提供的官方给的StorageClass的yml文件可以看出是用的AWSElasticBlockStore的Provisioner。
3、StorageClass实践
因为之前我们使用的都是nfs服务器,现在我们依然使用nfs服务器,虽然k8s官方没有提供nfs的Provisioner插件的,但是github上有人已经开源了。
开源地址:https://github.com/kubernetes-incubator/external-storage/tree/master/nfs
以下需要的yml文件都可以在上面github的开源地址中的/deploy/kubernetes文件夹下找到,我没有写的就是可以直接去github上拿来直接用的,有些进行了一些改动,会把改动以后的yml给出的。
3.3、准备好NFS服务器
可以使用上面pod中使用pvc实践中创建nfs服务器的步骤准备好nfs服务器,并确保nfs可以正常工作,同事创建持久化存储的目录。
mkdir -p /nfs/data/nginx
3.2根据rbac.yaml文件创建资源
kubectl apply -f rbac.yaml
3.3根据deployment.yaml文件创建资源
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: xxx.xxx.xx.xx #nfs服务器地址
- name: NFS_PATH
value: /nfs/data/nginx
volumes:
- name: nfs-client-root
nfs:
server: xxx.xxx.xx.xx #nfs服务器地址
path: /nfs/data/nginx
kubectl apply -f deployment.yaml
3.4根据class.yaml创建资源
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: example-nfs
provisioner: example.com/nfs
kubectl apply -f class.yaml
3.5根据pvc.yaml创建资源
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: my-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Mi
# 这个名字要和上面创建的storageclass名称一致
storageClassName: example-nfs
kubectl apply -f my-pvc.yaml
kubectl get pvc
3.6根据nginx-pod创建资源
kind: Pod
apiVersion: v1
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: my-pvc
mountPath: "/usr/test"
restartPolicy: "Never"
volumes:
- name: my-pvc
persistentVolumeClaim:
claimName: my-pvc
kubectl apply -f nginx-pod.yaml
kubectl exec -it nginx bash
cd /usr/test # 进行同步数据测试
4、StorageClass总结
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只能对应一种后端存储;
流程图如下:
(五) PV的状态和回收策略
5.1、PV的状态
Available:表示当前的pv没有被绑定;
Bound:表示已经被pvc挂载;
Released:pvc没有在使用pv, 需要管理员手工释放pv;
Failed:资源回收失败;
5.2、PV回收策略
Retain:表示删除PVC的时候,PV不会一起删除,而是变成Released状态等待管理员手动清理;
Recycle:在Kubernetes新版本就不用了,采用动态PV供给来替代;
Delete:表示删除PVC的时候,PV也会一起删除,同时也删除PV所指向的实际存储空间;
注意 :目前只有NFS和HostPath支持Recycle策略。AWS EBS、GCE PD、Azure Disk和Cinder支持Delete策略