k8s mysql volume_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.22C2Gnode节点node02Centos 7.6.1810
环境说明:
主机名
操作系统版本
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服务器
一、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
创建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 443/TCP 3d17h
my-service NodePort 10.102.191.57 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
hostpath-nginx 1/1 Running 0 16s 10.244.1.140 node01
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服务器搭建和客户端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
注意此时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
删除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
创建pod mongodb-pvc,使用PVC mypvc,测试同四-4中的nfs共享存储测试,不再赘述。
更多推荐
所有评论(0)