存储架构对比

单节点存储

一种常见但又失败的存储设计:一台存储服务器,挂载了很多磁盘,搭建一个nfs或者其他nas服务提供存储。

image-20250828155935532

这种架构存在很多问题:

  1. 单点故障,非高可用
  2. 如果想再搭一台作为高可用,很难实时同步两台节点的数据;而且故障转移也很困难,大概率转移不过去。
  3. 性能瓶颈,数据阻塞
  4. 存储服务器的网卡成为网络数据流量的瓶颈,一般只有一个或者两个磁盘承载读写,也会成为瓶颈。
  5. 存储有限,扩容复杂
  6. 安全性低,容易宕机
  7. 功能有限,只支持文件
  8. 如果想实现块存储或者对象存储是不支持的
  9. 隔离性差,无权限管理

分布式存储

生产环境中建议使用分布式存储。分布式存储有以下优势:

  1. 支持近乎无限的扩容
  2. 可以通过添加节点到集群的方式扩容
  3. 扩容后集群会自动balance
  4. 支持容错能力和数据冗余
  5. 可以配置存储多份文件副本
  6. 可以配置文件拆分成多份存储到不同节点上
  7. 支持多机房多区域部署
  8. 支持负载均衡和并行处理
  9. 数据可以在多个节点并行处理,性能更好
  10. 支持权限管理和多用户
  11. 支持多种文件存储类型
  12. 块存储(可以当做一块硬盘直接挂载到某台机器上或者容器内)、对象存储(s3、hadoop等协议)
  13. 支持普通硬件设计
  14. nfs存储性能就依赖于高性能磁盘,分布式存储可以通过缓存、中间件等,即使在普通硬件上也可以提高性能

分布式存储平台

主要对比主流的Ceph和CubeFS:

特性 Ceph CubeFS
设计目标 统一存储解决方案,支持块、对象、文件存储 专注于文件、对象存储
存储类型 对象、块、文件系统 对象、文件系统(目前不支持块存储)
扩展性 支持超大规模集群,PB-EB级 支持大规模集群,但是针对中小规模场景优化更好
性能 大文件性能优秀,小文件性能一般 小文件和大文件性能均优秀
元数据管理 基于RADOS分布式存储,元数据分布在OSD中 独立的Metadata节点管理元数据,避免瓶颈
部署复杂度 需要配置多组件(Monitor、Manager、OSD等)维护比k8s还困难
直接部署到k8s较困难,而且对接K8s也需要其它工具支持
轻量级,部署简单,适合云原生环境
硬件要求 对磁盘IO和网络带宽要求较高 硬件要求相对较低(企业级也要用高性能磁盘和万兆带宽)
社区活跃度 社区庞大、文档丰富、生态成熟 中文文档齐全(由OPPO开源的)
成熟度 技术成熟,已在生产环境中广泛使用 较新的项目,生产环境验证时间较短
使用场景 通用存储需求,企业级数据中心 云原生环境、大数据分析、AI训练、容器化等场景,有专门优化。
兼容性 支持多种协议(S3、iSCSI、NFS、HDFS等) 支持多种协议,主要针对对象、文件存储
扩展方式 水平扩展、支持动态扩展 水平扩展、支持动态扩展
容错机制 多副本嚯纠删码机制 多副本或纠删码机制(文件经常访问-多副本好一些;不经常访问-纠删码好一些)

k8s上落地分布式存储平台

裸机部署分布式存储平台,管理很复杂,而且对故障的承受能力也比较差。

在k8s上部署分布式存储平台,有如下优势:

  1. 简化部署和管理
  2. 不需要在每台机器上维护配置文件。用helm等工具在k8s上实现IaC,方便管理
  3. 自动化运维
  4. 一键式动态扩展
  5. daemonset实现每个节点的配置相同
  6. 故障自愈和高可用性
  7. 健康检查、自动故障恢复漂移
  8. 云原生生态集成
  9. 云原生应用可以直接用CSI对接到存储

CubeFS介绍

官网:CubeFS - 云原生分布式存储系统

CubeFS是新一代云原生存储产品,目前已经是CNCF毕业的开源项目。兼容S3、POSIX、HDFS等多种访问协议,支持多副本与纠删码两种存储引擎,提供多租户、多AZ部署以及跨区域复制等多种特性。广泛应用于大数据、AI、容器平台、数据库、中间件存算分离、数据共享以及数据保护等场景。

比如大模型服务,部署一个deepseek,671B的参数量,模型文件就得接近上TB。模型文件越多,对存储性能要求越高,放到CubeFS上,吞吐量很优秀,可以支持大模型服务。放到单节点存储上会拖慢大模型速度。

特性

  1. 多协议:S3、POSIX、HDFS
  2. 双引擎:支持多副本和纠删码
  3. 多租户:支持多租户隔离和权限分配
  4. 可扩展:支持各模块水平扩展、轻松扩展到PB或EB级
  5. 高性能:支持多级缓存、支持多种高性能的复制协议。
  6. 云原生:自带CSI插件,一键集成到k8s
  7. 多场景:大数据分析、机器学习、深度训练、共享存储、对象存储、数据库中间件等。

架构

image-20250828165135446

  1. Master Node:

集群管理中枢和请求入口,管理其他节点,分发请求到其他节点

  1. Meta Node:

元数据节点,存储文件metadata:文件名称、存储状态、副本状态等。这些数据都是缓存到内存中的

  1. Data Node:

数据节点,真正存储数据的地方。如果采用副本模式就是Data Node;如果采用纠删码模式就是Blob Node。

  1. Object Node:

CubeFS也是支持S3协议的,object node对外提供s3协议,应用通过object node作为入口访问到对象存储。

企业级分布式存储架构设计

部署方式

混合部署

混合部署指的是已经有一个现成的k8s集群,已经部署了一些业务应用,再把分布式存储平台也部署上去。

不过是部署在特殊节点上,通过打污点的形式,不让其他应用pod部署上去。

对于CubeFS的挂载,应用可以通过Master节点的service来挂载,或者通过object node来挂载对象存储。

独立部署-基础设施集群

是企业中比较常见、典型、可靠的部署方式:

  1. 把一些通用服务单独部署到基础设施集群中(比如CubeFS、prometheus、grafana、ECK等)
  2. 应用业务单独部署在业务集群中

基础设施集群中的通用服务怎么对外提供服务?需要看服务是通过什么协议代理的:

  1. 通过TCP四层协议代理的,比如mysql、redis:
  2. 可以在两个集群中间加一个F5(负载均衡器)
  3. 可以把服务通过NodePort暴露
  4. 通过HTTP七层协议代理的
  5. 可以通过ingress,暴露一个内部域名,只能内网去解析。如果没有内网域名,可以在业务集群的coreDNS上配置自定义解析,解析到基础设施集群的入口。
  6. 也可以通过istio ingressGateway暴露域名。

资源分配建议

总内存

以CubeFS为例,元数据节点总内存计算规则:每个文件元数据占用空间2KB-4KB。

  • 根据文件数量预估:
  • 假设已知的文件数量预估为10亿,则需要的内存KB为:20亿KB
  • 换算为GB:20亿/1024/1024 = 2000GB = 2TB内存

  • 根据数据量预估:

  • 假设集群数据总量为10PB = 10240TB = 10737418240MB
  • 通过CubeFS默认分片大小8MB预估,可能需要10737418240/8=1342177280个文件
  • 通过计算规则,需要的内存为:1342177280*2 = 2684354560KB = 2500GB = 2.5TB

服务器硬件

假设需要落地1PB CubeFS存储(需要256G内存)。

常见的配置:一台16硬盘位的存储服务器:

  • 每个硬盘位插一个16T的SAS硬盘(目前SAS盘性价比最高),这样一台存储服务器就有256T硬盘空间,四台就能满足1PB了。
  • 四个内存槽,每个插上64G内存条,总共256G内存,四台服务器就有约1TB内存了,远超需求量。(内存一般来说是容易满足的,而且服务器内存条也比较便宜,不会成为瓶颈)

如果担心16盘位受到服务器故障影响较大,可以换成8盘位的服务器:

  • 每个硬盘位插一个16T的SAS硬盘,8台可以满足1PB。
  • 内存槽4个,每个槽插一个32G内存条,8台也是1TB内存,足够用。

所以就把服务器盘位、硬盘容量规划好就行,内存一般不会成为瓶颈。

CubeFS部署

部署架构

image-20250828180510573

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

配置标签

CubeFS 安装时会根据这些标签通过nodeSelector进行匹配,然后在机器创建起对应的Pod

实验环境下就三个节点,所有节点都用作所有三个角色:

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

配置数据盘

搭建分布式存储,是需要专门挂载磁盘的。

在配置了kubectl label node <nodename> component.cubefs.io/datanode=enabled的数据节点上,对数据盘进行初始化操作。

首先添加一个新盘,然后通过fdisk -l查看:

fdisk -l | grep dev/sd
# Disk /dev/sdb: 6 GiB, 6442450944 bytes, 12582912 sectors

磁盘空间要求

磁盘可用空间必须大于10G,否则磁盘会被cubeFS标记为ReadOnly,并且空间计算也会不准确。

参考:[Bug]: the datanodes is not WRITABLE - Issue #3173

格式化磁盘并挂载:

# 格式化硬盘 
mkfs.xfs -f /dev/sdb 
# 创建挂载目录,如果机器上存在多个需要挂载的数据磁盘,则每个磁盘按以上步骤进行格式化和挂载磁盘,挂载目录按照data0/data1/../data999的顺序命名 
mkdir /data0 
# 挂载磁盘 
mount /dev/sdb /data0 

# 开机自动挂载
blkid /dev/sdb # 获取磁盘UUID
tee -a /etc/fstab <<'EOF' 
UUID=c94b2053-bf99-4268-8255-bcbe4e6e4b7f   /data0   xfs    defaults  0 0
EOF
mount -a 

注意

如果有多个数据节点,需要保证每个数据节点的磁盘数量、大小等配置是完全一致的。因为后面配置数据节点的时候,是所有数据节点统一配置的。这样出错的概率小。

helm安装

官网指南:CubeFS K8s部署指南

github地址:cubefs-helm - GitHub

下载helm包

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

配置values

组件和镜像

# 临时关闭一些组件 
cd cubefs-helm/cubefs 
vim values.yaml 
  client: false 
  csi: false 
  monitor: false 
  ingress: false 
# 调整组件的镜像地址(如果报错说明镜像源还不支持这些新版镜像,还是用默认的,手动传到节点解压)
image: 
  server: m.daocloud.io/docker.io/cubefs/cfs-server:v3.4.0 
  client: m.daocloud.io/docker.io/cubefs/cfs-client:v3.4.0 
  blobstore: m.daocloud.io/docker.io/cubefs/blobstore:v3.4.0 
  csi_driver: m.daocloud.io/docker.io/cubefs/cfs-csi-driver:v3.4.0 
  csi_provisioner: m.daocloud.io/registry.k8s.io/sig-storage/csi-provisioner:v2.2.2 
  csi_attacher: m.daocloud.io/registry.k8s.io/sig-storage/csi-attacher:v3.4.0 
  csi_resizer: m.daocloud.io/registry.k8s.io/sig-storage/csi-resizer:v1.3.0 
  driver_registrar: m.daocloud.io/registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.5.0 
  grafana: m.daocloud.io/docker.io/grafana/grafana:6.4.4 
  prometheus: m.daocloud.io/docker.io/prom/prometheus:v2.13.1 
  consul: m.daocloud.io/docker.io/library/consul:1.6.1 

主节点配置

# 主节点副本数,建议奇数 
master: 
  replicas: 3
  tolerations:
    - key: "node-role.kubernetes.io/control-plane"
      operator: "Equal"
      effect: "NoSchedule"
# 资源请求: 
  resources:
    enabled: true
    requests: 
      memory: "256Mi" 
      cpu: "200m" 
    limits: 
      memory: "2Gi" 
      cpu: "2000m" 
# 生产环境建议: 
    requests: 
      memory: "8Gi" 
      cpu: "2000m" 
    limits: 
      memory: "32Gi" 
      cpu: "8000m"

元数据节点配置

metanode: 
  # metanode可用的内存,建议主机内存的80% 
  total_mem: "7516192768" # 这里配置7G
# 资源配置 
  resources:
    enabled: true
    requests: 
      memory: "256Mi" 
      cpu: "200m" 
    limits: 
      memory: "2Gi" 
      cpu: "2000m" 
# 生产环境建议 
  resources:
    enabled: true
    requests: 
      memory: "32Gi" 
      cpu: "2000m" 
    limits: 
      memory: "256Gi" # 要比上面的total_mem的值稍微大一些。比如上面配置了128G,这里配置132G。给一些buffer
      cpu: "8000m" 

数据节点配置

# 数据盘配置 
# datanode.disks字段用于配置数据的挂载点,同时可以为每个磁盘保留一定的空间 
  disks:
    # 格式: 挂载点:保留的空间
    # 保留的空间: 单位字节,当磁盘剩余空间小于该值时将不会再在该磁盘上写入数据
    - /data0:536870912
# 资源配置:
  resources:
    enabled: true
    requests: 
      memory: "256Mi" 
      cpu: "200m" 
    limits: 
      memory: "2Gi" 
      cpu: "2000m" 
# 生产环境建议 
    requests: 
      memory: "8Gi" 
      cpu: "2000m" 
    limits: 
      memory: "32Gi" 
      cpu: "8000m" 

对象存储节点配置

objectnode: 
  # 对象存储的域名 
  region: "spark" 
# 对象存储的域名。公司内部有域名就用,没有就用这个默认的。
domains: "objectcfs.cubefs.io,objectnode.cubefs.io" 

安装CubeFS

cd ./cubefs-helm/cubefs
helm upgrade -i cubefs -n cubefs . --create-namespace -f values.yaml 

版本Bug

2025-08-28 安装的3.3.2.150.0 Release版本,有个bug:

cubefs-helm/cubefs/templates/statefulset-master.yaml文件的第80行,需要把:

value: {{ .Value.master.legacy_data_media_type | quote }} 他的Value加上s:

value: {{ .Values.master.legacy_data_media_type | quote }}

否则会报错:Error: template: cubefs/templates/statefulset-master.yaml:80:30: executing "cubefs/templates/statefulset-master.yaml" at <.Value.master.legacy_data_media_type>: nil pointer evaluating interface {}.master

如果遇到启动失败的,可以在对应的节点上,查看/var/log/cubefs下的日志。

CubeFS客户端部署使用

官网:CubeFS CLI概览

下载工具包

# 下载地址: https://github.com/cubefs/cubefs/releases/ 
tar xf cubefs-3.5.0-linux-amd64.tar.gz 
cd build/bin 
cp ./cfs-cli /usr/local/bin/
cfs-cli --version

客户端配置

cfs-cli cluster info
# 报错解析不了域名
# 修改配置
vim ~/.cfs-cli.json
# 更改masterAddr为master service的svc ip 
{ 
  "masterAddr": [ 
    "10.99.61.70:17010" 
  ], 
  "timeout": 60 
} 
# 查询集群状态
./cfs-cli cluster info

集群管理

# 查看集群信息
cfs-cli cluster info
# 获取集群状态
cfs-cli cluster stat 

Metanode的Total为最大可用内存,由所有metanode的MaxMemAvailWeight之和计算得来。

设置卷删除延迟的时间,表示卷被删除多久才会被彻底删除,默认48h,在此之前可以恢复:

cfs-cli cluster volDeletionDelayTime 72 

元数据节点管理

列出所有的元数据节点,包括ID、地址、读写状态及存活状态等:

cfs-cli metanode list

查看某个节点的详细信息:

cfs-cli metanode info rn1:17210

数据节点管理

列举所有的数据节点,包括ID、地址、读写状态和存活状态:

cfs-cli datanode list

展示某个节点的详细信息:

cfs-cli datanode info rn2:17310

下线数据节点,下线之后该该节点的数据会自动迁移到其他数据节点。

cfs-cli datanode decommission rn2:17310
# 下线之后,该节点无法查看,可用数据空间也降低
cfs-cli datanode info rn2:17310
cfs-cli cluster stat

该下线节点的datanode pod不会被删除,删掉pod重建之后,这个数据节点就会重新加入,数据空间恢复。

数据卷管理

数据卷可以给服务使用或者挂载到宿主机。如果是外部服务需要使用cubeFS,那就需要创建卷给外部服务用。如果存储是在k8s内部,那么就不需要创建卷了,直接用csi管理。

列出所有的卷:

cfs-cli volume list

创建一个卷:

# 命令格式:cfs-cli volume create [VOLUME NAME] [USER ID] [flags] 
# user id可以随便写,会自动创建这个用户
cfs-cli volume create volume-test test --capacity 1

列出卷:

cfs-cli volume list

查看某个卷的详细信息:

cfs-cli volume info volume-test

禁用卷、取消禁用

cfs-cli volume set-forbidden volume-test true 
cfs-cli volume info volume-test | grep -i Forbidden 
# 取消禁用
cfs-cli volume set-forbidden volume-test false 
cfs-cli volume info volume-test | grep -i Forbidden

卷扩容、更新卷:

cfs-cli volume update volume-test --capacity 2
cfs-cli volume list

添加空间限制(空间满了之后就不允许写入了):

cfs-cli volume update volume-test --readonly-when-full true 

删除卷:

cfs-cli volume delete volume-test -y

CubeFS用户管理

CubeFS支持多用户,可以为每个用户对每个卷分配不同的权限,同时也可为对象存储提供用户认证。

主要应用场景是对象存储给不同程序去用的时候,不同的卷权限不同,用户名密码也不同。

cfs-cli user create -h 
# 创建用户
cfs-cli user create hangx
# 获取用户信息
cfs-cli user info hangx
# 列举所有用户
cfs-cli user list
# 删除用户
cfs-cli user delete hangx --yes

CubeFS挂载测试

如果k8s集群外部裸机或者虚机部署的服务也想挂载cubeFS,也是可以的,挂载完成就等于访问一个本地文件夹一样了。

测试在K8s宿主机挂载cubeFS文件。由于masterAddr可以通过svc访问,就直接用svc地址了。如果对集群外部访问的话需要把master-service这个svc通过NodePort或者域名暴露出去。

客户端安装fuse:

yum install fuse -y 

cubeFS创建volume:

# 创建卷
cfs-cli volume create volume-test ltptest --capacity 1

客户端创建配置文件:

cat volume-test-client.conf  
{ 
  "mountPoint": "/volume-test", 
  "volName": "volume-test", 
  "owner": "ltptest", 
  "masterAddr": "10.99.61.70:17010", 
  "logDir": "/cfs/client/log", 
  "logLevel": "info", 
  "profPort": "27510" 
} 

执行挂载:

# 用的是另一个叫cfs-client的客户端工具
cp /root/cubefs-client/build/bin/cfs-client /usr/local/bin/
cfs-client -c volume-test-client.conf
# 这会:
# 1. 读取配置文件中的集群连接信息
# 2. 通过 FUSE 创建一个虚拟文件系统挂载点
# 3. 将文件系统操作转换为网络请求发送给 CubeFS 集群
df -Th | grep volume-test

挂载协议

这种挂载方式使用的是传统的POSIX。CubeFS 客户端通过 FUSE (Filesystem in Userspace) 提供标准的 POSIX 文件系统语义

测试写入数据:

dd if=/dev/zero of=./testdata bs=1M count=512
dd if=/dev/zero of=./testdata2 bs=128M count=4
# 查看卷使用
cfs-cli volume list

CubeFS扩容

新加磁盘

如果CubeFS是部署在K8s中的,扩容时需要给每个datanode都添加同样的磁盘。要注意新加的盘要和原来的盘大小、性能、型号一样,这样集群的balance不会受到影响。

# 加完盘进系统,格式化
mkfs.xfs -f /dev/sdc
# 创建挂载目录,如果机器上存在多个需要挂载的数据磁盘,则每个磁盘按以上步骤进行格式化和挂载磁盘,挂载目录按照data0/data1/../data999的顺序命名 
mkdir /data1 
# 挂载磁盘 
mount /dev/sdc /data1
blkid /dev/sdc
tee -a /etc/fstab <<'EOF' 
UUID=f3a1fd89-ff67-4512-b2d3-e914440d4b84   /data0   xfs    defaults  0 0
EOF
mount -a 

更新helm配置:

  disks:
    # 格式: 挂载点:保留的空间
    # 保留的空间: 单位字节,当磁盘剩余空间小于该值时将不会再在该磁盘上写入数据
    - /data0:536870912
    - /data1:536870912
helm upgrade -i cubefs -n cubefs . --create-namespace -f values.yaml

触发datanode pod重启:

kubectl delete po -n cubefs -l app.kubernetes.io/component=datanode

查看集群状态:

cfs-cli cluster stat 

基于主机的扩容

基于主机的扩容,需要通过添加datanode节点来完成。

  1. 添加一个新节点(k8s层面先加上),已有节点可以忽略

  2. 在新节点上添加和当前配置、数量、大小一样的硬盘,并挂载

  3. 在新节点上打component.cubefs.io/datanode=enabled标签即可:

kubectl label node xxx component.cubefs.io/datanode=enabled

CubeFS对象存储

程序直接连接到存储平台对文件读写操作,不需要挂载、不需要创建PV、PVC。

对象存储基本使用

要测试对象存储,可以自己写程序,或者用一些对象存储客户端工具。CubeFS自己没没有提供对象存储的客户端工具,得用别的。

下载Minio对象存储客户端:(Minio也是提供对象存储的平台,但是只提供对象存储。但是不论使用哪个平台,协议都是s3。)

curl https://dl.minio.org.cn/client/mc/release/linux-amd64/mc --create-dirs -o /usr/local/bin/mc
chmod +x /usr/local/bin/mc 

配置对象存储:添加 MinIO/S3 服务器

# host add test是user的name
# 对象存储的入口:objectnode-service的svc的clusterIP
# 后面是cubefs的user的access key和secret key。这其实就是对象存储的用户名和密码
mc alias set test http://10.104.157.128:1601 NSwS4hTkT43TW903 uY9Hg8IgyW2awnRF3XpaAK8Da91c8RG7
# 查看alias
mc ls test

mc命令版本差异

  1. 旧版本语法mc config host add 是旧版本的语法
  2. 新版本语法mc alias set 是新版本的正确语法

创建桶:(创建桶的本质就是在cubeFS中创建了一个volume)

mc mb test/buckettest

查看桶:

mc ls test/

上传文件:

mc cp cubefs-3.5.0-linux-amd64.tar.gz test/buckettest/ 
# 上传文件夹(不会自动创建目录,需要指定子目录名称)
mc cp templates/ test/buckettest/templates -r

查看文件:

mc ls test/buckettest/ 

删除文件:

mc rm test/buckettest/cubefs-3.5.0-linux-amd64.tar.gz 

删除桶:

mc rb test/buckettest

删除服务器:

mc alias rm test

对象存储项目管理

为每个项目创建用户:

cfs-cli user create projecta

添加项目的host:(生产环境使用需要配置域名)

mc config host add projecta http://10.100.82.212:1601 sTnnilRm1YPuohs0 ayIjC5uZYdRmVRFYey8lDy73Whdg716l

这个项目可能会有很多微服务,这些微服务就共用同一套用户名密码。为每个微服务单独创建不同的桶。(共享数据的微服务就共用同一个桶)

mc mb projecta/appa

CubeFS对接K8s

部署cubeFS csi

首先给需要使用cubeFS存储的节点打上CSI的标签(一般直接给所有节点打上标签即可):

kubectl label node component.cubefs.io/csi=enabled --all

配置values,开启csi安装:

component: 
  master: true
  datanode: true
  metanode: true
  objectnode: true
  client: false
  csi: true

执行安装:

helm upgrade -i cubefs -n cubefs . --create-namespace -f values.yaml

tolerations缩进问题

  1. 如果values里面配置了tolerations,渲染模板会报错:
Error: UPGRADE FAILED: YAML parse error on cubefs/templates/csi-controller-deployment.yaml: error converting YAML to JSON: yaml: line 21: did not find expected key
   
Error: UPGRADE FAILED: YAML parse error on cubefs/templates/csi-node-daemonset.yaml: error converting YAML to JSON: yaml: line 21: did not find expected key

cubefs/templates/csi-controller-deployment.yaml

cubefs/templates/csi-node-daemonset.yaml

这两个文件都有spec.tolerations字段的缩进问题

  1. 用helm template输出报错的模板:
helm template cubefs -n cubefs . -f values.yaml --debug > error-template.yaml
  1. 分析可知模板中的tolerations字段缩进有问题:

原始文件(错的)

    {{- with .Values.csi.controller.nodeSelector }}
      nodeSelector:
{{ toYaml . | indent 8 }}
    {{- end }}
      {{- with .Values.csi.controller.tolerations }}
        tolerations:
      {{ toYaml . | indent 8 }}

改为正确缩进:

    {{- with .Values.csi.controller.nodeSelector }}
      nodeSelector:
{{ toYaml . | indent 8 }}
    {{- end }}
    {{- with .Values.csi.controller.tolerations }}
      tolerations:
{{ toYaml . | indent 8 }}
  1. 改完之后可以成功安装

  2. 在其他模板中又发现了更多缩进错误,我提了一个github issue:Indent Error found in spec.tolerations for some templates - Issue #53

PV/PVC测试

创建pvc:

apiVersion: v1 
kind: PersistentVolumeClaim 
metadata: 
  name: cubefs-test 
  namespace: default 
spec: 
  accessModes: 
    - ReadWriteMany # Once也可以,单节点读写
  resources: 
    requests: 
      storage: 1Gi
  storageClassName: cfs-sc 
  volumeMode: Filesystem

创建之后就能看到自动创建了PV。下面可以创建服务来挂载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: 
      # 只有安装了CSI驱动的才可以挂载存储 
      nodeSelector: 
        component.cubefs.io/csi: enabled
      volumes: 
        - name: mypvc
          persistentVolumeClaim: 
            claimName: cubefs-test
      containers: 
        - name: cfs-csi-demo 
          image: registry.cn-beijing.aliyuncs.com/dotbalo/rabbitmq:3.6.12-management  
          imagePullPolicy: "IfNotPresent" 
          ports: 
            - containerPort: 15672 
              name: "http-server" 
          volumeMounts: 
            - mountPath: "/var/lib/rabbitmq" 
              name: mypvc 

在线扩容

支持不停机扩容,直接把PVC的request改大,等待一段时间就生效了。Pod是不会收到影响的。

spec: 
  accessModes: 
  - ReadWriteMany 
  resources: 
    requests: 
      storage: 2Gi 
  storageClassName: cfs-sc 

CubeFS为基础组件提供存储

mysql单实例

单实例手动创建PVC。如果是多实例就需要用集群模式或者主从模式,用helm或者Operator去部署,在模板里面直接写storageclass就行

apiVersion: v1 
kind: PersistentVolumeClaim 
metadata: 
  name: mysql 
  namespace: default 
spec: 
  resources: 
    requests: 
      storage: 1Gi 
  volumeMode: Filesystem 
  storageClassName: cfs-sc 
  accessModes: 
    - ReadWriteOnce
---
apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: mysql 
  namespace: default 
spec: 
  replicas: 1 
  selector: 
    matchLabels: 
      app: mysql 
  strategy:
    type: Recreate # 不能用滚动更新,因为。两个mysql不能同时使用一块存储。
  template: 
    metadata: 
      labels: 
        app: mysql 
      annotations: 
        app: mysql 
    spec: 
      volumes: 
        - name: data 
          persistentVolumeClaim: 
            claimName: mysql 
      containers: 
        - name: mysql 
          image: registry.cn-beijing.aliyuncs.com/dotbalo/mysql:8.0.20 
          ports: 
            - name: tcp-3306 
              containerPort: 3306 
              protocol: TCP 
          volumeMounts: 
            - name: data 
              mountPath: /var/lib/mysql 
          env: 
            - name: MYSQL_ROOT_PASSWORD 
              value: mysql

CubeFS为大模型提供存储

CubeFS对AI训练、模型存储及分发、IO加速等需求做了专门优化。所以可以直接把CubeFS作为大模型的数据存储底座。

很多位置去启动大模型服务,可以共享同一个大模型的存储。

ollama

apiVersion: v1 
kind: PersistentVolumeClaim 
metadata: 
  name: ollama-data 
  namespace: default 
spec: 
  resources: 
    requests: 
      storage: 10Gi
  volumeMode: Filesystem 
  storageClassName: cfs-sc 
  accessModes: 
  - ReadWriteMany 
---
apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: ollama 
  namespace: default 
spec: 
  selector: 
    matchLabels: 
      app: ollama 
  replicas: 1 
  template: 
    metadata: 
      labels: 
        app: ollama 
      annotations: 
        app: ollama 
    spec: 
      volumes: 
      - name: data 
        persistentVolumeClaim: 
          claimName: ollama-data 
          readonly: false 
      containers: 
      - name: ollama 
        image: registry.cn-beijing.aliyuncs.com/dotbalo/ollama
        imagePullPolicy: IfNotPresent 
        volumeMounts: 
        - name: data 
          mountPath: /data/models 
          readonly: false 
        env: 
        - name: OLLAMA_MODELS 
          value: /data/models 

部署完成后进入pod下载模型ollama pull deepseek-r1:1.5b

下载完成之后启动模型:ollama run deepseek-r1:1.5b