k8s 存储

1. 存储机制概述

容器内部存储的生命周期是短暂的,会随着容器环境的销毁而被销毁,具有不稳定性。同时,如果多个容器希望共享同一份存储资源,则仅仅依赖容器本身是很难实现的。因此 Kubernetes 通过将容器应用所需的存储资源抽象为 Volume(存储卷)来解决这些问题。

Volume 在 Kubernetes 中也是一种资源,Kubernetes 提供了多种类型的 Volume 供容器应用使用。Pod 通过挂载(Mount)的方式来使用一个或多个 Volume。

Volume 可以被分为两大类:

  • 临时卷:具有与Pod相同的生命周期。
  • 持久卷:通常比Pod的生命周期更长。在Pod被销毁时,持久卷通常不会被立刻删除,而是交给用户来处理其中的数据。

2. 临时卷详解

临时卷与 Pod 具有相同的生命周期,包括为 Pod 创建的临时卷(emptyDir、Generic Ephemeral、CSI Ephemeral),以及通过 Kubernetes 的资源对象 configMap、secret、Downward API、Service Account Token、Projected Volume 为 Pod 提供数据的临时卷等。

2.1. emptyDir

这种类型的 Volume 将在 Pod 被调度到 Node 时由 kubelet 进行创建,在初始状态下其目录是空的,所以被命名为“空目录”(Empty Directory)。它与 Pod 具有相同的生命周期,当 Pod 被销毁时,emptyDir 对应的目录也会被删除。同一个 Pod 中的多个容器都可以挂载这种类型的 Volume。

常见应用场景:

  • 基于磁盘进行合并排序操作时需要的暂存空间。
  • 长时间计算任务的中间检查点文件。
  • 为某个Web服务提供的临时网站内容文件。

在默认情况下,kubele 会在 Node 的 /var/lib/kubelet/pods 目录下为 Pod 创建 emptyDir目录,这个目录的存储介质可能是本地磁盘、SSD磁盘或者网络存储设备,具体取决于环境的配置。

另外,emptyDir 可以通过 medium 字段设置存储介质为“Memory”,表示使用基于内存的文件系统。在主机重启之后,tmpfs 中的内容就会被清空。此外,写入 tmpfs 中的数据将被计入容器的内存使用量,受到容器级别内存资源上限的限制。

emptyDir 示例:

...
spec:
  containers:
    ...
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}
YAML

设置 emptyDir 为内存示例:

...
spec:
  containers:
    ...
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: "Memory"
    
# 在 kubelet 开启 SizeMemoryBackedVolumes 后可以设置使用内存上限
...
  volumes:
  - name: cache-volume
    emptyDir: "Memory"
    sizeLimit: 500Mi
YAML

2.2. Generic Ephemeral

2.3. CSI Ephemeral

3. 持久卷详解

前面介绍了临时存储,临时存储数据不能持久化。因此,Kubernetes 引人了 Persistent Volume(简称PV,持久卷)和 Persistent Volume Claim(简称PVC,持久卷申请申明)两个资源对象来实现存储持久化管理。

3.1. PV 与 PVC

我们可以将 PV 看作可用的存储资源,PVC 则是对存储资源的需求。

PV 将存储定义为一种容器应用可以使用的资源。PV 由管理员创建和配置,它与存储资源提供者的具体实现直接相关。不同提供者提供的 PV 类型包括 NFS、iSCSI、RBD 或者由 GCE 或 AWS 公有云提供的共享存储等。

如果某个 Pod 想要使用 PV,则需要通过 PVC 来完成申请,随后由 Kubernetes 完成从PVC 到 PV 的自动绑定流程,PVC 可以申请存储空间的大小(Size)和访问模式(例如ReadWriteOnce、 ReadOnlyMany 或 ReadWriteMany)。

目前 PV 和 PVC 之间是一对一绑定的关系,也就是说一个 PV 只能被一个 PVC 绑定。

3.2. StorageClass

使用PVC申请的存储空间可能仍然不满足应用对存储设备的各种需求。例如:

  • 在大规模集群场景下,管理员手动管理 PV 非常不方便。
  • 不同存储设备IO性能不同,种类不同,需要进行归类,方便 pod 使用。

为了解决上面两个问题,Kubernetes 从 v1.4 版本开始引入了一个新的资源对象StorageClass,用于标记存储资源的特性和性能,根据 PVC 的需求动态供给合适的 PV 资源。通过 StorageClass 创建的 PV 又称之为动态 PV。

3.3. PV 详解

PV 没有名称空间限制。

yaml 文件示例:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-hostpath
  labels:
    type: local
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  hostPath:
    path: '/data/test1'
    type: DirectoryOrCreate
  storageClassName: manual   # 我们是静态pv,此处声明的sc,代表设定该pv归属的类为 manual,代表静态/手动
YAML

PV 资源对象需要设置的关键配置参数如下:

  • 存储容量(capacity):存储容量用于描述存储的容量,目前仅支持对存储空间的设置(storage=xx),未来可能加人对 IOPS、吞吐率等的设置。
  • 存储卷模式(volumeMode):其中可以设置的选项包括 Filesystem (文件系统,默认值)和 Block(块设备)。
  • 访问模式(accessModes):AccessModes 是用来对 PV 进行访问模式的设置,用于描述用户应用对存储资源的访问权限,包括下面几种方式(都是针对节点级别的):
    • ReadWriteOnce(RWO):读写权限,并且只能被单个单个节点上的一个或多个 pod 挂载使用。
    • ReadOnlyMany(ROX):只读权限,可以同时在多个节点上挂载并被不同的 Pod。
    • ReadWriteMany(RWX):读写权限,可以同时在多个节点上挂载并被不同的 Pod 使用。
    • ReadWriteOncePod(RWOP):可以被单个 Pod 以读写方式挂载的模式,该特性在 Kubernetes v1.22 版本中被引人。仅支持 CSI 存储卷,它保证一个 PV 同一个时间只能被一个 Pod 挂载,并且该 Pod 可以对这个卷进行读写操作。
  • 存储类别(Class):PV 可以设定其存储的类别,通过 storageClassName 参数指定一个 StorageClass 资源对象的名称。具有特定类别的 PV 只能与请求了该类别的 PVC 绑定。未设定类别的 PV 则只能与不请求任何类别的 PVC 绑定。
  • 回收策略(ReclaimPolicy):回收策略通过 PV 定义中的 persistentVolumeReclaimPolicy 字段进行设置,可选项如下:
    • Retain:保留数据,当 PVC 被删除时,PV 不会自动删除。它的状态变为“Released”,但保留资源不能被其他 PVC 继续使用,直到管理员手动处理。这允许管理员手动回收资源或者检查数据。重要的数据还是推荐用该策略。
    • Delete:在这种回收策略下,PVC 被删除时,PV 以及底层的存储资源也会自动被删除。
    • Recycle(已弃用):PVC 被删除时,PV 上的数据将被简单地删除,而不会销毁 PV 本身。该策略会对 PV 进行基本清理(如删除文件内容),然后将其重新标记为Available ,使其可以被新的 PVC 再次绑定。由于安全和效能考虑,这个选项在较新的 Kubernetes 版本中已经被标记为弃用。
  • 节点亲和性(nodeAffinity):PV 可以通过设置节点亲和性来实现只能通过某些 Node 访问 Volume,这可以在 PV 定义的 nodeAffinity 字段中进行设置。使用这些 Volume 的 Pod 将被调度到满足亲和性要求的 Node 上。示例:
apiVersion: v1
kind: PersistentVolume
metadata:
  name: prometheus-local
  labels:
    app: prometheus
spec:
  accessModes:
    - ReadWriteOnce
  capacity:
    storage: 10Gi
  storageClassName: local-storage
  local:
    path: /data/k8s/prometheus
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - k8s-master-03-u-203
  persistentVolumeReclaimPolicy: Retain
YAML
  • PV 生命周期的各个阶段
    • Available:可用状态,还未与某个 PVC 绑定。
    • Bound:已与某个 PVC 绑定。
    • Released:与之绑定的 PVC 已被删除,但未完成资源回收,不能被其他PVC使用。
    • Failed:自动资源回收失败。

3.4. PVC 详解

受限于名称空间,PVC 与 使用该 PVC 的 Pod 需要再同一个名称空间。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-hostpath
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 8Gi
  storageClassName: manual
  selector:
    matchLabels:
      release: "stable"
    matchExpressions:
      - {key: environment, operator: In, values: [dev]}
YAML

PVC 关键参数说明:

  • 资源请求(resources):描述对存储资源的请求,通过 resources.requests.storage 字段设置需要的存储空间大小。
  • 访问模式(accessModes):PVC 也可以设置访问模式,用于描述用户应用对存储资源的访问权限。其访问模式的设置与 PV 的设置相同。
  • 存储卷模式(volumeMode):PVC 也可以设置存储卷模式,用于描述希望使用的 PV 存储卷模式,包括文件系统和块设备。PVC 设置的存储卷模式应该与 PV 存储卷模式相同,以实现绑定;如果不同,则可能出现不同的绑定结果。PV 和 PVC 在各种组合模式下是否可以绑定的结果如下表所示:(未设定默认采用 Filesystem)
PV 的存储卷模式PVC 的存储卷模式是否可以绑定
FilesystemFilesystemtrue
FilesystemBlockfalse
BlockFilesystemfalse
BlockBlocktrue
  • PV 选择条件(selector):通过设置 LabelSelector ,可使 PVC 对于系统中已存在的各种 PV 进行筛选。系统将根据标签选出合适的 PV 与该 PVC 进行绑定。对于选择条件可以通过 matchLabels 和 matchExpressions 进行设置,如果两个字段都已设置,则 Selector 的逻辑将是两组条件同时满足才能完成匹配。
  • 存储类别(Class):在定义 PVC 时可以设定需要的后端存储的类别(通过 storageClassName 字段进行指定),只有设置了该 Class 的 PV 才能被系统筛选出来,并与该 PVC 进行绑定。

3.4. StorageClass 对象详解

StorageClass 作为对存储资源的抽象定义,可对用户设置的 PVC 申请蔽后端存储的细节,这一方面减少了用户对于存储资源细节的关注,另一方面减轻了管理员手动管理 PV 的工作,由系统自动完成 PV 的创建和绑定,实现动态的资源供应。

StorageClass 配置 yaml 文件示例:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2
reclaimPolicy: Retain
allowVolumeExpansion: true
mountOptions:
  - debug
volumeBindingMode: Immediate
YAML

StorageClass 资源关键配置:

  • 存储资源提供者(Provisioner):Provisioner 描述存储资源的提供者,用于提供具体的 PV 资源,可以将其看作后端存储驱动。
  • 回收策略(Reclaim Policy):通过动态资源供应模式创建的 PV 将继承在StorageClass 上设置的回收策略,配置字段名称为“reclaimPolicy”,可以设置的选项包括 Delete 和 Retain。如果 StorageClass 没有指定 reclaimPolicy 字段,则默认值为 Delete。
  • 是否允许存储卷扩容(Allow Volume Expansion):当 StorageClass 的 AllowVolumeExpansion 字段被设置为 true 时,表示 PV 被配置为可以扩容,系统将允许用户通过编辑增加 PVC 的存储空间自动完成 PV 的扩容。
  • 存储绑定模式(Volume Binding Mode):StorageClass 资源对象的 volumeBindingMode 字段用于控制何时将 PVC 与动态创建的 PV 绑定。目前支持的绑定模式包括:
    • Immediate:表示当一个 PersistentVolumeClaim (PVC)被创建出来时,就动态创建 PV 并进行 PVC 与 PV 的绑定操作。注意:提前绑定可能导致使用 PVC 的 Pod 调度到资源不匹配的 Node 导致 Pod 启动失败。
    • WaitForFirstConsumer:表示 PVC 与 PV 的绑定操作延迟到第一个使用 PVC 的 Pod 创建出来时再进行。系统将根据 Pod 的调度需求,在 Pod 所在的Node上创建 PV。

4. 持久化存储方案示例

4.1. hostPath

hostPath 类型的存储卷,使用的是目标节点上的本地目录。

特点:持久化数据在固定节点上,Pod 不能随意调度。

部署示例:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web-test
  name: web-test
spec: 
  replicas: 1
  selector: 
    matchLabels:
      app: web-test
  template:                
    metadata:
      labels:
        app: web-test
    spec:        
      nodeName: k8s-node-01-c-211 # 需要固定调度到某个节点才行,否则重启 pod 调度到别的节点了就找不到 hostPath
      containers:
        - image: nginx:1.26.2
          name: nginx
          # 挂载到容器内
          volumeMounts:
            - name: test-volume
              mountPath: /usr/share/nginx/html    
      volumes:
        - name: test-volume
          hostPath:
            path: /test-data   # 宿主机目录
            # type: Directory    # 表示该目录必须存在
            type:  DirectoryOrCreate  # 表示目录不存在则会自动创建
YAML

测试:

# 在挂载目录创建测试文件
echo wwwwww > index.html

# 访问 pod ip
curl 10.244.2.62
# 显示结果
wwwwww
ShellScript

4.2. Local PV

在 hostPath 的基础上,k8s 依靠 pv 与 pvc 实现了一个新特性叫 Local PV。

注意:Local pv 对应的存储介质应该是一块额外挂载到宿主机上的磁盘或者块设备,避免与宿主机文件共用一个磁盘。否则会导致磁盘空间管理不可控。

创建 PV:

# local-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv
spec:
  capacity:
    storage: 1Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /local-pv-data # k8s-node-01-c-211节点上的目录,该目录强烈建议挂载一块单独的磁盘,该目录需要手动创建
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - k8s-node-01-c-211
YAML

创建目录:

mkdir /local-pv-data
ShellScript

接下来不能直接创建 PVC 与 PV 绑定,如果上述 PV 创建了多个且亲和不同的节点,则直接创建的 PVC 会随机选择一个 PV 绑定。再创建 Pod 后使用该 PVC 的 Pod 会调度到绑定的 PV 亲和的节点上,如何该节点资源不足,则 Pod 会启动失败。需要通过 storageClass 对象实现延迟绑定。

创建 storageClass:

# local-storageclass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner  # 指定为 no-provisioner 代表我们是手动创建的 PV
volumeBindingMode: WaitForFirstConsumer  # 关键!延迟绑定,等待使用它的Pod被调度
YAML

创建 PVC:

# local-pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: local-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: local-storage
YAML

创建 Pod:

# pod-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web-test
  name: web-test
spec: 
  replicas: 1
  selector: 
    matchLabels:
      app: web-test             
  template:                
    metadata:
      labels:
        app: web-test
    spec:        
      containers:
        - image: nginx:1.26.2
          resources: 
            requests:
              cpu: 1
              memory: 1.5Gi
          name: nginx
          # 挂载到容器内
          volumeMounts:
            - name: test-volume
              mountPath: /usr/share/nginx/html 
      # PVC声明     
      volumes:
        - name: test-volume
          persistentVolumeClaim:
            claimName: local-pvc
YAML

上面的方案只能手动创建 PV ,下面推荐一个自动创建 PV 的插件:

由 Rancher 开发的 local-path-provisioner 这个存储方案允许 Kubernetes 用户在每个节点上使用本地存储,对外提供一个 storageclass 默认名为 local-path。

下载地址:https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml

4.3. NFS 共享存储

4.3.1. 手动创建 PV

在存储节点安装 nfs 服务端:

# 内网环境可以直接关闭防火墙
systemctl stop firewalld.service
systemctl disable firewalld.service

# 安装
yum install -y nfs-utils rpcbind

# 创建数据共享目录
mkdir -p /data/nfs
chmod 755 /data/nfs

# 配置共享目录
cat >  /etc/exports <<EOF
/data/nfs *(rw,sync,no_root_squash)
EOF

# *:表示任何人都有权限连接,当然也可以是一个网段,一个 IP,也可以是域名
# rw:读写的权限
# sync:表示文件同时写入硬盘和内存
# no_root_squash:当登录 NFS 主机使用共享目录的使用者是 root 时,其权限将被转换成为匿名使用者,通常它的 UID 与 GID,都会变成 nobody 身份

# 启动nfs服务
systemctl start rpcbind.service
systemctl enable rpcbind
systemctl status rpcbind
systemctl start nfs
systemctl enable nfs
systemctl status nfs
ShellScript

k8s 集群节点上安装客户端:

sudo yum install -y nfs-utils 

sudo apt install -y nfs-common
ShellScript

创建基于 nfs 的 PV:

# nfs-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-pv
spec:
  storageClassName: manual
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /data/nfs       # 指定 nfs 的挂载目录
    server: 192.168.2.50  # 指定 nfs 服务地址
YAML

创建 PVC:

# nfs-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
spec:
  storageClassName: manual
  accessModes:
    - ReadWriteOnce
  resources:
   requests:
     storage: 1Gi
YAML

测试 Pod:

# nfs-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-volumes
spec:
  volumes:
    - name: nfs
      persistentVolumeClaim:
        claimName: nfs-pvc
  containers:
    - name: web
      image: nginx:1.26.2
      ports:
        - name: web
          containerPort: 80
      volumeMounts:
        - name: nfs
          subPath: test-volumes
          mountPath: '/usr/share/nginx/html'
YAML

4.3.2. 动态创建 PV

插件官网:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

安装:

helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/

# 镜像地址可以更换为自己的
helm upgrade --install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner --set nfs.server=192.168.2.50 --set nfs.path=/data/nfs --set storageClass.defaultClass=true --set image.repository=crpi-sozjkv641zbs4m9x.cn-shenzhen.personal.cr.aliyuncs.com/pingk-k8s-test/nfs-subdir-external-provisioner --set image.tag=v4.0.2 -n kube-system

# 详细的安装可以通过 values.yaml 文件配置安装
ShellScript

常用 StorageClass 配置命令:

# 关闭默认
kubectl patch storageclass nfs-client -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}' 

# 设置为默认
kubectl patch storageclass nfs-client -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}' 

# StorageClass 回收策略不支持修改,只能重新创建
ShellScript

4.3. CubeFS 分布式存储

官方文档

同时在 K8s 集群内部署 CubeFS 服务端集群和其 CSI 驱动示例

CubeFS 目前由这四部分组成:

  • Master:资源管理节点,负责维护整个集群的元信息,部署为 StatefulSet 资源。
  • DataNode:数据存储节点,需要挂载大量磁盘负责文件数据的实际存储,部署为 DaemonSet 资源。
  • MetaNode:元数据节点,负责存储所有的文件元信息,部署为 DaemonSet 资源。
  • ObjectNode:负责提供转换 S3 协议提供对象存储的能力,无状态服务,部署为 Deployment 资源。

在相应的节点打上标签:

# Master 节点,至少三个,建议为奇数个
kubectl label node <nodename> component.cubefs.io/master=enabled
# MetaNode 元数据节点,至少 3 个,奇偶无所谓
kubectl label node <nodename> component.cubefs.io/metanode=enabled
# Dataode 数据节点,至少 3 个,奇偶无所谓
kubectl label node <nodename> component.cubefs.io/datanode=enabled
# ObjectNode 对象存储节点,可以按需进行标记,不需要对象存储功能的话也可以不部署这个组件
kubectl label node <nodename> component.cubefs.io/objectnode=enabled

# 根据自身情况给需要使用 CubeFS 的 k8s节点打上标签
kubectl label node <nodename> component.cubefs.io/csi=enabled
Bash

挂载数据盘:

在标志为 component.cubefs.io/datanode=enabled 的节点上进行挂载数据盘操作。

# 查看机器磁盘信息
fdisk -l

# 格式化磁盘
mkfs.xfs -f /dev/sdx

# 创建挂载目录
mkdir /data0

# 挂载磁盘
mount /dev/sdx /data0

# 如果机器上存在多个需要挂载的数据磁盘,则每个磁盘按以上步骤进行格式化和挂载磁盘,挂载目录按照data0/data1/../data999的顺序命名。
Bash

拉取 CubeFS Helm 仓库:

git clone https://github.com/cubefs/cubefs-helm.git
cd cubefs-helm
Bash

编辑配置:

部署 CubeFS 的 helm 存在大量的配置,所有的可配置项位于 helm 项目下的cubefs/values.yaml中,其中包含有详细的注释。这里单独创建一个配置文件cubefs-helm.yaml,覆盖其中常见的关键配置项。

# 要安装哪些组件
# 同时启用服务端和CSI
component:
  master: true
  datanode: true
  metanode: true
  objectnode: true
  client: false  # 通常不需要单独的client,用CSI即可
  csi: true      # 必须设置为 true 以部署CSI驱动
  monitor: false
  ingress: true

# path.data: Master、MetaNode 的元数据存储路径,会以 hostPath 的方式存储在宿主机上,建议使用性能较高的底层磁盘
# path.log: 所有组件的日志在宿主机上的存储路径
path:
  data: /var/lib/cubefs
  log: /var/log/cubefs

# Master 配置
master:
  # Master 组件实例数量
  replicas: 3
  # Master Ingres 配置使用的域名,记得需要将该域名 DNS 解析到 Ingres Controller 的入口,
  # 当然也可以不配置,在客户端处直接将所有 Master 的 IP + 端口配置上
  host: master.cubefs.com

# ObjectNode 配置
objectnode:
  replicas: 2

# MetaNode 配置
metanode:
  # MetaNode 可以使用的总内存,单位字节,建议设置为机器可以内存的 80%,也可以按需减少
  total_mem: "2147483648"

# DataNode 配置 - 这是关键,必须指向正确的磁盘
datanode:
  disks:
    - /data0:0
  # 保留的空间: 单位字节,当磁盘剩余空间小于该值时将不会再在该磁盘上写入数据
  # 如果你的磁盘是裸设备(例如 /dev/sdb),配置方式不同,通常需要先格式化挂载到/data0

# ------------------------ CSI 驱动配置 ------------------------
# CSI 镜像配置
image:
  csi_driver: cubefs/cfs-csi-driver:v3.4.0
  csi_provisioner: registry.k8s.io/sig-storage/csi-provisioner:v2.2.2
  csi_attacher: registry.k8s.io/sig-storage/csi-attacher:v3.4.0
  csi_resizer: registry.k8s.io/sig-storage/csi-resizer:v1.3.0
  driver_registrar: registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.5.0

# CSI 详细参数
csi:
  driverName: csi.cubefs.com
  logLevel: error
  kubeletPath: /var/lib/kubelet
  controller:
    tolerations: [ ]
    nodeSelector:
      "component.cubefs.io/csi": "enabled"
  node:
    tolerations: [ ]
    nodeSelector:
      "component.cubefs.io/csi": "enabled"
    resources:
      enabled: false
      requests:
        memory: "2048Mi"
        cpu: "1000m"
      limits:
        memory: "2048Mi"
        cpu: "1000m"
  storageClass:
    setToDefault: true    # 自动设为默认StorageClass
    reclaimPolicy: "Delete"
    # 重要:这里必须指定刚部署的CubeFS集群的Master地址!
    # 格式:master1:port,master2:port,master3:port
    # 如果是要连接外部 CubeFS 集群的设置该参数,否则可以忽略
    masterAddr: ""
    otherParameters:
YAML

测试环境低配版:

component:
  master: true
  datanode: true
  metanode: true
  objectnode: true
  client: false  # 学习测试通常不需要单独client,用CSI即可
  csi: true
  monitor: false  # 保留监控以便观察,但会调低资源
  ingress: true 
  blobstore_clustermgr: false
  blobstore_blobnode: false
  blobstore_proxy: false
  blobstore_scheduler: false
  blobstore_access: false

image:
  pull_policy: "IfNotPresent" # 确保使用本地镜像,加快启动

path:
  data: /var/lib/cubefs
  log: /var/log/cubefs

master:
  replicas: 3 # 保持3个副本以保证高可用逻辑,但大幅降低资源
  host: master.cubefs.com
  resources:
    enabled: true # 启用资源限制,但设置为很小的值
    requests:
      memory: "512Mi"  # 从 8Gi 降至 512Mi
      cpu: "250m"      # 从 2000m 降至 250m
    limits:
      memory: "1Gi"    # 从 32Gi 降至 1Gi
      cpu: "500m"      # 从 8000m 降至 500m

metanode:
  total_mem: "1073741824" # 从 25Gi 降至 1Gi (1GB)
  resources:
    enabled: true
    requests:
      memory: "512Mi"  # 从 32Gi 降至 512Mi
      cpu: "250m"      # 从 2000m 降至 250m
    limits:
      memory: "2Gi"    # 从 256Gi 降至 2Gi
      cpu: "500m"      # 从 8000m 降至 500m

datanode:
  disks:
    - /data0:0
  resources:
    enabled: true
    requests:
      memory: "256Mi"  # 从 8Gi 降至 256Mi
      cpu: "250m"      # 从 2000m 降至 250m
    limits:
      memory: "512Mi"  # 从 32Gi 降至 512Mi
      cpu: "500m"      # 从 8000m 降至 500m

objectnode:
  replicas: 1 # 从3个副本降至1个
  resources:
    enabled: true
    requests:
      memory: "128Mi"  # 从 512Mi 降至 128Mi
      cpu: "100m"      # 从 500m 降至 100m
    limits:
      memory: "256Mi"  # 从 1024Mi 降至 256Mi
      cpu: "200m"      # 从 1000m 降至 200m

# CSI 驱动资源调整
csi:
  driverName: csi.cubefs.com
  logLevel: error
  kubeletPath: /var/lib/kubelet
  controller:
    tolerations: [ ]
    nodeSelector:
      "component.cubefs.io/csi": "enabled"
  node:
    tolerations: [ ]
    nodeSelector:
      "component.cubefs.io/csi": "enabled"
    resources:
      enabled: true
      requests:
        memory: "512Mi"
        cpu: "200m"
      limits:
        memory: "512Mi"
        cpu: "200m"
  storageClass:
    setToDefault: true    # 自动设为默认StorageClass
    reclaimPolicy: "Delete"
    # 重要:这里必须指定刚部署的CubeFS集群的Master地址!
    # 格式:master1:port,master2:port,master3:port
    # 如果是要连接外部 CubeFS 集群的设置该参数,否则可以忽略
    masterAddr: ""
    otherParameters:
YAML

部署:

helm upgrade --install cubefs ./cubefs -f ./cubefs-helm.yaml -n cubefs --create-namespace
Bash

查看:

[admin@k8s-master-02 ~]$ kubectl get pod -n cubefs
NAME                                  READY   STATUS    RESTARTS   AGE
cfs-csi-controller-55476ff575-77wz9   4/4     Running   0          5m48s
cfs-csi-node-4cm54                    2/2     Running   0          5m48s
cfs-csi-node-c9h8h                    2/2     Running   0          5m48s
cfs-csi-node-hj2vh                    2/2     Running   0          5m48s
cfs-csi-node-ln227                    2/2     Running   0          5m48s
cfs-csi-node-wrhxz                    2/2     Running   0          5m48s
datanode-6d77s                        1/1     Running   0          5m48s
datanode-jghh4                        1/1     Running   0          5m48s
datanode-krrzb                        1/1     Running   0          5m48s
datanode-sln22                        1/1     Running   0          5m48s
datanode-xwqkj                        1/1     Running   0          5m48s
master-0                              1/1     Running   0          5m48s
master-1                              1/1     Running   0          5m45s
master-2                              1/1     Running   0          5m43s
metanode-brcs7                        1/1     Running   0          5m48s
metanode-fvzzv                        1/1     Running   0          5m48s
metanode-kz7qw                        1/1     Running   0          5m48s
objectnode-87fb6cd45-pw7jw            1/1     Running   0          5m48s


[admin@k8s-master-02 ~]$ kubectl get StorageClass
NAME               PROVISIONER      RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
cfs-sc (default)   csi.cubefs.com   Delete          Immediate           true                   6m11s
Bash

创建 PVC 测试:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: cubefs-pvc
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: cfs-sc
YAML
kubectl apply -f pvc.yaml

kubectl get pvc
NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIB
cubefs-pvc   Bound    pvc-2d30ba68-cdb5-4828-97d8-42421b9505c7   5Gi        RWO            cfs-sc         <unset>  
Bash

挂载 PVC:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: cfs-csi-demo
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: cfs-csi-demo-pod
  template:
    metadata:
      labels:
        app: cfs-csi-demo-pod
    spec:
      nodeSelector:
        component.cubefs.io/csi: enabled
      containers:
        - name: cfs-csi-demo
          image: nginx:1.26.2
          imagePullPolicy: "IfNotPresent"
          ports:
            - containerPort: 80
              name: "http-server"
          volumeMounts:
            - mountPath: "/usr/share/nginx/html"
              mountPropagation: HostToContainer
              name: mypvc
      volumes:
        - name: mypvc
          persistentVolumeClaim:
            claimName: cubefs-pvc
YAML

可以在数据节点上的 /data0/ 目录下看到数据:

[root@k8s-node-02 ~]# ll /data0/
total 40
drwxr-xr-x 3 root root 4096 Aug 30 22:11 datapartition_10_128849018880
drwxr-xr-x 3 root root 4096 Aug 30 22:11 datapartition_1_128849018880
drwxr-xr-x 3 root root 4096 Aug 30 22:11 datapartition_2_128849018880
drwxr-xr-x 3 root root 4096 Aug 30 22:11 datapartition_3_128849018880
drwxr-xr-x 3 root root 4096 Aug 30 22:11 datapartition_4_128849018880
drwxr-xr-x 3 root root 4096 Aug 30 22:11 datapartition_5_128849018880
drwxr-xr-x 3 root root 4096 Aug 30 22:11 datapartition_6_128849018880
drwxr-xr-x 3 root root 4096 Aug 30 22:11 datapartition_7_128849018880
drwxr-xr-x 3 root root 4096 Aug 30 22:11 datapartition_8_128849018880
drwxr-xr-x 3 root root 4096 Aug 30 22:11 datapartition_9_128849018880
Bash
上一篇
下一篇