k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)
环境说明:主机名操作系统版本ipdocker versionkubelet version配置备注masterCentos 7.6.1810172.27.9.131Docker 18.09.6V1.14.22C2Gmaster主机node01Centos 7.6.1810172.27.9.135Docker 18.09.6V1.14.22C...
环境说明:
主机名 | 操作系统版本 | 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命令和资料分享
一、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: v1
kind: Pod
metadata:
labels:
app: prod #pod标签
name: emptydir-fortune
spec:
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 GATES
emptydir-fortune 2/2 Running 0 9s 10.244.2.140 node02 <none> <none>
创建pod emptydir-fortune,该pod有两个容器,同时挂载emptyDir卷,容器html-generator向卷中写入随机内容,通过访问容器web-server验证是否实现文件的共享。
2.1 loong576/fortune镜像
root@master ~]# more Dockerfile
[root@master ~]# more fortune/Dockerfile
FROM ubuntu:latest
RUN apt-get update ; apt-get -y install fortune
ADD fortuneloop.sh /bin/fortuneloop.sh
E*TRYPOINT /bin/fortuneloop.sh
该镜像的base镜像为ubuntu,镜像启动时会执行fortuneloop.sh脚本
fortuneloop.sh脚本:
[root@master ~]# more fortuneloop.sh
#!/bin/bash
trap "exit" SIGINT
mkdir /var/htdocs
while :
do
echo $(date) Writing fortune to /var/htdocs/index.html
/usr/games/fortune > /var/htdocs/index.html
sleep 10
done
该脚本主要是每10秒钟输出随机短语至index.html文件中。
3. 访问nginx
3.1 创建service
[root@master ~]# more service-fortune.yaml
apiVersion: v1
kind: Service
metadata:
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) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d17h
my-service NodePort 10.102.191.57 <none> 8881:30002/TCP 9s
3.2 nginx访问
[root@master ~]# curl 10.102.191.57:8881
Writing is easy; all you do is sit staring at the blank sheet of paper until
drops of blood form on your forehead.
-- Gene Fowler
[root@master ~]# curl 172.27.9.135:30002
Don'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: v1
kind: Pod
metadata:
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 wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
emptydir-fortune 2/2 Running 0 40m 10.244.2.140 node02 <none> <none>
hostpath-nginx 1/1 Running 0 16s 10.244.1.140 node01 <none> <none>
3. 访问pod hostpath-nginx
[root@master ~]# curl 10.244.1.140
node01
结论:
-
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.181
Export 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: v1
kind: Pod
metadata:
name: mongodb-nfs
spec:
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 GATES
mongodb-nfs 1/1 Running 0 23s 10.244.2.142 node02 <none> <none>
注意此时pod的ip为10.244.2.142
4. nfs共享存储测试
4.1 向MongoDB写入数据
[root@master ~]# kubectl exec -it mongodb-nfs mongo
> use loong
switched 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.yaml
pod/mongodb-nfs created
[root@master ~]# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mongodb-nfs 1/1 Running 0 22s 10.244.2.143 node02 <none> <none>
删除pod mongodb-nfs并重建,此时podip变为10.244.2.143,再次访问MongoDB验证之前写入的文档是否还存在。
4.4 新pod读取共享存储数据
[root@master ~]# kubectl exec -it mongodb-nfs mongo
> use loong
switched 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.181
Export 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: v1
kind: PersistentVolume
metadata:
name: pv001
spec:
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: v1
kind: PersistentVolume
metadata:
name: pv002
spec:
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: v1
kind: PersistentVolume
metadata:
name: pv003
spec:
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 created
persistentvolume/pv002 created
persistentvolume/pv003 created
[root@master ~]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLA*S REASON AGE
pv001 2Gi RWO Retain Available nfs 26s
pv002 2Gi ROX Retain Available nfs 26s
pv003 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: PersistentVolumeClaim
apiVersion: v1
metadata:
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 pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mypvc 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 pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLA*S REASON AGE
pv001 2Gi RWO Retain Bound default/mypvc nfs 12m
pv002 2Gi ROX Retain Available nfs 12m
pv003 1Gi RWO Retain Available nfs 12m
pv001被选中,符合PVC定义,pv002访问模式不匹配,pv003大小不匹配。
4. pod中使用PVC
[root@master ~]# more mongodb-pod-pvc.yaml
apiVersion: v1
kind: Pod
metadata:
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 GATES
mongodb-pvc 1/1 Running 0 16s 10.244.2.144 node02 <none> <none>
创建pod mongodb-pvc,使用PVC mypvc,测试同四-4中的nfs共享存储测试,不再赘述。
本文所有脚本和配置文件已上传:k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)
我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=1xq751tgzgk0m
更多推荐
所有评论(0)