K8S存储总结&持久化存储解决方案(以NFS为例)

目录

K8S存储

一、emptydir

[1. emptydir是什么](#1. emptydir是什么)

[2. emptydir例子](#2. emptydir例子)

二、hostpath

[hostpath 是什么](#hostpath 是什么)

[hostpath 例子](#hostpath 例子)

弊端

三、持久化存储

[1.Persistent Volume(PV)](#1.Persistent Volume(PV))

[2.Persistent Volume Claim(PVC)](#2.Persistent Volume Claim(PVC))

3.PV/PVC结合NFS使用实践(持久存储)

[1. NFS服务器搭建](#1. NFS服务器搭建)

[(1)搭建 NFS服务器](#(1)搭建 NFS服务器)

[3. 验证PV-PVC-NFS存储结果](#3. 验证PV-PVC-NFS存储结果)

(1)pod中创建文件,NFS服务器查看文件

[(2) NFS数据存储目录里创建文件,在Pod中查看文件](#(2) NFS数据存储目录里创建文件,在Pod中查看文件)

(3)删除Pod,NFS服务器查看文件

4.回收PV

4、StorageClass

[1.1 什么是StorageClass](#1.1 什么是StorageClass)

[1.2 为什么需要StorageClass](#1.2 为什么需要StorageClass)

[1.3 运行原理](#1.3 运行原理)

[2. StorageClass 资源](#2. StorageClass 资源)

[2.1 存储制备器](#2.1 存储制备器)

[3.NFS StorageClass 实战](#3.NFS StorageClass 实战)

[3.1 部署流程](#3.1 部署流程)

[3.2 创建Service Account](#3.2 创建Service Account)

[3.3 创建StorageClass,指定provisioner](#3.3 创建StorageClass,指定provisioner)

[3.4 创建pod测试](#3.4 创建pod测试)

K8S存储

一、emptydir

1. emptydir是什么

当pod的存储方案设定为emptydir的时候,pod启动时,就会在pod所在节点的磁盘空间开辟出一块空卷,最开始里面是什么都没有的,pod启动后容器产生的数据会存放到那个空卷中。

一旦pod容器消失,节点上开辟出的这个临时卷就会随着pod的销毁而销毁

一般来说emptydir的用途都是用来充当临时存储空间,例如一些不需要数据持久化的微服务,我们都可以用emptydir来当做微服务pod的存储方案

2. emptydir例子

# emptydir.yaml
---
apiVersion: v1
kind: Namespace
metadata:
  name: dev
---
apiVersion: v1
kind: Pod
metadata:
  name: test-emptydir
  namespace: dev
spec:
  containers:
  - image: nginx:1.20.0
    name: test-emptydir
    volumeMounts:
    - mountPath: /usr/share/nginx/html    #挂载到容器中的路径
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}   #指定存储方式为emptydir

验证emptydir

1.在调度节点g1-mix-online2-222上操作

docker ps -a | grep empty

2.查看容器详细信息

 docker inspect c3c0fa73aa9b

3.进入宿主机挂载路径并写入文件

4.测试访问

curl 10.14.57.123

5.删除pod,查看文件是否被删除

emptydir是随pod创建而创建,然后再随pod删除而删除

二、hostpath

hostpath 是什么

hostPath类型是映射宿主机文件系统中的文件或者目录到pod里。但当pod漂移到其他node节点的时候,pod不会跨节点的去读取目录。所以hostpath只能算一种半持久化的存储方式

hostpath 例子

#  hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-hostpath
  namespace: dev
spec:
  containers:
  - name: test-hostpath
    image: nginx:1.20.0
    volumeMounts:
    - name: test-hostpath  # 取个名字
      mountPath: /usr/share/nginx/html   # 挂载到容器中的路径
  volumes:
  - name: test-hostpath    # 取个名字必须与上面volumeMounts中name的一致
    hostPath:
    # directory location on host
      path: /data            # node节点上宿主机的路径
    # this field is optional
      type: DirectoryOrCreate   # path的属性

接下来就是验证:g1-mix-online2-222节点和pod内目录是否进行了关联

宿主机

1.data目录已经自动创建

2.进入data文件夹,并写入测试文件

echo "cat" > /data/index.html 

3.访问测试

查看 Pod内部:

1.查找容器

docker ps -a |  grep test-hostpath

2.查看容器详细信息

docker inspect 3e5431569426

3.进入容器查看文件是否存在

docker exec -it 3e5431569426  /bin/bash

接下来,删除Pod,重新拉起一个Pod,看会不会还存在这个index.html文件

此时,宿主机上的文件仍然存在

重新拉起pod,pod调度到g1-mix-online2-222节点

进入容器查看文件是否存在,可见文件仍然存在

弊端

1.宿主机宕机会导致数据全部丢失

2.pod重新调度时,未调度到上次调度的节点,而是调度到新的节点,也会导致数据丢失

三、持久化存储

1.Persistent Volume(PV)

PV 描述的,是持久化存储数据卷。这个 API 对象主要定义的是一个持久化存储在宿主机上的目录,比如一个 NFS 的挂载目录。

通常情况下,PV 对象是由运维人员事先创建在 Kubernetes 集群里待用的。比如,运维人员可以定义这样一个 NFS 类型的 PV,如下所示:

apiVersion: v1
kind: PersistentVolume  # pv可以全局共享,不用指定命名空间
metadata:
  name: nfs
spec:
  storageClassName: manual 
  capacity:   # 容量
    storage: 1Gi   # pv可用的大小   
  accessModes:   # 访问模式
    - ReadWriteMany  #读写权限
  nfs:
    server: 10.244.1.4 # NFS服务器地址
    path: "/" # NFS的路径  

2.Persistent Volume Claim(PVC)

PVC 描述的,则是 Pod 所希望使用的持久化存储的属性。比如,Volume 存储的大小、可读写权限等等。

PVC 对象通常由开发人员创建,比如,开发人员可以声明一个 1 GiB 大小的 PVC

apiVersion: v1
kind: PersistentVolumeClaim #需与使用pvc的pod在同一个namespace下
metadata:
  name: nfs
spec:
  accessModes: # 访问模式
    - ReadWriteMany   #读写权限
  storageClassName: manual
  resources:
    requests:
      storage: 1Gi # PVC允许申请的大小

而用户创建的 PVC 要真正被容器使用起来,就必须先和某个符合条件的 PV 进行绑定。

这里要检查的条件,包括两部分:

第一个条件,是 PV 和 PVC 的 spec 字段。比如,PV 的存储(storage)大小,就必须满足 PVC 的要求。

第二个条件,是 PV 和 PVC 的 storageClassName 字段需要相同。

PS: 也可以不使用storageClassName字段,通过PVC定义的 accessModes 读写权限,和storage定义的1G内存,PVC会自动找到符合这些配置的PV进行绑定。一个PV被PVC绑定后,不能被别的PVC绑定。

在成功地将 PVC 和 PV 进行绑定之后,Pod 就能够像使用 hostPath 等常规类型的 Volume 一样,在自己的 YAML 文件里声明使用这个 PVC 了,如下所示:

apiVersion: v1
kind: Pod   # 如果前面的PVC指定了命名空间这里必须指定与PVC一致的命名空间,否则PVC不可用
metadata:
  labels:
    role: web-frontend
spec:
  containers:
  - name: web
    image: nginx
    ports:
      - name: web
        containerPort: 80
    volumeMounts:
        - name: nfs  # 取个名字,与下面的volumes name 要一致
          mountPath: "/usr/share/nginx/html" # 容器中的路径
  volumes:
  - name: nfs #
    persistentVolumeClaim: 
      claimName: nfs   # 引用前面声明的PVC

3.PV/PVC结合NFS使用实践(持久存储)

简单来说,要使用持久化存储,就需要使用pvc去跟pv去申请,然后pv查看自己有没有合适的存储空间卷,有合适的就与pvc进行绑定。pv与pvc是一一对应绑定的。

创建顺序:后端存储---pv---pvc---pod

1. NFS服务器搭建

NFS 是什么? nfs(network file system) 网络文件系统,是FreeBSD支持的文件系统中的一种,允许网络中的计算机之间通过TCP/IP网络共享资源

(1)搭建 NFS服务器

找一台centos 7机器,执行以下脚本,搭建 NFS服务器:

# 操作节点为NFS服务器

# 安装nfs
yum -y install nfs-utils rpcbind

# 创建nfs目录
mkdir -p /nfs/data/
mkdir -p /nfs/data/k8s

# 授予权限
chmod -R 777 /nfs/data

# 编辑export文件
vim /etc/exports
/nfs/data *(rw,no_root_squash,sync)  # 这里给的是root权限---生产环境不推荐
# 或者/nfs/data   0.0.0.0/0(rw,sync,all_squash)  # 所有用户权限被映射成服务端上的普通用户nobody,权限被压缩

# 使得配置生效
exportfs -r

# 查看生效
exportfs

# 启动rpcbind、nfs服务
systemctl start rpcbind && systemctl enable rpcbind   #端口是111
systemctl start nfs && systemctl enable nfs           # 端口是 2049 

# 查看rpc服务的注册情况
rpcinfo -p localhost

# showmount测试
showmount -e ip(ip地址)

验证nfs是否启动

(2)在K8S集群所有node节点上安装NFS客户端

# 操作节点为k8s集群所有node节点
yum -y install nfs-utils  rpcbind
# systemctl start nfs && systemctl enable nfs
  1. 创建pv、pvc、pod

    vim nginx-pv-demo.yaml

    定义PV


    apiVersion: v1
    kind: PersistentVolume
    metadata:
    name: nginx-pv # pv的名称
    spec:
    accessModes: # 访问模式
    - ReadWriteMany # PV以read-write挂载到多个节点
    capacity: # 容量
    storage: 2Gi # pv可用的大小
    nfs:
    path: /nfs/data/ # NFS的挂载路径
    server: 10.16.216.221 # NFS服务器地址

    定义PVC,用于消费PV

    通过PVC定义的 accessModes 读写权限,和storage定义的2G内存,PVC会自动找到符合这些配置的PV进行绑定。一个PV被PVC绑定后,不能被别的PVC绑定。

    apiVersion: v1
    kind: PersistentVolumeClaim # 类型
    metadata:
    name: nginx-pvc # PVC 的名字
    namespace: dev # 命名空间
    spec:
    accessModes: # 访问模式
    - ReadWriteMany # PVC以read-write挂载到多个节点
    resources:
    requests:
    storage: 2Gi # PVC允许申请的大小

    定义Pod,指定需要使用的PVC

    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: nginx-pvc
    namespace: dev # 如果前面的PVC指定了命名空间这里必须指定与PVC一致的命名空间,否则PVC不可用
    spec:
    selector:
    matchLabels:
    app: nginx-pvc
    template:
    metadata:
    labels:
    app: nginx-pvc
    spec:
    containers:
    - name: nginx-test-pvc
    image: nginx:1.20.0
    imagePullPolicy: IfNotPresent
    ports:
    - name: web-port
    containerPort: 80
    protocol: TCP
    volumeMounts:
    - name: nginx-persistent-storage # 取个名字,与下面的volumes的名字要一致
    mountPath: /usr/share/nginx/html # 容器中的路径
    volumes:
    - name: nginx-persistent-storage
    persistentVolumeClaim:
    claimName: nginx-pvc # 引用前面声明的PVC

注意 :如果在NFS 服务器上定义的路径是/nfs/data/nginx 首先我们需要去 /nfs/data下创建 nginx的文件夹,不然pod是启动不起来的 接下来,在master节点上启动。

3. 验证PV-PVC-NFS存储结果
(1)pod中创建文件,NFS服务器查看文件

pod中数据存储目录创建一个index.html,然后到nfs服务器上看有没有。

pod创建并写入文件

nfs服务器查看文件

(2) NFS数据存储目录里创建文件,在Pod中查看文件

nfs中数据存储目录创建一个文件,然后到pod中看有没有。

nfs中创建文件:

进入pod查看文件是否存在:

(3)删除Pod,NFS服务器查看文件

将pod删除,重新拉起之后进入pod查看文件是否存在

4.回收PV

当 PV 不再需要时,可通过删除 PVC 回收

必须先删除pod再删除pvc,因为pod绑定了pvc,如果先删pvc会出现pvc一直处于删除状态而无法删除

PV 生命周期总共四个阶段 :

Available(可用)------ 可用状态,尚未被 PVC 绑定。

Bound(已绑定)------ 绑定状态,已经与某个 PVC 绑定。

Released(已释放)------ 与之绑定的 PVC 已经被删除,但资源尚未被集群回收。

Failed(失败)------ 当删除 PVC 清理资源,自动回收卷时失败,所以处于故障状态

PV空间的回收策略:通过PV定义中的persistentVolumeReclaimPolicy字段进行设置,可选

  • Recycle:会清除数据,自动回收。

  • Retain(默认):需要手动清理回收。

  • Delete:云存储专用的回收空间使用命令。

4、StorageClass

1.StorageClass

1.1 什么是StorageClass

StorageClass 对象的作用,其实就是创建 PV 的模板,具体地说,StorageClass 对象会定义如下两个部分内容:

第一,PV 的属性。比如,存储类型、Volume 的大小等等。

第二,创建这种 PV 需要用到的存储插件。比如,Ceph 等等,即存储制备器。

有了这样两个信息之后,Kubernetes 就能够根据用户提交的 PVC,找到一个对应的 StorageClass 了。然后,Kubernetes 就会调用该 StorageClass 声明的存储插件,创建出需要的 PV。

StorageClass 对象的命名很重要,用户使用这个命名来请求生成一个特定的类。 当创建 StorageClass 对象时,管理员设置 StorageClass 对象的命名和其他参数,一旦创建了对象就不能再对其更新。

apiVersion: storage.k8s.io/v1
kind: StorageClass 
metadata:
  name: managed-nfs-storage #一旦创建,StorageClass名字不可更改
provisioner: nfs-storage   # 存储插件的名字,这里的名称要和provisioner配置文件中的环境变量PROVISIONER_NAME保持一致 
                           # 若为公有云,此处填写公有云存储插件的名,例:provisioner: kubernetes.io/aws-ebs
parameters:   #具体存储插件相关信息
  server: "10.16.216.221 " 
  path: "/nfs/data/k8s"
  readOnly: "false"
1.2 为什么需要StorageClass

1)在一个大规模的Kubernetes集群里,可能有成千上万个PVC,这就意味着运维人员必须实现创建出这个多个PV,此外,随着项目的需要,会有新的PVC不断被提交,那么运维人员就需要不断的添加新的,满足要求的PV,否则新的Pod就会因为PVC绑定不到PV而导致创建失败。

2)通过 StorageClass 的定义,管理员可以将存储资源定义为某种类型的资源,比如快速存储、慢速存储等,用户根据 StorageClass 的描述就可以非常直观的知道各种存储资源的具体特性了,这样就可以根据应用的特性去申请合适的存储资源了。

在动态资源供应模式下,通过StorageClass和PVC完成资源动态绑定(系统自动生成PV),并供Pod使用的存储管理机制

1.3 运行原理

要使用 StorageClass,我们就得安装对应的自动配置程序,比如我们这里存储后端使用的是 nfs,那么我们就需要使用到一个 nfs-client 的自动配置程序,我们也叫它 Provisioner(制备器),这个程序使用我们已经配置好的 nfs 服务器,来自动创建持久卷,也就是自动帮我们创建 PV。

  1. 自动创建的 PV 以{namespace}-{pvcName}-${pvName}这样的命名格式创建在 NFS 服务器上的共享数据目录中

  2. 而当这个 PV 被回收后会以archieved-{namespace}-{pvcName}-${pvName}这样的命名格式存在 NFS 服务器上。

2. StorageClass 资源

2.1 存储制备器

每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备 PV。 该字段必须指定。

官方支持的StorageClass

NFS 没有内部制备器,但可以使用外部制备器。 也有第三方存储供应商提供自己的外部制备器。

3.NFS StorageClass 实战

3.1 部署流程

搭建StorageClass + NFS,大致有以下几个步骤:

  • 创建一个可用的NFS Server

  • 创建Service Account,这是用来管控NFS Provisioner 在k8s集群中运行的权限

  • 创建StorageClass,负责建立PV并调用NFS provisioner进行工作,并让PV与PVC建立关联

  • 创建NFS provisioner

    NFS provisioner是一个provisioner相关的插件,需要从网络上下载,我们需要下载下来放到镜像库中 下载以后需要以pod方式运行通过deployment部署导入到本地环境中
    提前下载镜像(选择一个下载即可):
    镜像1 docker pull registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner
    镜像2 docker pull lizhenliang/nfs-client-provisioner

3.2 创建Service Account
(1) 准备好NFS服务器

确保nfs可以正常工作,创建持久化需要的目录。

path: /nfs/data/class
server: 10.16.216.221 

(2)开启rbac权限

# rbac.yaml:#唯一需要修改的地方只有namespace,根据实际情况定义
apiVersion: v1
kind: ServiceAccount #  创建一个账户,主要用来管理NFS provisioner在k8s集群中运行的权限
metadata:
  name: nfs-client-provisioner
  namespace: default
---
kind: ClusterRole # 创建集群角色
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner # 角色名
rules: # 角色权限
  - apiGroups: [""]
    resources: ["persistentvolumes"] # 操作的资源 #pv
    verbs: ["get", "list", "watch", "create", "delete"] # 对该资源的操作权限
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"] #pvc
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"] #storageclasses
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["list", "watch", "create", "update","patch"]
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["create", "delete", "get", "list","watch", "patch", "update"]
---
kind: ClusterRoleBinding # 集群角色绑定
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects: # 角色绑定对象
  - kind: ServiceAccount
    name: nfs-client-provisioner
    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 # 角色名
  namespace: default # Role需要指定名称空间,ClusterRole全局可用不需要指定namespace
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
subjects: # 角色绑定对象
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: default
roleRef: # 绑定哪个角色
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io
3.3 创建StorageClass,指定provisioner

创建nfs-client-provisioner.yaml文件

# 创建NFS资源的StorageClass
---
apiVersion: storage.k8s.io/v1
kind: StorageClass # 创建StorageClass
metadata:
  name: managed-nfs-storage
provisioner: nfs-storage   #这里的名称要和provisioner配置文件中的环境变量PROVISIONER_NAME保持一致
parameters:  
  server: "10.16.216.221 " 
  path: "/nfs/data/k8s"
  readOnly: "false"
---
# 创建NFS provisioner
apiVersion: apps/v1
kind: Deployment # 部署nfs-client-provisioner
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  namespace: default #与RBAC文件中的namespace保持一致
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nfs-client-provisioner
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner # 指定serviceAccount!
      containers:
        - name: nfs-client-provisioner
          image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner #镜像地址
          volumeMounts: # 挂载数据卷到容器指定目录
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME # 配置provisioner的Name
              value: nfs-storage     # 确保该名称与 StorageClass 资源中的provisioner名称保持一致
            - name: NFS_SERVER       #绑定的nfs服务器
              value: 10.16.216.221 
            - name: NFS_PATH   #绑定的nfs服务器目录
              value: /nfs/data/k8s
      volumes: # 申明nfs数据卷
        - name: nfs-client-root
          nfs:
            server: 10.16.216.221 
            path: /nfs/data/k8s
3.4 创建pod测试

创建pod,声明PVC进行测试

# 申明一个PVC,指定StorageClass
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-claim
spec:
  storageClassName: managed-nfs-storage
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 10Mi
---
# 创建测试pod,查看是否可以正常挂载    
kind: Pod
apiVersion: v1
metadata:
  name: test-pod
spec:
  containers:
  - name: test-pod
    image: nginx:1.20.0
    imagePullPolicy: IfNotPresent
    volumeMounts:
      - name: nfs-pvc # 挂载数据卷
        mountPath: "/usr/share/nginx/html"
  restartPolicy: "Never"
  volumes:
    - name: nfs-pvc
      persistentVolumeClaim: # 数据卷挂载的是pvc
        claimName: test-claim  #与PVC名称保持一致

进入pod,向指定目录写文件

验证成功

相关推荐
网络研究院1 小时前
Am I Isolated:一款安全态势基准测试工具
容器·工具·基准测试·安全态势
颜淡慕潇5 小时前
【K8S问题系列 | 9】如何监控集群CPU使用率并设置告警?
后端·云原生·容器·kubernetes·问题解决
运维&陈同学5 小时前
【模块一】kubernetes容器编排进阶实战之k8s基础概念
运维·docker·云原生·容器·kubernetes·云计算
信子姚青5 小时前
K8s使用nfs
容器
葡萄皮Apple5 小时前
[CKS] K8S RuntimeClass SetUp
java·容器·kubernetes
mit6.8246 小时前
[Docker#4] 镜像仓库 | 部分常用命令
linux·运维·docker·容器·架构
诡异森林。9 小时前
Docker--Docker是什么和对Docker的了解
运维·docker·容器
老大白菜9 小时前
goframe开发一个企业网站 验证码17
运维·docker·容器·golang·goframe
研究司马懿10 小时前
【Golang】Go语言环境安装
开发语言·后端·云原生·golang·二开
zhangxueyi11 小时前
OpenEuler 下 Docker 安装、配置与测试实例
运维·docker·容器