前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >k8s实践(七):存储卷和数据持久化(Volumes and Persistent Storage)

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

作者头像
loong576
修改2019-10-23 11:17:22
4.6K0
修改2019-10-23 11:17:22
举报
文章被收录于专栏:运维ABC运维ABC

环境说明:

主机名

操作系统版本

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

代码语言:txt
复制
[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

代码语言:txt
复制
[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脚本:

代码语言:txt
复制
[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

代码语言:txt
复制
[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访问

代码语言:txt
复制
[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上分别执行如下操作

代码语言:txt
复制
[root@master ~]# mkdir /data && cd /data && echo `hostname` > index.html

2.2 创建pod

代码语言:txt
复制
[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

代码语言:txt
复制
[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服务是否正常

代码语言:txt
复制
[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

代码语言:txt
复制
[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写入数据

代码语言:txt
复制
[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 查看写入的数据

代码语言:txt
复制
> db.foo.find()
{ "_id" : ObjectId("5d6e17b018651a21e0063641"), "name" : "loong576" }

4.3 删除pod并重建

代码语言:txt
复制
[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读取共享存储数据

代码语言:txt
复制
[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服务器共享目录配置:

代码语言:txt
复制
[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配置:

代码语言:txt
复制
[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创建

代码语言:txt
复制
[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创建

代码语言:txt
复制
[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查看:

代码语言:txt
复制
[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

代码语言:txt
复制
[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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-09-03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Volume
    • 1. 概念
      • 2. 为什么需要Volume
        • 3. Volume类型
    • 二、emptyDir
      • 1. emptyDir概念
        • 2. 创建pod emptyDir-fortune
          • 2.1 loong576/fortune镜像
        • 3. 访问nginx
          • 3.1 创建service
          • 3.2 nginx访问
      • 三、hostPath
        • 1. 概念
          • 2. 创建pod hostpath-nginx
            • 2.1 创建挂载目录
            • 2.2 创建pod
          • 3. 访问pod hostpath-nginx
          • 四、NFS共享存储
            • 1. 概念
              • 2. nfs搭建及配置
                • 3. 新建pod mongodb-nfs
                  • 4. nfs共享存储测试
                    • 4.1 向MongoDB写入数据
                    • 4.2 查看写入的数据
                    • 4.3 删除pod并重建
                    • 4.4 新pod读取共享存储数据
                • 五、PV and PVC
                  • 1. 概念
                    • 2. 创建PV
                      • 2.1 nfs配置
                    • 2.2 PV创建
                      • 3. 创建PVC
                        • 3.1 PVC创建
                        • 3.2 查看选中的PV
                      • 4. pod中使用PVC
                      相关产品与服务
                      容器服务
                      腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档