环境说明:

主机名操作系统版本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.1810172.27.9.136Docker 18.09.6V1.14.22C2Gnode节点
centos7Centos 7.3.1611172.27.9.181××1C1Gnfs服务器

 

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 类型:

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

本文将对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中两个容器之间的文件共享

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

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>

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

创建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

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

结论:

  • 容器nginx成功的读取到了容器fortune写入存储的内容,emptyDir卷可以实现容器间的文件共享。

  • emptyDir卷的生存周期与pod的生存周期相关联,所以当删除pod时,卷的内容就会丢失

三、hostPath

1. 概念

  hostPath允许挂载Node上的文件系统到Pod里面去。如果Pod需要使用Node上的文件,可以使用hostPath。在同一个节点上运行并在其hostPath卷中使用相同路径的pod可以看到相同的文件。

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

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

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

结论:

  • 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服务器对外提供的共享目录。

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

本文测试的NFS内容:

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

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

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

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" }

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

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" }

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

即使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绑定来释放。

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

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。

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

卷可以处于以下的某种状态:

  • Available(可用),一块空闲资源还没有被任何声明绑定
  • Bound(已绑定),卷已经被声明绑定
  • Released(已释放),声明被删除,但是资源还未被集群重新声明
  • Failed(失败),该卷的自动回收失败

 

PV的访问模式有三种:

  • 第一种,ReadWriteOnce:是最基本的方式,可读可写,但只支持被单个Pod挂载。
  • 第二种,ReadOnlyMany:可以以只读的方式被多个Pod挂载。
  • 第三种,ReadWriteMany:这种存储可以以读写的方式被多个Pod共享。不是每一种存储都支持这三种方式,像共享方式,目前支持的还比较少,比较常用的是NFS。

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

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的数量。

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

3.2 查看选中的PV

PVC筛选条件:

PVaccessModesstorage
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

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

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>

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

创建pod mongodb-pvc,使用PVC mypvc,测试同四-4中的nfs共享存储测试,不再赘述。

k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

 
 

本文所有脚本和配置文件已上传:k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

 
 

我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=1xq751tgzgk0m

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐