侧边栏壁纸
博主头像
小周的个人博客 博主等级

行动起来,活在当下

  • 累计撰写 23 篇文章
  • 累计创建 9 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

K8S数据存储

Administrator
2024-06-05 / 0 评论 / 0 点赞 / 0 阅读 / 0 字

由于容器的生命周期不稳定,可能随时被创建与销毁,如果容器被销毁后,在容器中产生的数据也会被清除,如果需要对容器内的数据实现持久化保存,我们需要将容器内的数据与pod分离,将数据放在专门的存储卷上。

K8s支持的存储类型参考地址:https://kubernetes.io/zh-cn/docs/concepts/storage/volumes/

本地存储 EmptyDir

参考地址:https://kubernetes.io/zh-cn/docs/concepts/storage/volumes/#emptydir

EmptyDir是最基础的存储类型,一个EmptyDir就是主机上的一个空目录,该目录是在Pod被分配到Node节点时创建的,它的初始内容为空,当Pod销毁时,EmptyDir中的数据也会被删除。

案例:创建一个nginx的Pod并使用emptydir作为数据的临时存储

# cat deploy-nginx.yml
​
apiVersion: apps/v1
kind: Deployment
metadata:
    name: deploy-nginx
    namespace: test
​
spec:
    selector:
      matchLabels:
        app: deploy-nginx
​
    template:
      metadata:
        labels:
          app: deploy-nginx
​
      spec:
        volumes:                        #volume存储卷
        - name: nginx-logs              #volume卷名称
          emptyDir: {}                  #类型为emptyDir,{}表示空目录
          
        containers:
        - name: nginx
          image: nginx:1.17.0
        
          volumeMounts:
          - name: nginx-logs            #挂载的volume名称,与上边定义的保持一致
            mountPath: /var/log/nginx   #挂载到容器的目录
#创建Pod
kubectl create -f emptydir_nginx.yml
​
#查看Pod信息
kubectl get pod -n test -o wide
​
#可以通过find命令在Pod所在节点中搜索存储卷目录的所在位置
find /var/lib/kubelet/pods/ -name nginx-logs
​
#删除Pod
kubectl delete -f emptydir_nginx.yml
​
#再次查看存储卷是否存在
find /var/lib/kubelet/pods/ -name nginx-logs

总结:EmptyDir存储方式不会永久保存数据,应为EmptyDir的生命周期是跟据Pod的生命周期是一样的,它会随着Pod的结束而销毁,如果想简单的将数据持久化到集群主机中,可以选择用HostPath。

本地存储 HostPath

参考地址:https://kubernetes.io/zh-cn/docs/concepts/storage/volumes/#hostpath

HostPath是将Node节点中一个实际目录挂载到Pod中,以供容器使用,这样的设计可以保证Pod销毁了,但是数据仍然可以保存在宿主机上,实现数据永久保存。

案例:创建nginx,并通过hostPath存储卷方式对nginx实现数据持久化保存。

# cat hostpath_nginx.yml
​
apiVersion: apps/v1
kind: Deployment
metadata:
    name: deploy-nginx
    namespace: test
​
spec:
    selector:
      matchLabels:
        app: deploy-nginx
​
    template:
      metadata:
        labels:
          app: deploy-nginx
​
​
      spec:
        volumes:                      #volume存储卷
        - name: nginx-html            #volume名称
          hostPath:                   #类型hostPath
            type: DirectoryOrCreate   #目录存在就使用,不存在就先创建后使用
            path: /nginx/html         #指定hostPath的目录(存放页面)
​
        - name: nginx-logs            #volume名称
          hostPath:                   #类型hostPath
            type: DirectoryOrCreate   #目录存在就使用,不存在就先创建后使用
            path: /nginx/logs         #指定hostPath的目录(存放日志)
​
​
        containers:
        - name: nginx
          image: nginx:1.17.0
          ports:
          - containerPort: 80
​
          volumeMounts:
          - name: nginx-html                  #挂载的volume名称,与上边定义的保持一致
            mountPath: /usr/share/nginx/html  #挂载到容器的目录
​
          - name: nginx-logs                  #挂载的volume名称,与上边定义的保持一致
            mountPath: /var/log/nginx         #挂载到容器的目录

关于 hostPath的type可用值说明:

  • DirectoryOrCreate //目录存在就使用,不存在就先创建后使用

  • Directory //目录必须存在

  • FileOrcreate //文件存在就使用,不存在就先创建后使用

  • File //文件必须存在

#创建Pod
kubectl create -f hostpath_nginx.yml
​
#查看Pod信息
kubectl get pod -n test -o wide
​
#查看Pod所在节点的hostPath目录
ls /nginx
​
#创建index.html文件进行访问测试
echo hello > /nginx/html/index.html
​
#通过Pod访问
curl PodIP
​
#删除Pod验证数据持久化
kubectl delete -f hostpath_nginx.yml

总结:HostPath可以解决数据持久化的问题,但是一旦Node节点故障了,那么数据卷并不会转移到其他的节点,为了解决以上的问题,可以使用网络文件存储系统,比较常见的有NFS、glusterfs等。

网络存储 NFS

参考地址:https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/#nfs

可以搭建NFS服务器,然后将Pod中的存储直接连到NFS系统上,这样无论Pod在节点上怎么转移,只要该节点跟NFS对接没问题,数据就可以成功访问。

单独准备一台服务器部署NFS

#安装nfs-utils
[root@k8s-nfs01 ~]# yum -y install nfs-utils
​
#创建共享目录
[root@k8s-nfs01 ~]# mkdir -p /k8s-nfs/nginx/html
[root@k8s-nfs01 ~]# mkdir  /k8s-nfs/nginx/logs
​
#共享目录
[root@k8s-nfs01 ~]# vim /etc/exports
/k8s-nfs/nginx/html 192.168.0.0/24(rw,no_root_squash)
/k8s-nfs/nginx/logs 192.168.0.0/24(rw,no_root_squash)
​
#启动nfs服务&&设置服务随机自启
[root@k8s-nfs01 ~]# systemctl start nfs && systemctl enable nfs

部署NFS客户端

在集群节点上都安装nfs客户端工具,不需要启动服务,这样节点才可以驱动nfs设备

yum -y install nfs-utils
​
#查看共享资源
# showmount -e 192.168.0.15
Export list for 192.168.0.15:
/k8s-nfs/nginx/logs 192.168.0.0/24
/k8s-nfs/nginx/html 192.168.0.0/24

案例:创建Pod,并通过NFS存储卷方式对nginx实现数据持久化保存。

# cat nfs_nginx.yml
​
apiVersion: apps/v1
kind: Deployment
metadata:
    name: deploy-nginx
    namespace: test
​
spec:
    selector:
      matchLabels:
        app: deploy-nginx
​
    template:
      metadata:
        labels:
          app: deploy-nginx
​
​
      spec:
        volumes:                          #volume存储卷
        - name: volume-ngx-html           #volume名称
          nfs:                            #类型nfs
            server: 192.168.0.15          #NFS服务器地址
            path: /k8s-nfs/nginx/html     #NFS共享目录
​
        - name: volume-ngx-logs           #定义volume名称
          nfs:                            #类型nfs
            server: 192.168.0.15          #NFS服务器地址
            path: /k8s-nfs/nginx/logs     #NFS共享目录
​
​
        containers:
        - name: nginx
          image: nginx:1.18.0
          ports:
          - containerPort: 80
​
          volumeMounts:
          - name: volume-ngx-html             #挂载的volume名称,与上边定义的保持一致
            mountPath: /usr/share/nginx/html  #挂载到容器的目录
​
          - name: volume-ngx-logs             #挂载的volume名称,与上边定义的保持一致
            mountPath: /var/log/nginx         #挂载到容器的目录
#创建Pod
kubectl create -f nfs_nginx.yml
​
#查看Pod详细信息
kubectl describe pod nfs-nginx-5c8699644d-mv6bl -n test
...
    Mounts:
      /usr/share/nginx/html from volume-html (rw)
      /var/log/nginx from volume-logs (rw)
...
Volumes:
  volume-html:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    192.168.0.15
    Path:      /k8s-nfs/nginx/html
    ReadOnly:  false
  volume-logs:
    Type:      NFS (an NFS mount that lasts the lifetime of a pod)
    Server:    192.168.0.15
    Path:      /k8s-nfs/nginx/logs
    ReadOnly:  false
#在nfs主机查看共享目录
[root@k8s-nfs01 ~]# ls /k8s-nfs/nginx/logs/
access.log  error.log
​
#删除Pod
[root@master01 ~]# kubectl delete -f nfs_nginx.yml
​
#验证数据持久化
[root@k8s-nfs01 ~]# ls /k8s-nfs/nginx/logs/
access.log  error.log

数据存储 PV与PVC介绍

参考地址:https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-persistent-volume-storage/

经过前面的NFS存储已经可以实现数据的持久化保存,而k8s为了能够屏蔽底层的存储细节,方便用户使用,k8s引入了PV和PVC的存储方式。

PV(Persistent Volume 存储卷)

PV是k8s里面的一个概念,它本身不是存储,只不过是通过创建PV的资源文件来指定后端网络存储的地址,PV后端需要通过NFS、ISCSI、Ceph和云存储等,来提供真正的存储空间。

PVC(Persistent Volume Claim 存储卷声明)

可以理解为Pod对存储空间的申请方式,也就是说PVC为Pod寻找一个合适的PV进行绑定,绑定之后就可以使用PV后端提供的存储资源。

案例:使用NFS作为后端存储,创建一个4G 的 PV 存储卷。

#nfs准备共享目录
[root@k8s-nfs01 ~]# mkdir -p /k8s-nfs/pv01-mysql
​
#共享目录
[root@k8s-nfs01 ~]# vim /etc/exports
/k8s-nfs/pv01-mysql 192.168.0.0/24(rw,no_root_squash)
​
#重启nfs服务
[root@k8s-nfs01 ~]# systemctl restart nfs
#创建PV,PV是全局资源,无需指定命名空间
cat pv01-mysql.yml
​
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv01-mysql
​
spec:
  capacity:                     #存储能力,目前只支持存储空间的设置
    storage: 4Gi                #存储空间
  accessModes:                  #访问权限
  - ReadWriteMany               #读、写、执行,可以被多个节点挂载
  persistentVolumeReclaimPolicy: Recycle  #回收策略
  nfs:                          #存储类型
    path: /k8s-nfs/pv01-mysql   #挂载路径
    server: 192.168.0.15        #nfs服务器地址

accessModes(访问权限)包含以下几种方式:

  • ReadWriteOnce(RWO) //读、写,但是只能被一个节点挂载

  • ReadOnlyMany(ROX) //读、执行,可以被多个节点挂载

  • ReadWriteMany(RWX) //读、写、执行,可以被多个节点挂载

注意事项:底层不同的存储类型可能支持的访问模式也不同。

persistentVolumeReclaimPolicy(回收策略)当PV不再被PVC使用之后,对PV内的数据处理方式:

  • Retain(保留) //保留数据

  • Recycle(回收) //删除PV中的数据

  • Delete(删除) //与PV相连的后端存储完成volume的删除操作,当然这常见于云服务商的存储服务

注意事项:底层不同的存储类型可能支持的回收策略也不同

#创建pv
kubectl create -f pv01-mysql.yml
​
#查看pv信息
kubectl get pv

一个PV的生命周期中,会处于4种不同的阶段:

  • Avallable(可用) //表示可用状态,还未被任何的PVC绑定

  • Bound(已绑定) //表示PV已经被PVC绑定

  • Released(已释放) //表示PVC被删除,但是资源还未被集群重新分配

  • Falled(失败) //表示该PV的自动回收失败

案例:创建一个PVC,并绑定pv01-mysql存储卷

#PVC是局部资源,需要指定名称空间
cat pvc01-mysql.yml
​
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc01-mysql
  namespace: test
​
spec:
  accessModes:       #访问模式,访问模式要与PV模式相同,否则绑定不上
  - ReadWriteMany    #读写执行权限,可以被多个节点挂载
  resources:         #请求
    requests:        #资源
      storage: 4Gi   #资源大小,在绑定PV时,如果超出PV空间范围,则无法绑定
#创建pvc
kubectl create -f pvc01-mysql.yml
​
#查看pvc信息
kubectl get pvc -n test
​
#查看pv信息
kubectl get pv

案例:创建MySQL并挂载PVC实现数据持久化。

# cat mysql.yml
​
apiVersion: apps/v1
kind: Deployment
metadata:
    name: deploy-mysql
    namespace: test
​
spec:
    selector:
      matchLabels:
        app: deploy-mysql
​
    template:
      metadata:
        labels:
          app: deploy-mysql
​
      spec:
        volumes:                        #volume存储卷
        - name: mysql-data              #volume名称
          persistentVolumeClaim:        #类型为PVC
            claimName: pvc01-mysql      #PVC名称(与前边创建的PVC名称一致)
            readOnly: false             #访问模式,false为读写,true为只读
​
        containers:
        - name: mysql
          image: mysql:5.7
          ports:
          - containerPort: 3306
          env:                           #定义环境变量
          - name: "MYSQL_ROOT_PASSWORD"  #变量名称
            value: "123456"              #MySQL的root密码
​
          volumeMounts:
          - name: mysql-data           #挂载的volume名称,与上边定义的保持一致
            mountPath: /var/lib/mysql  #挂载到容器的目录
#创建pod
kubectl create -f pv-pvc_mysql.yml
​
#查看pod信息
kubectl get po -n test
​
#查看NFS共享存储路径
ls /k8s-nfs/pv01-mysql/
​
#删除pod、pvc、pv
kubectl delete -f pv01-mysql.yml
kubectl delete -f pvc01-mysql.yml
kubectl delete -f mysql.yml

动态存储StorageClass

为什么需要动态存储?

  • 管理麻烦:上面介绍的PV和PVC模式都是需要先创建好PV,然后定义与PVC一对一的Bind关系,但是如果PVC请求成千上万,那么就需要创建成千上万的PV,非常的麻烦且不方便管理。

  • 资源浪费:手动创建PV无法预判Pod需要使用的容量大小,很容易造成资源浪费。

  • 申请失败:当PVC申请的容量没有合适的PV可以满足,会造成Pod无法正常运行。

k8s提供一种自动创建PV的机制StorageClass,当用户需要申请PVC时(例如10G),StorageClass会根据PVC申请的大小动态生成PV(10G)供PVC使用。

案例:基于NFS作为后端存储,通过StorageClass动态创建PV供PVC使用。

准备NFS后端存储

#创建共享目录
mkdir /storageclass
​
#共享目录
cat /etc/exports
...
/storageclass 192.168.0.0/24(rw,no_root_squash)
​
#重启nfs服务
systemctl restart nfs

使用NFS作为后端存储,需要一个nfs-client-provisioner的自动装载程序,这个程序支持NFS服务器自动创建PV,因为k8s没有为NFS提供自动创建PV的功能。

如果是其他存储类型可以参考https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/#aws-ebs

需要先创建RBAC,nfs-client-provisioner用于监视API-Server获取新的PVC创建请求(监视APIServer需要有权限才行,通过RBAC为nfs-client-provisioner绑定权限)

RBAC参考地址( rbac.yaml 文件):https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner/tree/master/deploy

#通过 :set paste 模式粘贴
cat nfs-storage-rbac.yaml
​
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["nodes"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io
#创建RBAC
kubectl create -f nfs-storage-rbac.yaml

创建nfs-client-provisioner的自动装载程序

参考地址(deployment.yaml 文件):https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner/tree/master/deploy

提示:该文件中的镜像默认无法下载,提前导入

#通过 :set paste 模式粘贴
cat nfs-client-provisioner.yaml
​
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:                           #通过环境变量传递给nfs-provisioner用于找到后端的NFS存储服务器
            - name: PROVISIONER_NAME     #nfs-provisioner变量名称
              value: k8s-sigs.io/nfs-subdir-external-provisioner #值名称自定义,后续StorageClass要与该名称一致
            - name: NFS_SERVER
              value: 192.168.0.15        #传递NFS服务器IP地址
            - name: NFS_PATH
              value: /storageclass       #传递NFS服务器共享路径
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.0.15         #后端NFS服务器IP地址
            path: /storageclass          #后端NFS服务器共享路径
#创建nfs-provisioner
kubectl create -f nfs-client-provisioner.yaml

创建StorageClass动态存储

参考地址(class.yaml 文件):https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner/tree/master/deploy

#通过 :set paste 模式粘贴
cat nfs-storageclass.yaml
​
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-client
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner #存储供应者名称必须和上边创建的PROVISIONER_NAME中的值一致
parameters:                     #向底层存储系统传递配置信息
  archiveOnDelete: "false"      #PV被删除时,false表示数据也会被删除,设定为true数据保留(如果定义下边的pathPattern属性,此参数并不会删除数据)
  pathPattern: "${.PVC.namespace}/${.PVC.name}"  #存储设备的目录名称,默认随机命名

提示:以上配置仅用于底层是NFS,其他存储设备还需要参考地址:https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/

#创建storageclass
kubectl create -f nfs-storageclass.yaml
​
#查看storageclass
kubectl get sc
​
#说明:
NAME                    //存储名称。
PROVISIONER             //该存储用的存储供应商。
RECLAIMPOLICY           //PV回收策略,通常是Delete(删除)或Retain(保留)。
VOLUMEBINDINGMODE       //卷绑定模式,例如Immediate(无论是否有Pod使用该PVC,都会立即与合适的PVC绑定)或WaitForFirstConsumer(直到有Pod使用该PVC时,才会与PV绑定)。
ALLOWVOLUMEEXPANSION    //是否允许对卷进行扩容,false不允许(NFS不支持扩容,云存储和分布式支持)

创建PVC并通过StorageClass动态创建PV

# cat pvc-storageclass.yml
​
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc01-storageclass
  namespace: test
​
spec:
  storageClassName: "nfs-client"    #指定使用的动态存储名称
  accessModes:                      #访问模式
  - ReadWriteMany                   #读写执行权限,可以被多个节点挂载
  resources:                        #请求
    requests:                       #资源
      storage: 6Gi                  #资源大小
#创建PVC
kubectl create -f pvc-storageclass.yml
​
#查看PVC
kubectl get pvc -n test
​
#查看PV
kubectl get pv

创建Pod并使用PVC

# cat nginx-pod.yml
​
apiVersion: apps/v1
kind: Deployment
metadata:
    name: deploy-nginx
    namespace: test
​
spec:
    selector:
      matchLabels:
        app: deploy-nginx
​
    template:
      metadata:
        labels:
          app: deploy-nginx
​
      spec:
        volumes:                            #volume存储卷
        - name: pvc-nginx                   #volume名称
          persistentVolumeClaim:            #类型为PVC
            claimName: pvc01-storageclass   #PVC名称(与前边创建的PVC名称一致)
            readOnly: false                 #访问模式,false为读写,true为只读
​
        containers:
        - name: nginx
          image: nginx:1.20.0
          ports:
          - containerPort: 80
          volumeMounts:
          - name: pvc-nginx             #挂载的volume名称,与上边定义的保持一致
            mountPath: /var/log/nginx   #挂载到容器的目录
#创建Pod
kubectl create -f nginx-pod.yml
​
#查看Pod详细信息
kubectl describe pod -n test
#在NFS主机查看数据
ls /storageclass/test/pvc01-storageclass/
access.log  error.log
#删除Pod
kubectl delete -f nginx-pod.yml
​
#删除PVC(删除PVC后,PV也会自动删除,但是后端存储中的数据不会被删除)
kubectl delete -f pvc-storageclass.yml


0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区