【7】Kubernetes存储(本章知识密度较高,仅浅浅了解后续详解)

文章目录

卷(Volume)

Kubernetes 卷(Volume)为 Pod 中的容器提供了一种通过文件系统访问和共享数据的方式。数据共享可以发生在容器内不同本地进程之间,或在不同容器之间,或在多个 Pod 之间。

常见的 Volume 类型:

emptyDir
  • emptyDir:一个临时的目录,Pod 存在时有效。Pod 删除时目录会消失。

    对于定义了 emptyDir 卷的 Pod,在 Pod 被指派到某节点时此卷会被创建。 就像其名称所表示的那样,emptyDir 卷最初是空的。尽管 Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不同,但这些容器都可以读写 emptyDir 卷中相同的文件。 当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会被永久删除。

    ⚠️注意:由于emptyDir 生命周期关联Pod,所以当 Pod 被删除时,Pod 中的 Volume 也会被删除,数据不会被持久化。

    ✅提示:容器崩溃并会导致 Pod 被从节点上移除,因此容器崩溃期间卷中的数据是安全的(Pod崩溃的话卷依然不会被保留)。

    由于emptyDir的卷只能在本Pod中的容器使用,并且不能直接挂载外部资源,所以就不演示了。

hostPath
  • hostPath:将宿主机上的目录挂载到 Pod 中。适用于特定节点的存储。

    hostPath 卷能将主机节点文件系统上的文件或目录挂载到你的 Pod 中。 虽然这不是大多数 Pod 需要的,但是它为一些应用提供了强大的逃生舱。(注意,只能挂载Pod所在节点上的资源,这个卷用于本地存储)

    无论 hostPath 卷是以只读还是读写方式挂载,使用时都需要小心,这是因为:

    • 访问主机文件系统可能会暴露特权系统凭证(例如 kubelet 的凭证)或特权 API(例如容器运行时套接字), 这些可以被用于容器逃逸或攻击集群的其他部分。
    • 具有相同配置的 Pod(例如基于 PodTemplate 创建的 Pod)可能会由于节点上的文件不同而在不同节点上表现出不同的行为。
    • hostPath 卷的用量不会被视为临时存储用量。 你需要自己监控磁盘使用情况,因为过多的 hostPath 磁盘使用量会导致节点上的磁盘压力。

hostPath实验验证:

宿主机挂载资源/data目录并写入一个index.html文件

bash 复制代码
mkdir -p /mnt/data
echo " Welcome to NGINX! " > /mnt/data/index.html
chmod 777 /mnt/data
cat /mnt/data/index.html
Welcome to NGINX!


创建使用hostPath卷的pod并访问

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
name: nginx-hostpath-pod
spec:
containers:
  - name: nginx-container
    image: nginx
    volumeMounts:
    - mountPath: /usr/share/nginx/html  # nginx 的 Web 根目录
      name: hostpath-volume  # 卷的名称
  volumes:
  - name: hostpath-volume
    hostPath:
      path: /mnt/data  # 宿主机上挂载的目录路径
      type: Directory  # 类型为 Directory,指示这是一个目录

可以看见,curl后显示了 Welcome to NGINX!,也就是我们在宿主机资源写入的index.html,而不是显示nginx默认主页。

再次修改,验证是否成功挂载

ini 复制代码
写入"zxnbmk到此一游!"

再次访问服务,可以看见页面已经被修改,挂载是没有问题

🎇 小知识:hostPath的挂载资源由于是在宿主机上,而不是在容器内部,所以删除Pod并不会将数据清除

可以发现,我们删除了Pod后,本地资源依然存在

NFS、iSCSI、Ceph
  • NFS、iSCSI、Ceph 等:将外部存储系统作为 Volume 挂载到 Pod 中。

    这些类型的卷可以挂载外部存储系统的资源,比如nfs卷能将 NFS (网络文件系统) 挂载到你的 Pod 中。 不像 emptyDir 那样会在删除 Pod 的同时也会被删除,nfs 卷的内容在删除 Pod 时会被保存,卷只是被卸载。 这意味着 nfs 卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。

    yaml 复制代码
    apiVersion: v1
    kind: Pod 
    metadata:
      name: test-pd
    spec:
      containers:
      - name: test-container
        image: registry.k8s.io/test-webserver
        volumeMounts:
        - mountPath: /my-nfs-data  # 容器内挂载的路径,指定容器中 `/my-nfs-data` 路径为挂载点
          name: test-volume  # 与 volumes 部分的卷名称匹配,这里是 `test-volume`
      volumes:
      - name: test-volume  # 定义一个名为 `test-volume` 的卷
        nfs:
          server: my-nfs-server.example.com  # 指定 NFS 服务器的地址(实际的 NFS 服务器地址)
          path: /my-nfs-volume  # NFS 服务器上共享的目录路径
          readOnly: true  # 设置为只读,表示挂载的目录不能写入,只能读取

    如上NFS卷的yaml示意,由于资源有限,只做yaml文件示例

持久卷(PV)

存储对于大多数的实际生产环境应用来说是非常重要的,我们常常需要统一和标准化存储资源管理,并且提供更多的功能和灵活性PV 提供了 更强的存储资源管理能力 ,包括动态供应、跨节点共享、访问控制等功能,是 Kubernetes 持久化存储的标准化解决方案,而单独的卷管理复杂且不灵活(NFS,hostpath等),考虑到这个问题,我们的Kubernetes 拥有成熟且功能丰富的存储子系统,称为持久化卷子系统(Persistent Volume Subsystem)。

🎯🎯🎯说人话就是,持久卷(PV) 的核心意义就是将前面提到的普通卷(如 hostPathNFS 等支持持久性的卷)通过 PVC 进行持久化和标准化管理,或者使用存储类(Storage Class)来动态制备,这样用户和集群管理员就不需要再手动管理底层存储,Kubernetes 会根据 PVC 的需求自动绑定合适的 PV,实现 存储资源的自动化和灵活化管理

pv结构

yaml 复制代码
apiVersion: v1
kind: PersistentVolume
metadata:
  name: static-pv-hostpath
  labels:
    type: local-pv  # pv的标签,用于被绑定或者被筛选
spec:
  capacity:  # 定义存储能力
    storage: 10Gi  # 存储容量
  accessModes:
    - ReadWriteOnce  # 访问模式:单节点读写
  persistentVolumeReclaimPolicy: Retain  # 回收策略:保留
  hostPath:  # 卷类型为hostPath
    path: "/mnt/data"  # 主机上的目录路径
    type: DirectoryOrCreate  # 目录不存在则创建

可以看到上面是一个hostPath类型的pv卷,一个pv拥有的属性如下:

  1. 存储容量:storage字段,拥有Mi和Gi两个单位

  2. 访问模式:

    • ReadWriteOnce卷可以被一个节点以读写方式挂载。 ReadWriteOnce 访问模式仍然可以在同一节点上运行的多个 Pod 访问(读取或写入)该卷。 对于单个 Pod 的访问,请参考 ReadWriteOncePod 访问模式。

    • ReadOnlyMany卷可以被多个节点以只读方式挂载。

    • ReadWriteMany卷可以被多个节点以读写方式挂载。

    • ReadWriteOncePod卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC, 请使用 ReadWriteOncePod 访问模式。

  3. 回收策略:

    • Retain 保留:删除PVC不会同步删除PV,等管理员手动去处理PV里的数据。NFS、HostPath都支持该项
    • Delete 删除:删除PVC同步删除PV。NFS、HostPath都不支持该项
    • Recycle 回收:删除PVC同步删除PV且重回可用状态。K8s V1.9后该回收策略标记为过时
  4. 卷类型(类型较多,暂不一一展示)

    • csi - 容器存储接口(CSI)
    • fc - Fibre Channel(FC)存储
    • hostPath - HostPath 卷 (仅供单节点测试使用;不适用于多节点集群;请尝试使用 local 卷作为替代)
    • iscsi - iSCSI(IP 上的 SCSI)存储
    • local - 节点上挂载的本地存储设备
    • nfs - 网络文件系统(NFS)存储

    以下的持久卷已被弃用但仍然可用。 如果你使用除 flexVolumecephfsrbd 之外的卷类型,请安装相应的 CSI 驱动程序。

    • awsElasticBlockStore - AWS Elastic 块存储(EBS) (从 v1.23 开始默认启用迁移
    • azureDisk - Azure 磁盘 (从 v1.23 开始默认启用迁移
    • azureFile - Azure 文件 (从 v1.24 开始默认启用迁移
    • cinder - Cinder(OpenStack 块存储) (从 v1.21 开始默认启用迁移
    • flexVolume - FlexVolume (从 v1.23 开始弃用,没有迁移计划,没有移除支持的计划)
    • gcePersistentDisk - GCE 持久磁盘 (从 v1.23 开始默认启用迁移
    • portworxVolume - Portworx 卷 (从 v1.31 开始默认启用迁移
    • vsphereVolume - vSphere VMDK 卷 (从 v1.25 开始默认启用迁移

    旧版本的 Kubernetes 仍支持这些"树内(In-Tree)"持久卷类型:

    • cephfs (v1.31 之后不可用
    • flocker - Flocker 存储。 (v1.25 之后不可用
    • glusterfs - GlusterFS 存储。 (v1.26 之后不可用
    • photonPersistentDisk - Photon 控制器持久化盘 (v1.15 之后不可用
    • quobyte - Quobyte 卷。 (v1.25 之后不可用
    • rbd - Rados 块设备 (RBD) 卷 (v1.31 之后不可用
    • scaleIO - ScaleIO 卷 (v1.21 之后不可用
    • storageos - StorageOS 卷 (v1.25 之后不可用

工作原理

Kubernetes支持来自多种途径的多种类型的存储。例如iSCSI、SMB、NFS,以及对象存储等,都是不同类型的、部署在云上或自建数据中心的外部存储系统。不过,无论什么类型的存储,或来自哪里,在其对接到Kubernetes集群中后,都会被统称为卷(volume)

总体架构如上:存储提供商提供存储阵列,Kubernetes 的CSI提供统了一套接口开放标准,提供商就可以通过自己的插件接入集群,从而集群将这些存储资源抽象为PV卷在 Kubernetes 中进行使用。

持久化卷子系统

持久化卷子系统中的3个主要资源如下。

  • 持久化卷(Persistent Volume,PV)。
  • 持久化卷申请(Persistent Volume Claim,PVC)。
  • 存储类(Storage Class,SC)。

上图某存储管理员创建了名为ebs-vol的25GB的ebs物理卷,通过厂商的插件与Kubernetes连接,Kubernetes管理员也创建了一个名为k8s-vol的PV卷通过插件与真实的ebs卷建立连接(将外部存储映射到集群),这时候Pod通过向PVC申请对PV的使用。

!!!小知识!!!

  • PV相当于外部存储的映射**(在定义时可以小于等于外部存储卷大小)**,一种表示外部存储的简单方式。
  • PVC 是Pod对存储资源的一个申请,主要包括存储空间申请、访问模式等。创建PV后,Pod就可以通过PVC向PV申请磁盘空间了。
  • SC可以在 PVC(Persistent Volume Claim)中指定所需的存储卷的特性从而自动创建PV,而无需直接操作 PV 对象。

⚠️注意:一个外部存储卷只能被一个PV使用。例如,一个50GB的外部卷不能分成两个25GB的Kubernetes PV来使用。

关于容器存储接口(CSI)

容器存储接口(The Container Storage Interface, CSI)是一个开源项目,定义了一套基于标准的接口,从而使存储能够以一种统一的方式被不同的容器编排工具使用。容器存储接口(CSI) 为容器编排系统(如 Kubernetes)定义标准接口,以将任意存储系统暴露给它们的容器工作负载。

🔍**(所以存储厂商只需要按照接口编写驱动/插件即可让自家存储资源用于编排工具)**

PVC

前面已经解释pvc是Pod对存储资源的一个申请,主要包括存储空间申请、访问模式等。创建PV后,Pod就可以通过PVC向PV申请磁盘空间了。

pvc主要属性有:需要绑定的pv的访问模式,容量大小,被绑定的pv的标签

!!!特别注意!!!

  • 这里pvc绑定的pv只要大于等于pvc需要的容量就可以匹配成功,并不一定非要一致,只要pv有足够能留提供pvc所需容量即可。
yaml 复制代码
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: static-pvc-example
  labels:
    type: local-pvc  # pvc的标签,用于被绑定或者被筛选
spec:
  accessModes:  # 开始定义需要绑定的pv的规格
    - ReadWriteOnce
  resources:
    requests:
      storage: 8Gi   # 实际上被绑定的pv只要满足大于等于10Gi就可以
  selector:
    matchLabels:
      type: local-pv  # 选中标签为type: local-pv的pv

可以发现pvc已经成功绑定上一节创建的pv

创建pod使用pv

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: test-pvc-pod
  labels:
    app: test-app
spec:
  containers:
  - name: nginx-container
    image: nginx:alpine
    ports:
    - containerPort: 80
    volumeMounts:
    - name: data-volume  #引用的卷名称(必须与下面的 volumes 名称匹配),后面多个路径需要挂载就会通过这个名字来区分,所以这里需要你起个名字
      mountPath: /usr/share/nginx/html  # 容器内挂载点,原内容将被卷内容替换
  volumes:
  - name: data-volume   # 被引用的卷名称,与上面路径引用的名称对应
    persistentVolumeClaim:
      claimName: static-pvc-example   # 这里定义要使用的pvc的名字
  • 由于pod被调度到node02上,我们用的是hostpath本地存储,所以根据我们pv的目录: path: "/mnt/data"我们需要在node02上创建改目录并创建一个index.html替换pod的index.html
bash 复制代码
[root@node02-k8s ~]# echo "这是pv卷" > /mnt/data/index.html
[root@node02-k8s ~]# cat /mnt/data/index.html 
  • 访问该pod,显示为刚刚的html内容

小知识,多路径多卷挂载

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: multi-volume-pod
spec:
  containers:
  - name: app-container
    image: nginx:alpine
    volumeMounts:
    - name: html-volume      # 挂载第一个卷
      mountPath: /usr/share/nginx/html
    - name: config-volume    # 挂载第二个卷
      mountPath: /etc/nginx/conf.d
    - name: logs-volume      # 挂载第三个卷
      mountPath: /var/log/nginx
  volumes:
  # 卷1:HTML 内容 (PVC)
  - name: html-volume
    persistentVolumeClaim:
      claimName: html-pvc
  
  # 卷2:配置文件 (ConfigMap)
  - name: config-volume
    configMap:
      name: nginx-config
  
  # 卷3:日志存储 (emptyDir)
  - name: logs-volume
    emptyDir: {}

本ymal仅仅用于拓展知识了解多路径多卷挂载,暂不实机演示。

存储类(StorageClass)

上面我详细讲解了创建pvc绑定pv从而使用pv的资源,整个过程来说,如果有多个pod想要使用我们的pv,就要一直创建pv,显得很繁琐,所以,这里出现了存储类(StorageClass),他就是用来动态管理我们的pv的我们称为动态供应,而上一节我们手动创建pv的过程就属于静态供应具体如下图:

StorageClass与PV的关系就好比类与对象得关系,StorageClass相当于PV的模板

由于下面的实验需要用到NFS服务器,这里仅解释原理,不进行实机操作,操作将在未来纳入我的《linux实战》专栏

  1. 创建StorageClass对象

    yaml 复制代码
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
        name: nfs-csi
        # StorageClass名称,在PVC中通过storageClassName引用
        
    # 1. CSI Driver名称(要和CSI驱动部署时指定的名称保持一致)
    provisioner: nfs.csi.k8s.io
    # 这是最重要的字段,指定使用哪个CSI驱动来创建存储卷
    # CSI (Container Storage Interface) 是K8s存储插件标准接口
    # nfs.csi.k8s.io 是NFS的CSI驱动名称,需要先部署对应的CSI驱动
    
    # 2. 存储参数 - 传递给CSI驱动的参数
    parameters:
        # nfs-server的IP地址
        server: 192.168.26.33
        # 这是NFS服务器的地址,CSI驱动会连接这个服务器
        
        # nfs-server上的存储目录路径
        share: /root/nfs/data
        # NFS服务器上暴露的共享目录路径
        # CSI驱动会在这个目录下为每个PVC创建子目录
    
    # 3. 回收策略(支持Delete和Retain, 默认Delete)
    reclaimPolicy: Retain
    # 当PVC被删除时,底层存储卷的处理方式:
    # - Delete: 自动删除PV和底层存储(NFS通常不支持自动删除)
    # - Retain: 保留PV和底层存储,需要手动清理(适合NFS)
    # - Recycle: 已废弃,不推荐使用
    # 这里设为Retain是因为NFS存储通常需要手动管理目录清理
    
    # 4. 卷的绑定模式
    volumeBindingMode: Immediate
    # 控制PVC何时绑定到PV:
    # - Immediate: PVC创建时立即绑定到PV(默认值)
    # - WaitForFirstConsumer: 延迟绑定,直到Pod调度时再绑定
    #   (适合有拓扑限制的存储,如本地存储)
    # 这里用Immediate,因为NFS存储没有节点限制
    
    # 5. 挂载选项 - 传递给mount命令的参数
    mountOptions:
        - hard    # 硬挂载模式:服务器异常时客户端会持续重试,直到服务器恢复
                  # 软挂载(soft)会在超时后放弃,可能导致数据不一致
        - nfsvers=4.1  # 指定NFS协议版本,4.1支持更多特性
        - nolock    # 禁用文件锁,提高性能(适合只读或单写场景)
        # 其他可用选项:
        # - rw/ro: 读写/只读模式
        # - tcp/udp: 传输协议
        # - timeo=<value>: 超时时间(十分之一秒)
        # - retrans=<value>: 重试次数
    
    # 6. 允许动态扩容,默认false
    allowVolumeExpansion: true
    # 控制是否允许PVC创建后扩容:
    # - true: PVC可以编辑spec.resources.requests.storage字段来扩容
    # - false: PVC创建后不能修改容量
    # 注意:这里设为true,但NFS本身可能不支持动态扩容
    # 实际效果取决于CSI驱动和底层存储系统的支持
    
    # 其他重要字段(未在此示例中使用):
    
    # 可选的字段:
    # allowedTopologies: []  # 存储可用的拓扑区域
    # mountOptions: []       # 挂载选项(已使用)
    # parameters: {}         # 驱动特定参数(已使用)
    # provisioner: string    # 驱动名称(已使用)
    # reclaimPolicy: string  # 回收策略(已使用)
    # volumeBindingMode: string  # 绑定模式(已使用)
    # allowVolumeExpansion: boolean  # 扩容支持(已使用)

    这里要非常注意volumeBindingMode的值,Immediate和WaitForFirstConsumer决定了是否要等待Pod成功调度后再自动创建PV

  2. 创建PVC

    yaml 复制代码
    # PVC引用StorageClass
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: nfs-pvc
    spec:
      storageClassName: nfs-csi  # 引用上面的StorageClass
      accessModes:
        - ReadWriteMany  # NFS支持多节点读写
      resources:
        requests:
          storage: 10Gi
    
    # Pod使用PVC
    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx
        volumeMounts:
        - name: data
          mountPath: /data
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: nfs-pvc

    由于我们volumeBindingMode的值为Immediate,所以当我们创建PVC后,对应的PV就会在同时创建并被PVC绑定


🎀至此,实验完成,存储章节梗概版本暂时完结,K8s入门篇章已过大半,后续将会开通K8s详解,以更深层次的理解来研究。

相关推荐
ChangYan.2 小时前
Windows命令行(cmd)下快速查找文件路径(类似Linux下find命令)
linux·运维·服务器
叫致寒吧2 小时前
pod详解
云原生·kubernetes
Hey小孩2 小时前
[个人总结] LDD3:3.字符驱动 - scull(4)
linux·驱动开发
陈让然2 小时前
VS Code新版本无法连接WSL ubuntu18.04
linux·运维·ubuntu
oMcLin2 小时前
如何在Oracle Linux 8.4上通过配置Oracle RAC集群,确保企业级数据库的高可用性与负载均衡?
linux·数据库·oracle
小杰帅气2 小时前
神秘的环境变量和进程地址空间
linux·运维·服务器
Vect__2 小时前
基于CSAPP对链接和库的理解
linux
胖咕噜的稞达鸭2 小时前
进程间的通信(1)(理解管道特性,匿名命名管道,进程池,systeam V共享内存是什么及优势)重点理解代码!
linux·运维·服务器·数据库
Coder个人博客2 小时前
Linux6.19-ARM64 boot Makefile子模块深入分析
linux·车载系统·系统架构·系统安全·鸿蒙系统