k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)
环境说明:
主机名 | 操作系统版本 | ip | docker version | kubelet version | 配置 | 备注 |
---|---|---|---|---|---|---|
master | Centos 7.6.1810 | 172.27.9.131 | Docker 18.09.6 | V1.14.2 | 2C2G | master主机 |
node01 | Centos 7.6.1810 | 172.27.9.135 | Docker 18.09.6 | V1.14.2 | 2C2G | node节点 |
node02 | Centos 7.6.1810 | 172.27.9.136 | Docker 18.09.6 | V1.14.2 | 2C2G | node节点 |
centos7 | Centos 7.3.1611 | 172.27.9.181 | × | × | 1C1G | nfs服务器 |
k8s集群部署详见:Centos7.6部署k8s(v1.14.2)集群
k8s学习资料详见:基本概念、kubectl命令和资料分享
k8s高可用集群部署详见:Centos7.6部署k8s v1.16.4高可用集群(主备模式)
一、Volume
1. 概念
Kubernetes的卷是pod的一个组成部分,因此像容器一样在pod的规范中就定义了。它们不是独立的Kubernetes对象,也不能单独创建或删除。pod中的所有容器都可以使用卷,但必须先将它挂载在每个需要访问它的容器中。在每个容器中,都可以在其文件系统的任意位置挂载卷。
2. 为什么需要Volume
容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题。首先,当容器崩溃时,kubelet会重启它,但是容器中的文件将丢失--容器以干净的状态(镜像最初的状态)重新启动。其次,在 Pod 中同时运行多个容器时,这些容器之间通常需要共享文件。Kubernetes 中的 Volume 抽象就很好的解决了这些问题。
3. Volume类型
目前,Kubernetes支持以下Volume 类型:
本文将对emptyDir,hostPath,共享存储NFS,PV及PVC分别进行测试实践。
二、emptyDir
1. emptyDir概念
emptyDir是最基础的Volume类型,用于存储临时数据的简单空目录。如果Pod设置了emptyDir类型Volume,Pod被分配到Node上时候,会创建emptyDir,只要Pod运行在Node上,emptyDir都会存在(容器挂掉不会导致emptyDir丢失数据),但是如果Pod从Node上被删除(Pod被删除,或者Pod发生迁移),emptyDir也会被删除,并且永久丢失。
下面将用emptyDir卷实现在同一pod中两个容器之间的文件共享
2. 创建pod emptyDir-fortune
[root@master ~]# more emptyDir-pod.yaml apiVersion: v1kind: Podmetadata: labels: app: prod #pod标签 name: emptydir-fortunespec: containers: - image: loong576/fortune name: html-generator volumeMounts: #名为html的卷挂载至容器的/var/htdocs目录 - name: html mountPath: /var/htdocs - image: nginx:alpine name: web-server volumeMounts: #挂载相同的卷至容器/usr/share/nginx/html目录且设置为只读 - name: html mountPath: /usr/share/nginx/html readOnly: true ports: - containerPort: 80 protocol: TCP volumes: - name: html #卷名为html的emptyDir卷同时挂载至以上两个容器 emptyDir: {} [root@master ~]# kubectl apply -f emptyDir-pod.yaml pod/emptydir-fortune created[root@master ~]# kubectl get po -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESemptydir-fortune 2/2 Running 0 9s 10.244.2.140 node02
创建pod emptydir-fortune,该pod有两个容器,同时挂载emptyDir卷,容器html-generator向卷中写入随机内容,通过访问容器web-server验证是否实现文件的共享。
2.1 loong576/fortune镜像
root@master ~]# more Dockerfile
[root@master ~]# more fortune/Dockerfile FROM ubuntu:latestRUN apt-get update ; apt-get -y install fortuneADD fortuneloop.sh /bin/fortuneloop.shE*TRYPOINT /bin/fortuneloop.sh
该镜像的base镜像为ubuntu,镜像启动时会执行fortuneloop.sh脚本
fortuneloop.sh脚本:
[root@master ~]# more fortuneloop.sh #!/bin/bashtrap "exit" SIGINTmkdir /var/htdocswhile :do echo $(date) Writing fortune to /var/htdocs/index.html /usr/games/fortune > /var/htdocs/index.html sleep 10done
该脚本主要是每10秒钟输出随机短语至index.html文件中。
3. 访问nginx
3.1 创建service
[root@master ~]# more service-fortune.yaml apiVersion: v1kind: Servicemetadata: name: my-service #service名spec: type: NodePort selector: app: prod #pod标签,由此定位到pod emptydir-fortune ports: - protocol: TCP nodePort: 30002 #节点监听端口,暴露静态端口30002对外提供服务 port: 8881 #ClusterIP监听的端口 targetPort: 80 #容器端口 sessionAffinity: ClientIP #是否支持Session,同一个客户端的访问请求都转发到同一个后端Pod [root@master ~]# kubectl apply -f service-fortune.yaml service/my-service created[root@master ~]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEkubernetes ClusterIP 10.96.0.1 443/TCP 3d17hmy-service NodePort 10.102.191.57 8881:30002/TCP 9s
3.2 nginx访问
[root@master ~]# curl 10.102.191.57:8881Writing is easy; all you do is sit staring at the blank sheet of paper untildrops of blood form on your forehead. -- Gene Fowler[root@master ~]# curl 172.27.9.135:30002Don't Worry, Be Happy. -- Meher Baba
结论:
容器nginx成功的读取到了容器fortune写入存储的内容,emptyDir卷可以实现容器间的文件共享。
- emptyDir卷的生存周期与pod的生存周期相关联,所以当删除pod时,卷的内容就会丢失
三、hostPath
1. 概念
hostPath允许挂载Node上的文件系统到Pod里面去。如果Pod需要使用Node上的文件,可以使用hostPath。在同一个节点上运行并在其hostPath卷中使用相同路径的pod可以看到相同的文件。
2. 创建pod hostpath-nginx
2.1 创建挂载目录
在node节点上创建挂载目录,master和各node上分别执行如下操作
[root@master ~]# mkdir /data && cd /data && echo `hostname` > index.html
2.2 创建pod
[root@master ~]# more hostPath-pod.yaml apiVersion: v1kind: Podmetadata: labels: app: prod name: hostpath-nginx spec: containers: - image: nginx name: nginx volumeMounts: - mountPath: /usr/share/nginx/html #容器挂载点 name: nginx-volume #挂载卷nginx-volume volumes: - name: nginx-volume #卷名 hostPath: path: /data #准备挂载的node上的文件系统[root@master ~]# kubectl apply -f hostPath-pod.yaml pod/hostpath-nginx created[root@master ~]# kubectl get po -o wideNAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESemptydir-fortune 2/2 Running 0 40m 10.244.2.140 node02 hostpath-nginx 1/1 Running 0 16s 10.244.1.140 node01
3. 访问pod hostpath-nginx
[root@master ~]# curl 10.244.1.140node01
结论:
pod运行在node01上,访问的内容为'node01',为挂载的文件系统/data下index.html内容,容器成功读取到挂载的节点文件系统里的内容。
仅当需要在节点上读取或写入系统文件时才使用hostPath , 切勿使用它们来持久化跨pod的数据。
- hostPath可以实现持久存储,但是在node节点故障时,也会导致数据的丢失。
四、NFS共享存储
1. 概念
NFS是Network File System的缩写,即网络文件系统。Kubernetes中通过简单地配置就可以挂载NFS到Pod中,而NFS中的数据是可以永久保存的,同时NFS支持同时写操作。
emptyDir可以提供不同容器间的文件共享,但不能存储;hostPath可以为不同容器提供文件的共享并可以存储,但受制于节点限制,不能跨节点共享;这时需要网络存储 (NAS),即既可以方便存储容器又可以从任何集群节点访问,本文以NFS为例做测试。
2. nfs搭建及配置
nfs搭建详见:Centos7下NFS服务器搭建及客户端连接配置
完成nfs服务器搭建和客户端nfs软件安装安装后,可在master和各node节点检查nfs服务是否正常
[root@master ~]# showmount -e 172.27.9.181Export list for 172.27.9.181:/backup 172.27.9.0/24
master和node01、node02节点都执行showmount命令,用于验证nfs服务是否正常,/backup为nfs服务器对外提供的共享目录。
本文测试的NFS内容:
3. 新建pod mongodb-nfs
[root@master ~]# more mongodb-pod-nfs.yaml apiVersion: v1kind: Podmetadata: name: mongodb-nfsspec: containers: - image: mongo name: mongodb volumeMounts: - name: nfs-data #挂载的卷名,与上面的mongodb-data保持一致 mountPath: /data/db #MongoDB数据存放的路径 ports: - containerPort: 27017 protocol: TCP volumes: - name: nfs-data #卷名 nfs: server: 172.27.9.181 #nfs服务器ip path: /backup #nfs服务器对外提供的共享目录[root@master ~]# kubectl apply -f mongodb-pod-nfs.yaml pod/mongodb-nfs created[root@master ~]# kubectl get po -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESmongodb-nfs 1/1 Running 0 23s 10.244.2.142 node02
注意此时pod的ip为10.244.2.142
4. nfs共享存储测试
4.1 向MongoDB写入数据
[root@master ~]# kubectl exec -it mongodb-nfs mongo> use loongswitched to db loong> db.foo.insert({name:'loong576'})WriteResult({ "nInserted" : 1 })
切换至db loong,插入JSON文档(name:'loong576')
4.2 查看写入的数据
> db.foo.find(){ "_id" : ObjectId("5d6e17b018651a21e0063641"), "name" : "loong576" }
4.3 删除pod并重建
[root@master ~]# kubectl delete pod mongodb-nfs pod "mongodb-nfs" deleted[root@master ~]# kubectl apply -f mongodb-pod-nfs.yamlpod/mongodb-nfs created[root@master ~]# kubectl get po -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESmongodb-nfs 1/1 Running 0 22s 10.244.2.143 node02
删除pod mongodb-nfs并重建,此时podip变为10.244.2.143,再次访问MongoDB验证之前写入的文档是否还存在。
4.4 新pod读取共享存储数据
[root@master ~]# kubectl exec -it mongodb-nfs mongo> use loongswitched to db loong> db.foo.find(){ "_id" : ObjectId("5d6e17b018651a21e0063641"), "name" : "loong576" }
即使pod被删除重建仍然能访问共享数据。
结论:
- NFS共享存储可持久化数据
- NFS共享存储可跨节点提供数据共享
五、PV and PVC
1. 概念
PersistentVolume (持久卷, 简称 PV)和Persistent VolumeClaim(持久卷声明,简称 PVC)使得K8s集群具备了存储的逻辑抽象能力,使得在配置Pod的逻辑里可以忽略对实际后台存储技术的配置,而把这项配置的工作交给PV的配置者,即集群的管理者。存储的PV和PVC的这种关系,跟计算的Node和Pod的关系是非常类似的;PV和Node是资源的提供者,根据集群的基础设施变化而变化,由K8s集群管理员配置;而PVC和Pod是资源的使用者,根据业务服务的需求变化而变化,由K8s集群的使用者即服务的管理员来配置。
当集群用户需要在其pod中使用持久化存储时,他们首先创建PVC清单,指定所需要的最低容量要求和访问模式,然后用户将待久卷声明清单提交给Kubernetes API服务器,Kubernetes将找到可匹配的PV并将其绑定到PVC。PVC可以当作pod中的一个卷来使用,其他用户不能使用相同的PV,除非先通过删除PVC绑定来释放。
2. 创建PV
2.1 nfs配置
nfs服务器共享目录配置:
[root@centos7 ~]# exportfs /backup/v1 172.27.9.0/24/backup/v2 172.27.9.0/24/backup/v3 172.27.9.0/24
master和各node节点检查nfs配置:
[root@master ~]# showmount -e 172.27.9.181Export list for 172.27.9.181:/backup/v3 172.27.9.0/24/backup/v2 172.27.9.0/24/backup/v1 172.27.9.0/24
2.2 PV创建
[root@master ~]# more pv-nfs.yaml apiVersion: v1kind: PersistentVolumemetadata: name: pv001spec: capacity: storage: 2Gi #指定PV容量为2G volumeMode: Filesystem #卷模式,默认为Filesystem,也可设置为'Block'表示支持原始块设备 accessModes: - ReadWriteOnce #访问模式,该卷可以被单个节点以读/写模式挂载 persistentVolumeReclaimPolicy: Retain #回收策略,Retain(保留),表示手动回收 storageClassName: nfs #类名,PV可以具有一个类,一个特定类别的PV只能绑定到请求该类别的PVC nfs: #指定NFS共享目录和IP信息 path: /backup/v1 server: 172.27.9.181---apiVersion: v1kind: PersistentVolumemetadata: name: pv002spec: capacity: storage: 2Gi #指定PV容量为2G volumeMode: Filesystem #卷模式,默认为Filesystem,也可设置为'Block'表示支持原始块设备 accessModes: - ReadOnlyMany #访问模式,该卷可以被多个节点以只读模式挂载 persistentVolumeReclaimPolicy: Retain #回收策略,Retain(保留),表示手动回收 storageClassName: nfs #类名,PV可以具有一个类,一个特定类别的PV只能绑定到请求该类别的PVC nfs: #指定NFS共享目录和IP信息 path: /backup/v2 server: 172.27.9.181---apiVersion: v1kind: PersistentVolumemetadata: name: pv003spec: capacity: storage: 1Gi #指定PV容量为1G volumeMode: Filesystem #卷模式,默认为Filesystem,也可设置为'Block'表示支持原始块设备 accessModes: - ReadWriteOnce #访问模式,该卷可以被单个节点以读/写模式挂载 persistentVolumeReclaimPolicy: Retain #回收策略,Retain(保留),表示手动回收 storageClassName: nfs #类名,PV可以具有一个类,一个特定类别的PV只能绑定到请求该类别的PVC nfs: #指定NFS共享目录和IP信息 path: /backup/v3 server: 172.27.9.181[root@master ~]# kubectl apply -f pv-nfs.yaml persistentvolume/pv001 createdpersistentvolume/pv002 createdpersistentvolume/pv003 created[root@master ~]# kubectl get pvNAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLA*S REASON AGEpv001 2Gi RWO Retain Available nfs 26spv002 2Gi ROX Retain Available nfs 26spv003 1Gi RWO Retain Available nfs 26s
创建pv001、pv002、pv003,分别对应nfs的共享目录/backup/v1、/backup/v2、/backup/v2。
卷可以处于以下的某种状态:
- Available(可用),一块空闲资源还没有被任何声明绑定
- Bound(已绑定),卷已经被声明绑定
- Released(已释放),声明被删除,但是资源还未被集群重新声明
- Failed(失败),该卷的自动回收失败
PV的访问模式有三种:
- 第一种,ReadWriteOnce:是最基本的方式,可读可写,但只支持被单个Pod挂载。
- 第二种,ReadOnlyMany:可以以只读的方式被多个Pod挂载。
- 第三种,ReadWriteMany:这种存储可以以读写的方式被多个Pod共享。不是每一种存储都支持这三种方式,像共享方式,目前支持的还比较少,比较常用的是NFS。
PV不属于任何命名空间, 它跟节点一样是集群层面的资源,区别于pod和PVC。
3. 创建PVC
3.1 PVC创建
[root@master ~]# more pvc-nfs.yaml kind: PersistentVolumeClaimapiVersion: v1metadata: name: mypvc #声明的名称,当做pod的卷使用时会用到 spec: accessModes: - ReadWriteOnce #访问卷模式,筛选PV条件之一 volumeMode: Filesystem #卷模式,与PV保持一致,指示将卷作为文件系统或块设备使用 resources: #声明可以请求特定数量的资源,筛选PV条件之一 requests: storage: 2Gi storageClassName: nfs #请求特定的类,与PV保持一致,否则无法完成绑定[root@master ~]# kubectl apply -f pvc-nfs.yaml persistentvolumeclaim/mypvc created[root@master ~]# kubectl get pvcNAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGEmypvc Bound pv001 2Gi RWO nfs 22s
创建PVC mypvc,访问卷模式为ReadWriteOnce,大小为2G;WO、ROX、RWX、RWO表示可以同时使用卷的工作节点的数量而并非pod的数量。
3.2 查看选中的PV
PVC筛选条件:
PV | accessModes | storage |
---|---|---|
pv001 | √ | √ |
pv002 | × | √ |
pv003 | √ | × |
PV查看:
[root@master ~]# kubectl get pvNAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLA*S REASON AGEpv001 2Gi RWO Retain Bound default/mypvc nfs 12mpv002 2Gi ROX Retain Available nfs 12mpv003 1Gi RWO Retain Available nfs 12m
pv001被选中,符合PVC定义,pv002访问模式不匹配,pv003大小不匹配。
4. pod中使用PVC
[root@master ~]# more mongodb-pod-pvc.yaml apiVersion: v1kind: Podmetadata: name: mongodb-pvc spec: containers: - image: mongo name: mongodb volumeMounts: - name: pvc-data mountPath: /data/db ports: - containerPort: 27017 protocol: TCP volumes: - name: pvc-data persistentVolumeClaim: claimName: mypvc #与pvc中声明的name保持一致[root@master ~]# kubectl apply -f mongodb-pod-pvc.yaml pod/mongodb-pvc created[root@master ~]# kubectl get po -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESmongodb-pvc 1/1 Running 0 16s 10.244.2.144 node02
创建pod mongodb-pvc,使用PVC mypvc,测试同四-4中的nfs共享存储测试,不再赘述。
本文所有脚本和配置文件已上传:k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)
我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=1xq751tgzgk0m