EB级分布式高可用存储平台落地 1.分布式存储介绍 1.1单节点或NFS存储存在的问题 常见的存储设计如下:
1.2分布式存储的主要特性与优势 支持近乎无限的扩容 支持容错能力和数据冗余 支持多机房多区域部署 支持负载均衡和并行处理 支持权限管理和多用户 支持多种文件存储类型 支持普通硬件设计
1.3分布式存储平台对比
特性/项目
Ceph
CubeFS
设计目标
统一存储解决方案,支持对象、块和文件存储
专注于文件存储和对象存储,适用云原生和大数据场景
存储类型
对象存储、块存储、文件系统存储
文件存储、对象存储
扩展性
支持超大规模集群,PB级到EB级
支持大规模集群,但针对中小规模场景优化更好
性能
大文件性能优秀,小文件性能一般
小文件和大文件性能均优秀
元数据管理
基于RADOS分布式存储,元数据分布在0SD中
独立的 MetaNode 管理元数据,避免瓶颈
部署复杂度
较复杂,需要配置多个组件(Monitor、Manager、0SD等)
轻量级,部署简单,适合云原生环境
硬件要求
对磁盘I/O和网络带宽要求较高
硬件要求相对较低
社区活跃度
社区庞大,文档丰富,生态成熟
中文文档齐全,微信社区群等
成熟度
技术成熟,已在生产环境中广泛使用
较新项目,生产环境验证时间相对较短
适用场景
通用存储需求(对象、块、文件),企业级数据中心
云原生环境、大数据分析、AI 训练、容器化应用场景
兼容性
支持多种协议(S3、iSCSI、NFS、HDFS等)
支持多种协议,主要针对对象存储和文件存储
扩展方式
水平扩展,支持动态扩展
水平扩展,支持动态扩展
容错机制
多副本或纠删码机制
多副本或纠删码机制
1.4为什么要在K8s上落地存储平台 分布式存储架构复杂,在k8s上方便管理和维护
1.5云原生存储平台CubeFS介绍
官网:https://cubefs.io
CubeFs是新一代云原生存储产品,目前是云原生计算基金会(CNCF)托管的毕业开源项目,兼容S3、POSIX、HDFS等多种访问协议,支持多副本与纠删码两种存储引擎,为用户提供多租户、多AZ部署以及跨区域复制等多种特性,广泛应用于大数据、AI、容器平台、数据库、中间件存算分离、数据共享以及数据保护等场景。
CubeFs特性: 多协议:支持S3、POSIX、HDFS 双引擎:支持多副本和纠删码 多租户:支持多租户隔离和权限分配 可扩展:支持各模块水平扩展,轻松扩展到PB或EB级高性能:支持多级缓存、支持多种高性能的复制协议 云原生:自带CSI插件,一键集成Kubernetes 多场景:大数据分析、机器学习、深度训练、共享存储、对象存储、数据库中间件等
CubeFS核心架构:
1.5分布式存储架构设计 混合部署:在一套k8s集群中部署,和应用节点在一起,通过污点控制存储pod所在的节点:
独立部署:使用独立的k8s集群部署,不和应用集群在意思
1.6CubeFS资源分配建议 元数据节点总内存计算规则:每个文件元数据占用空间2KB~4KB左右根据文件数量预估: 假设已知的文件数量预估为10亿 通过计算规则需要的内存KB为: 20亿KB 换算为G: 2000000000/1024/1024约等于2000G
根据数据量预估: 假设集群数据总量为 10PB = 10240TB = 10737418240MB 通过默认分片大小8MB预估,可能需要10737418240 / 8约等于 1342177280 个文件 通过计算规则需要的内存KB为: 2684354560KB 约为 2500G
服务器硬件设计:
分布式存储落地与实战 2.分布式存储落地与实战 2.1CubeFs部署架构介绍及部署
CubeFS 目前由这四部分组成:
Master:资源管理节点,负责维护整个集群的元信息,部署为 StatefulSet 资源
DataNode:数据存储节点,需要挂载大量磁盘负责文件数据的实际存储,部署为 DaemonSet 资源
MetaNode:元数据节点,负责存储所有的文件元信息,部署为 DaemonSet 资源
ObjectNode:负责提供转换 S3 协议提供对象存储的能力,无状态服务,部署为 Deployment 资源
首先给节点打上标签,用来标记部署什么服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 # Master 节点,至少三个,建议为奇数个 # kubectl label node <nodename> component.cubefs.io/master=enabled [root@k8s-master01 34-cubefs]# kubectl label node k8s-master01 component.cubefs.io/master=enabled node/k8s-master01 labeled [root@k8s-master01 34-cubefs]# kubectl label node k8s-master02 component.cubefs.io/master=enabled node/k8s-master02 labeled [root@k8s-master01 34-cubefs]# kubectl label node k8s-master03 component.cubefs.io/master=enabled node/k8s-master03 labeled # MetaNode 元数据节点,至少 3 个,奇偶无所谓 # kubectl label node <nodename> component.cubefs.io/metanode=enabled [root@k8s-master01 34-cubefs]# kubectl label node k8s-master01 component.cubefs.io/metanode=enabled node/k8s-master01 labeled [root@k8s-master01 34-cubefs]# kubectl label node k8s-master02 component.cubefs.io/metanode=enabled node/k8s-master02 labeled [root@k8s-master01 34-cubefs]# kubectl label node k8s-master03 component.cubefs.io/metanode=enabled node/k8s-master03 labeled # DataNode 数据节点,至少 3 个,奇偶无所谓 # kubectl label node <nodename> component.cubefs.io/datanode=enabled [root@k8s-master01 34-cubefs]# kubectl label node k8s-master01 component.cubefs.io/datanode=enabled node/k8s-master01 labeled [root@k8s-master01 34-cubefs]# kubectl label node k8s-master02 component.cubefs.io/datanode=enabled node/k8s-master02 labeled [root@k8s-master01 34-cubefs]# kubectl label node k8s-master03 component.cubefs.io/datanode=enabled node/k8s-master03 labeled # ObjectNode 对象存储节点,可以按需进行标记,不需要对象存储功能的话也可以不部署这个组件 # kubectl label node component.cubefs.io/objectnode=enabled [root@k8s-master01 34-cubefs]# kubectl label node k8s-master01 component.cubefs.io/objectnode=enabled node/k8s-master01 labeled [root@k8s-master01 34-cubefs]# kubectl label node k8s-master02 component.cubefs.io/objectnode=enabled node/k8s-master02 labeled [root@k8s-master01 34-cubefs]# kubectl label node k8s-master03 component.cubefs.io/objectnode=enabled node/k8s-master03 labeled
2.2数据盘配置 在数据节点,也就是配置了 component.cubefs.io/datanode=enabled 标签的节点上,对数据盘进行初始化操 作。
这里使用vmware作为演示:
磁盘大小根据自己情况来,我这里用50G:
其他数据节点也是一样,增加一块新的硬盘。
首先需要添加一个新盘,然后通过fdisk -l 查看:
1 2 3 4 5 6 [root@k8s-master01 ~]# fdisk -l|grep dev Disk /dev/nvme0n1: 100 GiB, 107374182400 bytes, 209715200 sectors /dev/nvme0n1p1 * 2048 2099199 2097152 1G 83 Linux /dev/nvme0n1p2 2099200 209715199 207616000 99G 8e Linux LVM Disk /dev/nvme0n2: 50 GiB, 53687091200 bytes, 104857600 sectors Disk /dev/mapper/rl-root: 99 GiB, 106296246272 bytes, 207609856 sectors
格式化每个磁盘并挂载(所有的DataNode节点都要操作):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 # 格式化硬盘 [root@k8s-master01 ~]# mkfs.xfs -f /dev/nvme0n2 meta-data=/dev/nvme0n2 isize=512 agcount=4, agsize=3276800 blks = sectsz=512 attr=2, projid32bit=1 = crc=1 finobt=1, sparse=1, rmapbt=0 = reflink=1 bigtime=1 inobtcount=1 nrext64=0 data = bsize=4096 blocks=13107200, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0, ftype=1 log =internal log bsize=4096 blocks=16384, version=2 = sectsz=512 sunit=0 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0 # 创建挂载目录,如果机器上存在多个需要挂载的数据磁盘,则每个磁盘按以上步骤进行格式化和挂载磁盘,挂载目录按照data0/data1/../data999的顺序命名 mkdir /data0 # 挂载磁盘 mount /dev/nvme0n2 /data0/ # 设置为开机自动挂载,使用uuid进行挂载: # [root@k8s-master01 cubefs-helm]# blkid /dev/nvme0n2 /dev/nvme0n2: UUID="329fe4ea-fc45-4670-ab81-5c9929a10978" TYPE="xfs" # 写入配置文件/etc/fstab中 vim /etc/fstab # 添加一行信息 UUID=329fe4ea-fc45-4670-ab81-5c9929a10978 /data0 xfs defaults 0 0 # 执行立即生效命令 mount -a
2.3 安装helm 下载安装包:https://helm.sh/docs/intro/install/
1 2 3 4 5 6 7 8 9 10 # 将下按照的安装包上传到服务器 [root@k8s-master01 34-cubefs]# ll total 19504 -rw-r--r-- 1 root root 19969382 Dec 16 21:10 helm-v4.0.4-linux-amd64.tar.gz [root@k8s-master01 34-cubefs]# tar -xf helm-v4.0.4-linux-amd64.tar.gz [root@k8s-master01 34-cubefs]# mv linux-amd64/helm /usr/local/bin/ [root@k8s-master01 34-cubefs]# helm version version.BuildInfo{Version:"v4.0.4", GitCommit:"8650e1dad9e6ae38b41f60b712af9218a0d8cc11", GitTreeState:"clean", GoVersion:"go1.25.5", KubeClientVersion:"v1.34"}
下载安装文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@k8s-master01 34-cubefs]# git clone https://github.com/cubefs/cubefs-helm.git Cloning into 'cubefs-helm'... remote: Enumerating objects: 854, done. remote: Counting objects: 100% (58/58), done. remote: Compressing objects: 100% (37/37), done. remote: Total 854 (delta 25), reused 24 (delta 21), pack-reused 796 (from 1) Receiving objects: 100% (854/854), 502.92 KiB | 613.00 KiB/s, done. Resolving deltas: 100% (542/542), done. [root@k8s-master01 34-cubefs]# cd cubefs-helm/ [root@k8s-master01 cubefs-helm]# ll total 32 drwxr-xr-x 2 root root 63 Dec 16 21:23 assets drwxr-xr-x 2 root root 138 Dec 16 21:23 build -rw-r--r-- 1 root root 138 Dec 16 21:23 CODE_OF_CONDUCT.md drwxr-xr-x 4 root root 93 Dec 16 21:23 cubefs drwxr-xr-x 2 root root 110 Dec 16 21:23 examples -rw-r--r-- 1 root root 11351 Dec 16 21:23 LICENSE -rw-r--r-- 1 root root 155 Dec 16 21:23 Makefile -rw-r--r-- 1 root root 136 Dec 16 21:23 OWNERS -rw-r--r-- 1 root root 7388 Dec 16 21:23 README.md drwxr-xr-x 2 root root 33 Dec 16 21:23 scripts
2.4修改CubeFS配置 调整安装配置,关闭一些不需要的组件,更改镜像的下载地址:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 # 关闭不用的组件 [root@k8s-master01 34-cubefs]# cd cubefs-helm/cubefs/ # 编辑values.yaml文件 [root@k8s-master01 cubefs]# vim values.yaml # 下列配置是修改后的配置 # Select which component to install component: master: true datanode: true metanode: true objectnode: true client: false csi: false monitor: false ingress: false blobstore_clustermgr: false blobstore_blobnode: false blobstore_proxy: false blobstore_scheduler: false blobstore_access: false # 调整镜像地址 image: server: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/cfs-server:v3.5.0 client: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/cfs-client:v3.5.0 blobstore: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/blobstore:v3.4.0 # CSI related images csi_driver: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/cfs-csi-driver:v3.5.0 csi_provisioner: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/csi-provisioner:v2.2.2 csi_attacher: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/csi-attacher:v3.4.0 csi_resizer: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/csi-resizer:v1.3.0 driver_registrar: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/csi-node-driver-registrar:v2.5.0 grafana: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/grafana:6.4.4 prometheus: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/prometheus:v2.13.1 consul: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/consul:1.6.1 pull_policy: "IfNotPresent" # 数据节点配置 datanode: # NodeSelector for datanode daemonset nodeSelector: "component.cubefs.io/datanode": "enabled" tolerations: [ ] log_level: error port: 17310 prof: 17320 raft_heartbeat: 17330 raft_replica: 17340 exporter_port: 9520 # Disks will be used by datanode to storage data # Format: disk_mount_point:reserved_space # disk_mount_point: the mount point of disk in machine # reserved_space: similar to metanode reserved space, if disk available # space less than this number, then the disk will be unwritable disks: # 字段用于配置数据的挂载点,同时可以为每个磁盘保留一定的空间 - /data0:2147483648 #这里预留大约2G的空间,避免爆盘 # - /data1:21474836480 media_type: 1 # 这里给的资源配置是用在个人测试环境 resources: enabled: false requests: memory: "512Mi" cpu: "200m" limits: memory: "2Gi" cpu: "2000m" # 生产环境建议使用该配置 # requests: # memory: "8Gi" # cpu: "2000m" # limits: # memory: "32Gi" # cpu: "8000m" # 主节点配置 master: # The replicas of master component, at least 3, recommend to be an odd number replicas: 3 #定义主节点的节点数量,建议奇数个 # 修改资源请求,该配置用于测试 resources: enabled: false requests: memory: "512Mi" cpu: "200m" limits: memory: "2Gi" cpu: "2000m" # 生产环境建议使用该配置 # requests: # memory: "8Gi" # cpu: "2000m" # limits: # memory: "32Gi" # cpu: "8000m" # 元数据节点配置 metanode: total_mem: "6871947673" #定义metanode可用的内存,建议主机的80% ,我的主机8G,这里设置大约6.4G,单位是字节 port: 17210 prof: 17220 raft_heartbeat: 17230 raft_replica: 17240 exporter_port: 9510 # 修改资源请求,该配置用于测试 resources: enabled: true requests: memory: "512Mi" cpu: "200m" limits: memory: "2Gi" cpu: "2000m" # 生产环境建议使用该配置 # requests: # memory: "32Gi" # cpu: "2000m" # limits: # memory: "256Gi" # cpu: "8000m" # 对象存储域名,如果有可以设置自己的域名,没有域名可以不修改,我这里保持默认 objectnode: # 对象存储的域名 region: "spark" # Domains listed here will be used in resolution of pan-domain names to parse bucket name # 对象存储的域名 domains: "objectcfs.cubefs.io,objectnode.cubefs.io" host: objectnode.cubefs.com
2.5部署CubeFS 执行部署指令:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 # 在执行安装指令前可以先执行 helm lint . 命令,检查文件中是否存在语法错误 [root@k8s-master01 cubefs]# helm upgrade --install cubefs -n cubefs --create-namespace . Release "cubefs" does not exist. Installing it now. NAME: cubefs LAST DEPLOYED: Tue Dec 16 22:49:43 2025 NAMESPACE: cubefs STATUS: deployed REVISION: 1 DESCRIPTION: Install complete TEST SUITE: None # 记录安装错误 [root@k8s-master01 cubefs]# helm upgrade --install cubefs -n cubefs --create-namespace . Release "cubefs" does not exist. Installing it now. Error: 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 # 上述错误解决方法:应该是Values,而配置文件写的时Value,少了个s 修改cubefs/templates/statefulset-master.yaml的第80行,将value: {{ .Value.master.legacy_data_media_type | quote }}改为value: {{ .Values.master.legacy_data_media_type | quote }} # 检查语法,没有错误在执行安装命令 [root@k8s-master01 cubefs]# helm lint . ==> Linting . 1 chart(s) linted, 0 chart(s) failed # 检查pod运行情况,Running表示正常 [root@k8s-master01 cubefs]# kubectl get po -n cubefs NAME READY STATUS RESTARTS AGE datanode-jtp52 1/1 Running 0 2m6s datanode-nngdt 1/1 Running 0 2m6s datanode-q5qx8 1/1 Running 0 2m6s master-0 1/1 Running 0 2m6s master-1 1/1 Running 0 52s master-2 1/1 Running 0 29s metanode-6st64 1/1 Running 0 2m6s metanode-ksjh6 1/1 Running 0 2m6s metanode-vg79k 1/1 Running 0 2m6s objectnode-7669844df5-57fm5 1/1 Running 0 2m6s objectnode-7669844df5-bbpvl 1/1 Running 0 2m6s objectnode-7669844df5-k6hb4 1/1 Running 0 2m6s # 如果有报错可以在/var/log/cubefs中查找相关日志 [root@k8s-master01 ~]# ll /var/log/cubefs/ total 24 drwxr-xr-x 2 root root 4096 Dec 17 00:00 dataNode -rw-r--r-- 1 root root 1 Dec 16 22:51 datanode.pid drwxr-xr-x 2 root root 4096 Dec 17 00:00 master -rw-r--r-- 1 root root 1 Dec 16 22:50 master.pid drwxr-xr-x 2 root root 4096 Dec 17 00:00 metaNode -rw-r--r-- 1 root root 1 Dec 16 22:51 metanode.pid drwxr-xr-x 2 root root 42 Dec 16 22:52 oplogs drwxr-xr-x 2 root root 26 Dec 16 22:51 stat
2.6CubeFS客户端使用 官网介绍:https://cubefs.io/zh/docs/master/user-guide/cli/overview.html#%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95
下载客户端工具:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 # 下载地址 https://github.com/cubefs/cubefs/releases/download/v3.5.0/cubefs-3.5.0-linux-amd64.tar.gz # 将下载好的客户端工具上传到服务 [root@k8s-master01 34-cubefs]# ll total 107644 -rw-r--r-- 1 root root 90248682 Dec 17 14:16 cubefs-3.5.0-linux-amd64.tar.gz drwxr-xr-x 9 root root 4096 Dec 16 21:23 cubefs-helm -rw-r--r-- 1 root root 19969382 Dec 16 21:10 helm-v4.0.4-linux-amd64.tar.gz drwxr-xr-x 2 1001 1001 38 Dec 16 21:16 linux-amd64 # 解压 [root@k8s-master01 34-cubefs]# tar -xf cubefs-3.5.0-linux-amd64.tar.gz # 将客户端拷贝到/usr/local/bin/目录 [root@k8s-master01 34-cubefs]# cp cubefs/build/bin/ blobstore/ cfs-bcache cfs-client cfs-fsck cfs-server libcfs.h cfs-authtool cfs-cli cfs-deploy cfs-preload fdstore libcfs.so [root@k8s-master01 34-cubefs]# cp cubefs/build/bin/cfs-cli cfs-cli cfs-client [root@k8s-master01 34-cubefs]# cp -rp cubefs/build/bin/cfs-cli /usr/local/bin/ # 查看版本信息 [root@k8s-master01 34-cubefs]# cfs-cli --version CubeFS CLI Version : v3.5.0 Branch : HEAD Commit : 10353bf433fefd51c6eef564035c8a682515789c Build : go1.20.4 linux amd64 2025-03-17 17:40 # 查看集群信息,该报错是客户端不知道连接的集群是哪个 [root@k8s-master01 34-cubefs]# cfs-cli cluster info Error: Get "http://master.cube.io/admin/getCluster?volStorageClass=false": dial tcp: lookup master.cube.io on 223.5.5.5:53: no such host
配置客户端,让客户端知道连接我们自己部署的集群:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 # 在当前用户的家目录中创建下面文件,该文件已经存在,直接编辑 [root@k8s-master01 ~]# ll -a|grep .cfs-cli.json -rw------- 1 root root 65 Dec 17 14:29 .cfs-cli.json [root@k8s-master01 ~]# vim ~/.cfs-cli.json { "masterAddr": [ "10.96.167.132:17010" #修改链接地址 ], "timeout": 60 } # 10.96.167.132和17010是master-service这个svc的ip和端口 kubec[root@k8s-master01 ~]# kubectl get svc -n cubefs NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE master-service ClusterIP 10.96.167.132 <none> 17010/TCP 15h objectnode-service ClusterIP 10.96.96.158 <none> 1601/TCP 15h # 再次执行cfs-cli cluster info查看信息 [root@k8s-master01 ~]# cfs-cli cluster info [Cluster] Cluster name : my-cluster Master leader : master-0.master-service:17010 Master-1 : master-0.master-service:17010 Master-2 : master-1.master-service:17010 Master-3 : master-2.master-service:17010 Auto allocate : Enabled MetaNode count (active/total) : 3/3 MetaNode used : 0 GB MetaNode available : 18 GB MetaNode total : 19 GB DataNode count (active/total) : 3/3 DataNode used : 1 GB DataNode available : 133 GB DataNode total : 134 GB Volume count : 0 Allow Mp Decomm : Enabled EbsAddr : LoadFactor : 0 DpRepairTimeout : 2h0m0s DataPartitionTimeout : 20m0s volDeletionDelayTime : 48 h EnableAutoDecommission : false AutoDecommissionDiskInterval : 10s EnableAutoDpMetaRepair : false AutoDpMetaRepairParallelCnt : 100 MarkDiskBrokenThreshold : 0% DecommissionDpLimit : 10 DecommissionDiskLimit : 1 DpBackupTimeout : 168h0m0s ForbidWriteOpOfProtoVersion0 : false LegacyDataMediaType : 0 BatchCount : 0 MarkDeleteRate : 0 DeleteWorkerSleepMs: 0 AutoRepairRate : 0 MaxDpCntLimit : 3000 MaxMpCntLimit : 300
2.7配置tab补全功能 生成补全脚本:首先,检查 cfs-cli 是否支持 completion 子命令。在终端输入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 [root@k8s-master01 ~]# cfs-cli completion File "cfs-cli.sh" has been generated successfully under the present working directory, following command to execute: $ $ echo 'source /usr/share/bash-completion/bash_completion' >> ~/.bashrc $ echo 'source {path of cfs-cli.sh}' >>~/.bashrc $ source ~/.bashrc # 根据提示配置 1.安装 bash-completion (如果没有安装): CentOS/yum: yum install bash-completion Ubuntu/apt: apt-get install bash-completion 2. 生成补全脚本,执行后,你的当前文件夹下会多出一个名为 cfs-cli.sh 的文件 [root@k8s-master01 ~]# cfs-cli completion [root@k8s-master01 ~]# ll cfs-cli.sh -rw-r--r-- 1 root root 76379 Dec 17 22:33 cfs-cli.sh 3. 配置环境变量 # 加载系统级补全库: echo 'source /usr/share/bash-completion/bash_completion' >> ~/.bashrc # 加载 cfs-cli 专属脚本: 注意:请将下面的/root/cfs-cli.sh 替换为你 cfs-cli.sh 文件的实际绝对路径。 echo 'source /root/cfs-cli.sh' >> ~/.bashrc # 让配置立即生效: source ~/.bashrc 4. 测试补全功能 # 输入: cfs-cli vol 然后按下 Tab 键。如果它自动补全为 volume,说明配置成功了!
2.8客户端基本使用 2.8.1集群管理 获取集群信息,包括集群名称、地址、卷数量、节点数量和使用率等:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 [root@k8s-master01 ~]# cfs-cli cluster info [Cluster] Cluster name : my-cluster Master leader : master-0.master-service:17010 Master-1 : master-0.master-service:17010 Master-2 : master-1.master-service:17010 Master-3 : master-2.master-service:17010 Auto allocate : Enabled MetaNode count (active/total) : 3/3 MetaNode used : 0 GB MetaNode available : 18 GB MetaNode total : 19 GB DataNode count (active/total) : 3/3 DataNode used : 1 GB DataNode available : 133 GB DataNode total : 134 GB Volume count : 0 Allow Mp Decomm : Enabled EbsAddr : LoadFactor : 0 DpRepairTimeout : 2h0m0s DataPartitionTimeout : 20m0s volDeletionDelayTime : 48 h EnableAutoDecommission : false AutoDecommissionDiskInterval : 10s EnableAutoDpMetaRepair : false AutoDpMetaRepairParallelCnt : 100 MarkDiskBrokenThreshold : 0% DecommissionDpLimit : 10 DecommissionDiskLimit : 1 DpBackupTimeout : 168h0m0s ForbidWriteOpOfProtoVersion0 : false LegacyDataMediaType : 0 BatchCount : 0 MarkDeleteRate : 0 DeleteWorkerSleepMs: 0 AutoRepairRate : 0 MaxDpCntLimit : 3000 MaxMpCntLimit : 300
获取集群状态,按区域获取元数据和数据节点使用率、状态等:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [root@k8s-master01 ~]# cfs-cli cluster stat [Cluster Status] DataNode Status: TOTAL/GB USED/GB INCREASED/GB USED RATIO 134 1 0 0.008 MetaNode Status: TOTAL/GB USED/GB INCREASED/GB USED RATIO 19 0 0 0.014 Zone List: ZONE NAME ROLE TOTAL/GB USED/GB AVAILABLE/GB USED RATIO TOTAL NODES WRITEBLE NODES default DATANODE 134.81 1.14 133.67 0.01 3 3 METANODE 19.2 0.28 18.92 0.01 3 3 # Metanode的Total为最大可用内存,由所有metanode的MaxMemAvailWeight之和计算 得来。
设置卷删除延迟的时间,表示卷被删除多久才会被彻底删除,默认48h,在此之前可以恢复:
1 2 3 # 修改为72小时 [root@k8s-master01 ~]# cfs-cli cluster volDeletionDelayTime 72 master volDeletionDelayTime is set to 72 h!
2.8.2 元数据节点管理 列出所有的元数据节点,包括ID、地址、读写状态及存活状态等:
1 2 3 4 5 6 [root@k8s-master01 ~]# cfs-cli metanode list [Meta nodes] ID ADDRESS WRITABLE ACTIVE MEDIA ForbidWriteOpOfProtoVer0 2 192.168.0.81:17210(k8s-master01:17210) Yes Active N/A notForbid 6 192.168.0.82:17210(k8s-master02:17210) Yes Active N/A notForbid 7 192.168.0.83:17210(k8s-master03:17210) Yes Active N/A notForbid
查看某个节点的详细信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [root@k8s-master01 ~]# cfs-cli metanode info 192.168.0.81:17210 [Meta node info] ID : 2 Address : 192.168.0.81:17210(k8s-master01:17210) Threshold : 0.75 MaxMemAvailWeight : 6.31 GB Allocated : 89.37 MB Total : 6.40 GB Zone : default Status : Active Rdonly : false Report time : 2025-12-17 15:16:41 Partition count : 0 Persist partitions : [] Can alloc partition : true Max partition count : 300 CpuUtil : 11.2%
2.8.3数据节点管理 列举所有的数据节点,包括ID、地址、读写状态和存活状态:
1 2 3 4 5 6 [root@k8s-master01 ~]# cfs-cli datanode list [Data nodes] ID ADDRESS WRITABLE ACTIVE MEDIA ForbidWriteOpOfProtoVer0 3 192.168.0.83:17310(k8s-master03:17310) Yes Active SSD notForbid 4 192.168.0.81:17310(k8s-master01:17310) Yes Active SSD notForbid 5 192.168.0.82:17310(k8s-master02:17310) Yes Active SSD notForbid
展示某个节点的详细信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [root@k8s-master01 ~]# cfs-cli datanode info 192.168.0.81:17310 [Data node info] ID : 4 Address : 192.168.0.81:17310(k8s-master01:17310) Allocated ratio : 0.008449167137517385 Allocated : 388.80 MB Available : 44.56 GB Total : 44.94 GB Zone : default Rdonly : false Status : Active MediaType : SSD ToBeOffline : False Report time : 2025-12-17 15:18:59 Partition count : 0 Bad disks : [] Decommissioned disks: [] Persist partitions : [] Backup partitions : [] Can alloc partition : true Max partition count : 3000 CpuUtil : 6.3% IoUtils : /dev/nvme0n2:0.0%
下线数据节点,下线后该节点的数据将自动迁移至其他节点,实际生产环境不要随意下线节点:
1 2 3 4 5 6 7 8 9 10 11 # 这里下线k8s-master03节点 [root@k8s-master01 ~]# cfs-cli datanode decommission 192.168.0.83:17310 Decommission data node successfully # 再次查看数据节点信息,此时就已经剩余两个讲点了 [root@k8s-master01 ~]# cfs-cli datanode list [Data nodes] ID ADDRESS WRITABLE ACTIVE MEDIA ForbidWriteOpOfProtoVer0 4 192.168.0.81:17310(k8s-master01:17310) Yes Active SSD notForbid 5 192.168.0.82:17310(k8s-master02:17310) Yes Active SSD notForbid
下线后,节点信息无法在查看:
1 2 [root@k8s-master01 ~]# cfs-cli datanode info 192.168.0.83:17310 Error: data node not exists
数据空间也会降低:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 # 这是没有下线前的空间,134G [root@k8s-master01 ~]# cfs-cli cluster stat [Cluster Status] DataNode Status: TOTAL/GB USED/GB INCREASED/GB USED RATIO 134 1 0 0.008 MetaNode Status: TOTAL/GB USED/GB INCREASED/GB USED RATIO 19 0 0 0.014 Zone List: ZONE NAME ROLE TOTAL/GB USED/GB AVAILABLE/GB USED RATIO TOTAL NODES WRITEBLE NODES default DATANODE 134.81 1.14 133.67 0.01 3 3 METANODE 19.2 0.28 18.92 0.01 3 3 # 下线后剩余89G [root@k8s-master01 ~]# cfs-cli cluster stat [Cluster Status] DataNode Status: TOTAL/GB USED/GB INCREASED/GB USED RATIO 89 0 -1 0.008 MetaNode Status: TOTAL/GB USED/GB INCREASED/GB USED RATIO 19 0 0 0.014 Zone List: ZONE NAME ROLE TOTAL/GB USED/GB AVAILABLE/GB USED RATIO TOTAL NODES WRITEBLE NODES default DATANODE 89.88 0.76 89.12 0.01 2 2 METANODE 19.2 0.28 18.92 0.01 3 3
让节点重新加入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 # 删除pod重建,节点自动加入,把在下线节点的pod重建一下即可(datanode-xxx) [root@k8s-master01 ~]# kubectl get po -n cubefs -owide|grep 192.168.0.83 datanode-jtp52 1/1 Running 0 17h 192.168.0.83 k8s-master03 <none> <none> master-2 1/1 Running 0 17h 192.168.0.83 k8s-master03 <none> <none> metanode-vg79k 1/1 Running 0 17h 192.168.0.83 k8s-master03 <none> <none> # 删除pod [root@k8s-master01 ~]# kubectl delete po -n cubefs datanode-jtp52 pod "datanode-jtp52" deleted # datanode-xtmv4是新的pod [root@k8s-master01 ~]# kubectl get po -n cubefs -owide|grep 192.168.0.83 datanode-xtmv4 1/1 Running 0 31s 192.168.0.83 k8s-master03 <none> <none> master-2 1/1 Running 0 17h 192.168.0.83 k8s-master03 <none> <none> metanode-vg79k 1/1 Running 0 17h 192.168.0.83 k8s-master03 <none> <none> # 再次查看datanode的空间,空间容量已经扩容到134G [root@k8s-master01 ~]# cfs-cli cluster stat [Cluster Status] DataNode Status: TOTAL/GB USED/GB INCREASED/GB USED RATIO 134 1 1 0.008 MetaNode Status: TOTAL/GB USED/GB INCREASED/GB USED RATIO 19 0 0 0.014 Zone List: ZONE NAME ROLE TOTAL/GB USED/GB AVAILABLE/GB USED RATIO TOTAL NODES WRITEBLE NODES default DATANODE 134.81 1.14 133.67 0.01 3 3 METANODE 19.2 0.28 18.92 0.01 3 3
2.8.4数据卷管理 列出所有的卷:
1 2 [root@k8s-master01 ~]# cfs-cli volume list VOLUME OWNER USED TOTAL STATUS CREATE TIME
创建一个新的卷:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 # 命令格式:cfs-cli volume create [VOLUME NAME] [USER ID] [flags] # cfs-cli: 这是 CubeFS 的命令行管理工具,用于与资源管理器(Master)交互。 # volume (或简写为 vol): 指定操作对象为“卷”。 # create: 指定操作动词为“创建”。 # volume-test 这是卷名 (Name) 你要创建的卷的唯一标识符。后续挂载时需要用到这个名字。 # test 所有者 (Owner) 指定该卷的所有者(用户 ID)。这在多租户环境下用于权限管理和配额计算。虽然 Owner 字段可以随意填写,但建议在公司内部建立命名规范(如:使用工号、部门名或项目名)。因为卷一旦创建,某些版本的 CubeFS 不允许轻易修改 Owner 字段。# --capacity 1 指定卷的容量,单位通常默认为 GB。在这里表示创建一个 1GB 大小的卷。 [root@k8s-master01 ~]# cfs-cli volume create volume-test test --capacity 1 Create a new volume: Name : volume-test Owner : test capacity : 1 G deleteLockTime : 0 h crossZone : false DefaultPriority : false description : mpCount : 3 dpCount : 10 replicaNum : dpSize : 120 G followerRead : false readOnlyWhenFull : false zoneName : cacheRuleKey : ebsBlkSize : 8388608 byte cacheCapacity : 0 G cacheAction : 0 cacheThreshold : 10485760 byte cacheTTL : 30 day cacheHighWater : 80 cacheLowWater : 60 cacheLRUInterval : 5 min TransactionMask : TransactionTimeout : 1 min TxConflictRetryNum : 0 TxConflictRetryInterval : 0 ms volStorageClass : 0 allowedStorageClass : enableQuota : false metaFollowerRead : false Confirm (yes/no)[yes]: yes Create volume success. # 再次查看卷的列表 [root@k8s-master01 ~]# cfs-cli volume list VOLUME OWNER USED TOTAL STATUS CREATE TIME volume-test test 0.00 B 1.00 GB Normal Wed, 17 Dec 2025 22:03:54 CST
查看某个卷的详细信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 # 这里以查看volume-test 卷的详细信息为例 [root@k8s-master01 ~]# cfs-cli volume info volume-test Summary: ID : 9 Name : volume-test Owner : test Authenticate : Disabled Capacity : 1 GB Create time : 2025-12-17 22:03:54 DeleteLockTime : 0 Cross zone : Disabled DefaultPriority : false Dentry count : 0 Description : DpCnt : 10 DpReplicaNum : 3 Follower read : Disabled Meta Follower read : Disabled Direct Read : Disabled Inode count : 1 Max metaPartition ID : 3 MpCnt : 3 MpReplicaNum : 3 NeedToLowerReplica : Disabled RwDpCnt : 10 Status : Normal ZoneName : default VolType : 0 DpReadOnlyWhenVolFull : false Transaction Mask : rename Transaction timeout : 1 Tx conflict retry num : 121 Tx conflict retry interval(ms) : 500 Tx limit interval(s) : 0 Forbidden : false DisableAuditLog : false TrashInterval : 0s DpRepairBlockSize : 128KB EnableAutoDpMetaRepair : false Quota : Disabled AccessTimeValidInterval : 24h0m0s MetaLeaderRetryTimeout : 0s EnablePersistAccessTime : false ForbidWriteOpOfProtoVer0 : true VolStorageClass : ReplicaSSD AllowedStorageClass : [ReplicaSSD] CacheDpStorageClass : Unspecified QuotaOfClass(ReplicaSSD) : no limit(0)
禁用卷:
1 2 3 4 5 6 7 8 9 10 11 12 # 在 CubeFS 中,set-forbidden 是一个管理层面的“开关”,用于控制卷是否允许被访问。 # volume (或 vol): 指定操作对象为“卷”。 # set-forbidden: 子命令,意为“设置禁用状态”。 # volume-test: 你要操作的卷名称。 # true : 状态值,表示禁用该卷。# false : 激活(取消禁用)该卷。[root@k8s-master01 ~]# cfs-cli volume set-forbidden volume-test true Volume forbidden property has been set successfully, please wait few minutes for the settings to take effect. # 查看该卷的Forbidden字段就被设置为了true [root@k8s-master01 ~]# cfs-cli volume info volume-test | grep -i Forbidden Forbidden : true
取消禁用:
1 2 3 4 5 6 [root@k8s-master01 ~]# cfs-cli volume set-forbidden volume-test false Volume forbidden property has been set successfully, please wait few minutes for the settings to take effect. # Forbidden字段为false 表示已经取消禁用,可以正常使用该卷 [root@k8s-master01 ~]# cfs-cli volume info volume-test | grep -i Forbidden Forbidden : false
扩容卷或者更新卷配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 # 给名字时volume-test的卷扩容为2G [root@k8s-master01 ~]# cfs-cli volume update volume-test --capacity 2 Volume configuration changes: Name : volume-test Description : ZoneName : default Capacity : 1 GB -> 2 GB ReplicaNum : 3 Allow follower read : Disabled Allow CrossZone : Disabled EbsBlkSize : 8388608 byte CacheCap : 0 GB EnableQuota : Disabled DeleteLockTime : 0 h LeaderRetryTimeout : 0 s Transaction Mask : rename Transaction Timeout : 1 minutes Tx Conflict Retry Num : 121 Tx Conflict Retry Interval : 500 ms Tx Operation limit : 0 CacheAction : 0 CacheRule : 0 CacheThreshold : 10485760 byte CacheTTL : 30 day CacheHighWater : 80 CacheLowWater : 60 CacheLRUInterval : 5 min Vol readonly when full : Disabled TrashInterval : 0 min AccessTimeValidInterval : 86400 s EnablePersistAccessTime : false EnableAutoDpMetaRepair : false volStorageClass : ReplicaSSD ForbidWriteOpOfProtoVer0 : true Confirm (yes/no)[yes]: yes Volume configuration has been update successfully. # 查看扩容后卷的容量,可以看到volume-test容量已变更为2G [root@k8s-master01 ~]# cfs-cli volume list VOLUME OWNER USED TOTAL STATUS CREATE TIME volume-test test 0.00 B 2.00 GB Normal Wed, 17 Dec 2025 22:03:54 CST
添加空间限制:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 cfs-cli volume update volume-test --readonly-when-full true # 这条命令的作用是调整卷的安全策略,设置当卷的存储空间占满时,是否自动将该卷切换为只读状态。这是一种保护机制,用于防止因磁盘空间耗尽导致的文件系统崩溃或数据损坏。 # 核心命令部分cfs-cli: CubeFS 的命令行工具。 # volume (或 vol): 指定操作对象为“卷”。 # update: 指定操作动词为“更新配置”。 # volume-test卷名指定你要修改配置的卷名称。 # --readonly-when-full 配置项名称字面意思是“写满时只读”。它定义了当卷达到配额上限时的行为逻辑。 # true 状态值开启此功能。当卷空间满时,禁止任何写入操作,只能读取。# 为什么需要设置 true ? 在分布式文件系统中,当存储空间完全填满(100%)时,继续强制写入可能会带来以下风险: 元数据损坏:由于没有空间记录新的索引信息,可能导致文件系统结构异常。 应用报错:应用程序在写入失败时,如果没有良好的异常处理,可能会崩溃。 性能剧降:系统会不断尝试寻找剩余空间,导致极高的 CPU 或 IO 消耗。 设置为 true 的效果: 一旦空间满了,系统会像给卷加了“写保护”一样。此时你的业务程序会收到“Read-only file system”的错误提示,这虽然会中断写入,但能确保现有数据的安全,并方便管理员通过删除旧数据或扩容来恢复业务。
删除卷:
1 2 3 4 5 6 7 8 # 删除名字是volume-test的卷 [root@k8s-master01 ~]# cfs-cli volume delete volume-test Delete volume [volume-test] (yes/no)[no]:yes Volume has been deleted successfully. # 卷已经被删除 [root@k8s-master01 ~]# cfs-cli volume list VOLUME OWNER USED TOTAL STATUS CREATE TIME
2.8.5用户管理 CubeFS 支持多租户(多用户) 管理,主要通过 cfs-cli user 子命令实现对用户的创建、删除、查询、更新和卷权限控制。用户(也称租户)用于实现数据隔离和权限分配,常用于对象存储(S3 兼容)和卷访问控制。
用户类型分为:
normal :普通用户,仅能访问授权的卷。
admin :管理员用户,具有更高权限。
创建用户:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # cfs-cli user create [USER_ID] [flags] # 创建新用户(USER_ID 为用户名,必须唯一)。 # 常用参数(flags): --access-key string:指定用户的 Access Key(用于 S3 对象存储访问)。 --secret-key string:指定用户的 Secret Key(用于 S3 对象存储访问)。 --password string:指定用户密码(用于控制台或某些认证场景)。 --user-type string:用户类型,可选 normal(默认)或 admin。 -y, --yes:跳过确认提示,直接执行。 # 创建用户示例:创建名为 luijunwei 的普通用户 [root@k8s-master01 ~]# cfs-cli user create liujunwei Create a new CubeFS cluster user User ID : liujunwei Password : [default] Access Key: [auto generate] Secret Key: [auto generate] Type : normal Confirm (yes/no)[yes]: yes Create user success: [Summary] User ID : liujunwei #在使用 FUSE 客户端 (cfs-client) 挂载文件系统时,配置文件中的 owner 字段必须填这个名字。 Access Key : O6EGlPImiTywIQyr #访问密钥 ID (简称 AK)。类似于“账号”。 Secret Key : tNpHrVPNNSouvhPYjWJpsZ1TUP0me8Ho #私有密钥 (简称 SK)。类似于“密码”。 Type : normal #这里显示为 normal,表示这是一个普通用户 Create Time: 2025-12-26 15:15:25 [Volumes] #已授权的卷列表。 VOLUME PERMISSION
列出所有用户:
1 2 3 4 5 6 7 cfs-cli user list # 描述:获取集群中所有用户列表及基本信息,显示所有用户 ID 及简要信息,便于查看多用户状态。 [root@k8s-master01 ~]# cfs-cli user list ID TYPE ACCESS KEY SECRET KEY CREATE TIME liujunwei Normal O6EGlPImiTywIQyr tNpHrVPNNSouvhPYjWJpsZ1TUP0me8Ho 2025-12-26 15:15:25 root Root uIdBy6oIypnbcrVK 1gtjaQypeBftgQ8SYyH4UyHcaJy5wVds 2025-12-16 22:51:24 test Normal NKUk6UpDQXcCxHZo LvWLhYsC0lhE30kYGo7ic6jnYQRqmv95 2025-12-17 22:03:55
删除用户:
1 2 3 4 5 6 7 8 cfs-cli user delete [USER_ID] [flags] # 删除指定用户(会移除其所有关联权限,谨慎操作)。 # -y, --yes :跳过确认提示,直接执行。 # 示例:直接删除用户 test ,无确认提示 [root@k8s-master01 ~]# cfs-cli user delete test -y Delete user success.
查询用户信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 cfs-cli user info [USER_ID] # 描述:获取指定用户详细信息(包括 AK/SK、类型等)。 # 参数:[USER_ID]:必填,用户名称。 # 示例: 查询用户 liujunwei 的详细信息,如 AccessKey、SecretKey、用户类型等。 [root@k8s-master01 ~]# cfs-cli user info liujunwei [Summary] User ID : liujunwei Access Key : O6EGlPImiTywIQyr Secret Key : tNpHrVPNNSouvhPYjWJpsZ1TUP0me8Ho Type : normal Create Time: 2025-12-26 15:15:25 [Volumes] VOLUME PERMISSION
更新用户信息:
1 2 3 4 5 6 7 8 9 10 11 cfs-cli user update [USER_ID] [flags] # 描述:更新指定用户的属性(常用于重置 AK/SK 或更改类型)。 # 常用参数(flags): --access-key string:更新后的 Access Key。 --secret-key string:更新后的 Secret Key。 --user-type string:更新后的用户类型(normal 或 admin)。 -y, --yes:跳过确认提示。 # 示例:将 myuser 的 AK/SK 更新,并升级为 admin 类型。 cfs-cli user update myuser --access-key NEWAK --secret-key NEWSK --user-type admin -y
设置卷权限:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 cfs-cli user perm [USER_ID] [VOLUME] [PERM] # 描述:为用户授权或更新对特定卷(volume)的访问权限(实现多用户隔离)。 # 参数: [USER_ID]:用户名称。 [VOLUME]:卷名称。 [PERM]:权限类型,可选: READONLY 或 RO:只读。 READWRITE 或 RW:读写。 NONE:删除授权(无权限)。 # 示例: # 授权用户 myuser 对卷 myvolume 具有读写权限。 cfs-cli user perm myuser myvolume READWRITE # 移除 myuser 对 myvolume 的所有权限。 cfs-cli user perm myuser myvolume NONE
2.9CubeFS挂载测试 在 Linux 服务器上挂载一个分布式的 CubeFS 存储卷,并验证其读写功能是否正常。
如果你的服务并不是在k8s中部署的,也时想使用cubefs作为存储,就可以按照下面的方式进行挂载使用,挂在后跟操作本地的文件目录是一样的。
第一步创建一个分布式存储卷(服务端操作):
1 2 3 4 5 6 7 8 9 10 11 12 13 [root@k8s-master01 ~]# cfs-cli volume create volume-test ltptest --capacity=20 -y # 含义: 使用 CubeFS 的管理工具 cfs-cli 向 Master 节点申请创建一个新的逻辑卷。 # 参数详解: volume-test:卷的名称。 ltptest:卷的所有者(Owner ID),用于鉴权。 --capacity=20:指定卷的大小为20G -y:自动确认创建,跳过交互式询问。 # 作用: 在分布式存储集群中划分出了一块逻辑空间,准备给客户端使用。 # 查看创建的卷 [root@k8s-master01 ~]# cfs-cli volume list VOLUME OWNER USED TOTAL STATUS CREATE TIME volume-test ltptest 0.00 B 20.00 GB Normal Fri, 26 Dec 2025 16:52:18 CST
第二步创建客户端配置文件 (客户端配置):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@k8s-master01 34-cubefs]# vim volume-test-client.conf # 配置文件内容如下 { "mountPoint": "/volumedata", #挂载点,即把远程卷映射到本地服务器的哪个目录。 "volName": "volume-test", #指定要挂载的卷是volume-test(卷的名称) "owner": "ltptest", #使用哪个身份去连接(必须与创建卷式时指定的的 Owner 一致) "masterAddr": "10.96.167.132:17010", #集群 Master 节点的地址,客户端需要找 Master 获取元数据 "logDir": "/cfs/client/log", #客户端日志输出目录。 "logLevel": "info", #日志等级 "profPort": "27510" } # 注意如果给k8s集群外的宿主机(外部裸机/VM)使用 cfs-client,应该先暴露Master Service,可以通过NodePort或者Ingress暴露 [root@k8s-master01 34-cubefs]# kubectl get svc -n cubefs NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE master-service ClusterIP 10.96.167.132 <none> 17010/TCP 9d objectnode-service ClusterIP 10.96.96.158 <none> 1601/TCP 9d # 暴露名字是master-service的svc,在配置文件中masterAddr填写暴露后的地址 # 如果是给k8s集群内的pod使用,在配置文件中masterAddr填写master-service的ip:端口,或者内部dns,例如: "masterAddr": "10.96.167.132:17010", #集群 Master 节点的地址,客户端需要找 Master 获取元数据 "masterAddr": "master-service.cubefs.svc.cluster.local:17010", #两种写法,这里用service内部dns解析
参数名称
类型
是否必填
含义与实现效果
配置注意事项与常见问题
mountPoint
string
必填
指定卷挂载到本地文件系统的目录路径。 效果:cfs-client 会将 CubeFS 的卷挂载到这个目录下,之后所有对该目录的读写操作都会转发到分布式集群。
- 生产环境建议使用专用路径,如 /mnt/cubefs/<volname> 或 /volumedata,避免与系统目录冲突。 - 权限:启动 cfs-client 的用户需对该目录有读写权限。
volName
string
必填
要挂载的卷(volume)名称。 效果:客户端只连接并挂载指定的卷,所有文件操作都作用在这个卷上。
- 必须与集群中已存在的卷名称完全一致(区分大小写)。 - 可通过 cfs-cli volume list 确认卷是否存在及状态是否 Normal。 - 如果卷不存在或状态异常,客户端启动会报错。
owner
string
必填
卷的拥有者用户(user ID)。 效果:用于权限验证。客户端会以该用户的身份访问卷,只有被授权的用户才能读写。
- 必须是卷创建时指定的 owner,或者通过 cfs-cli user perm 授权给该用户的卷。 - 如果当前用户无权限,挂载成功后读写会返回 Permission Denied。 - 多租户场景下,这是实现数据隔离的关键参数。
masterAddr
string
必填
CubeFS Master 节点的地址列表。 效果:客户端首先连接 Master 获取卷的元数据信息(如 DataNode 列表、MetaNode 列表、分区信息等),然后建立与 DataNode/MetaNode 的直连。
- 格式:单个地址 "host:port" 或多个地址用分号分隔 "host1:port1;host2:port2;host3:port3"。 - 生产环境强烈建议填写多个 Master 地址(通常部署 3 个以上),实现高可用。如果只填一个,Master 单点故障会导致客户端无法启动或重连失败。 - 端口默认 17010,可通过 cfs-cli cluster info 查看实际端口。
logDir
string
必填
客户端日志输出目录。 效果:cfs-client 会将运行日志(包括错误、警告、性能统计等)写入该目录下的日志文件。
- 生产环境建议单独分区或大磁盘路径,避免日志填满系统盘。 - 日志文件会按日期滚动,长期运行会产生较多日志,需配合 logrotate 等工具清理。
logLevel
string
必填
日志级别。 可选值:debug、info、warn、error(默认通常为 info)。 效果:控制日志输出的详细程度。debug 最详细,error 最少。
- 测试/排错时建议设为 debug,能看到详细的请求流程和错误栈。 - 生产环境建议设为 info 或 warn,避免日志量过大影响性能和磁盘。 - 修改后需重启 cfs-client 生效。
profPort
string / int
可选
客户端性能监控(pprof)监听端口。 效果:启动 Go 的 pprof HTTP 服务,可通过 http://localhost:profPort/debug/pprof/ 查看客户端的 CPU、内存、goroutine 等性能指标。
- 默认不填则不开启 pprof 服务。 - 生产环境建议开启(例如 27510),便于故障排查和性能调优。 - 注意防火墙放行该端口,或仅绑定 localhost(当前版本默认绑定所有接口)。 - 安全考虑:生产环境可通过 iptables 或防火墙限制只允许运维机器访问。
第三步安装依赖 (环境准备):
1 2 3 4 # 安装fuse工具 [root@k8s-master01 34-cubefs]# yum install fuse -y # 安装 FUSE (Filesystem in Userspace) 组件。 # 作用: CubeFS 的客户端是基于 FUSE 实现的。它允许用户空间的程序(即 cfs-client)实现文件系统功能,而无需修改操作系统内核。这是挂载成功的必要前置条件。
第四步启动客户端挂载 (建立连接):
cfs-client命令工具是cubefs客户端中的工具,参考2.6章节中下载的客户端工具包
1 2 3 4 5 6 7 8 9 10 # 把命令工具放到/usr/local/bin/中 ,执行名可以不使用全路径 [root@k8s-master01 34-cubefs]# cp cubefs/build/bin/cfs-client /usr/local/bin/ # 执行挂载指令,-c指定配置文件 [root@k8s-master01 34-cubefs]# cfs-client -c volume-test-client.conf 2025/12/26 20:44:41 maxprocs: Leaving GOMAXPROCS=4: CPU quota undefined # 检查挂载点 [root@k8s-master01 cubefs]# df -Th |grep cubefs-volume-test cubefs-volume-test fuse.cubefs 20G 0 20G 0% /volumedata
第五步验证挂载状态 (系统检查):
1 2 3 4 5 6 7 8 9 # 检查挂载点:查看系统磁盘空间,并过滤出刚才挂载的卷。 [root@k8s-master01 cubefs]# df -Th |grep cubefs-volume-test cubefs-volume-test fuse.cubefs 20G 0 20G 0% /volumedata # 结果解读: cubefs-volume-test:设备名。 fuse.cubefs:文件系统类型,说明挂载成功。 20G:卷的容量(默认初始容量通常是 10GB 或根据配置)。 /volume-test:当前的挂载路径。
第六步写入性能测试 (功能验证):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 # 使用 dd 命令模拟写入数据(注意:这里假设你已经 cd 到了挂载目录 /volume-test 下,否则是写到了本地磁盘)。 # 进入到/volumedata目录 [root@k8s-master01 cubefs]# cd /volumedata/ [root@k8s-master01 volumedata]# dd if=/dev/zero of=./test.log bs=1M count=1024 1024+0 records in 1024+0 records out 1073741824 bytes (1.1 GB, 1.0 GiB) copied, 63.6165 s, 16.9 MB/s [root@k8s-master01 volumedata]# dd if=/dev/zero of=./test.log bs=124M count=5 5+0 records in 5+0 records out 650117120 bytes (650 MB, 620 MiB) copied, 23.9789 s, 27.1 MB/s # 参数详解: if=/dev/zero:输入源是零设备(产生无限的空字符)。 of=./test.log:输出文件名为当前目录下的 test.log。 bs (Block Size) 和 count:定义写入块大小和次数。 # 测试结果: 第一次写入:1024MB 数据,速度 16.9 MB/s 第二次写入:620MB 数据,速度 27.1 MB/s 作用: 验证文件系统是否可写,并粗略测试写入带宽性能。 # test.log文件大小是620M [root@k8s-master01 volumedata]# ll -h total 620M -rw-r--r-- 1 root root 620M Dec 26 20:57 test.log
第七步查看卷使用情况 (服务端验证)
1 2 3 4 # USED: 620.00 MB 意思是该卷已使用 620.00 MB。 [root@k8s-master01 volumedata]# cfs-cli volume list VOLUME OWNER USED TOTAL STATUS CREATE TIME volume-test ltptest 620.00 MB 20.00 GB Normal Fri, 26 Dec 2025 16:52:18 CST
2.10CubeFS 扩容 2.10.1基于磁盘扩容 实际场景是cubefs服务器磁盘空间不足了,但是物理机器还有多余的盘符,可以插入新的硬盘,此时将新的磁盘插入物理机上进行扩容,如果是在物理机上操作,新增的磁盘型号和空间大小最好和已经在使用的磁盘保持一致。这里以vmware为例进行演示:
第一步:增加磁盘
增加新的磁盘参考2.2中的步骤
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 # 查看新增的磁盘 [root@k8s-master01 ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS sr0 11:0 1 1.8G 0 rom nvme0n1 259:0 0 100G 0 disk ├─nvme0n1p1 259:1 0 1G 0 part /boot └─nvme0n1p2 259:2 0 99G 0 part └─rl-root 253:0 0 99G 0 lvm /var/lib/kubelet/pods/978020b0-4489-420f-8283-8c3b8404652c/volume-subpaths/cubefs-bin/objectnode-pod/0 /var/lib/kubelet/pods/978020b0-4489-420f-8283-8c3b8404652c/volume-subpaths/cubefs-bin/init-config/0 /var/lib/kubelet/pods/978020b0-4489-420f-8283-8c3b8404652c/volume-subpaths/cubefs-bin/check-master-service/0 /var/lib/kubelet/pods/1a355978-c846-4da9-8e95-dce830ca50af/volume-subpaths/cubefs-bin/metanode-pod/0 /var/lib/kubelet/pods/1a355978-c846-4da9-8e95-dce830ca50af/volume-subpaths/cubefs-bin/init-config/0 /var/lib/kubelet/pods/1596e7b6-8d0f-4160-8efe-4d1693e16c1f/volume-subpaths/cubefs-bin/objectnode-pod/0 /var/lib/kubelet/pods/1596e7b6-8d0f-4160-8efe-4d1693e16c1f/volume-subpaths/cubefs-bin/init-config/0 /var/lib/kubelet/pods/1cf3d98e-3a4a-4dc3-8bc4-c16fd3c24ba1/volume-subpaths/cubefs-bin/datanode-pod/0 /var/lib/kubelet/pods/1cf3d98e-3a4a-4dc3-8bc4-c16fd3c24ba1/volume-subpaths/cubefs-bin/init-config/0 /var/lib/kubelet/pods/a4f1bd2c-a479-42e8-aad1-45a381a819e4/volume-subpaths/cubefs-bin/master-pod/0 /var/lib/kubelet/pods/1596e7b6-8d0f-4160-8efe-4d1693e16c1f/volume-subpaths/cubefs-bin/check-master-service/0 /var/lib/kubelet/pods/1cf3d98e-3a4a-4dc3-8bc4-c16fd3c24ba1/volume-subpaths/cubefs-bin/check-master-service/0 /var/lib/kubelet/pods/a4f1bd2c-a479-42e8-aad1-45a381a819e4/volume-subpaths/cubefs-bin/init-config/0 /var/lib/kubelet/pods/1a355978-c846-4da9-8e95-dce830ca50af/volume-subpaths/cubefs-bin/check-master-service/0 / nvme0n2 259:3 0 50G 0 disk /data0 nvme0n3 259:4 0 50G 0 disk [root@k8s-master01 ~]# fdisk -l Disk /dev/nvme0n1: 100 GiB, 107374182400 bytes, 209715200 sectors Disk model: VMware Virtual NVMe Disk Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disklabel type: dos Disk identifier: 0xf156fc13 Device Boot Start End Sectors Size Id Type /dev/nvme0n1p1 * 2048 2099199 2097152 1G 83 Linux /dev/nvme0n1p2 2099200 209715199 207616000 99G 8e Linux LVM Disk /dev/nvme0n2: 50 GiB, 53687091200 bytes, 104857600 sectors Disk model: VMware Virtual NVMe Disk Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk /dev/nvme0n3: 50 GiB, 53687091200 bytes, 104857600 sectors Disk model: VMware Virtual NVMe Disk Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk /dev/mapper/rl-root: 99 GiB, 106296246272 bytes, 207609856 sectors Units: sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes
结合fdisk -l 和lsblk得出**/dev/nvme0n3**是新增,完全未使用的磁盘:
磁盘
容量
是否有分区表
是否已挂载/使用
状态判断
/dev/nvme0n1
100G
有(p1+p2)
是(系统盘 + /boot)
已完全使用,非新盘
/dev/nvme0n2
50G
无
是(挂载到 /data0)
已用于 CubeFS 数据盘
/dev/nvme0n3
50G
无
否(无挂载点)
全新未使用的新磁盘
/dev/mapper/rl-root
99G
-
是(根文件系统)
LVM 逻辑卷,非物理盘
第二步:格式化新增加的硬盘
1 2 3 4 5 6 7 8 9 10 11 12 # 新增硬盘的节点都需要执行格式化 [root@k8s-master01 ~]# mkfs.xfs /dev/nvme0n3 meta-data=/dev/nvme0n3 isize=512 agcount=4, agsize=3276800 blks = sectsz=512 attr=2, projid32bit=1 = crc=1 finobt=1, sparse=1, rmapbt=0 = reflink=1 bigtime=1 inobtcount=1 nrext64=0 data = bsize=4096 blocks=13107200, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0, ftype=1 log =internal log bsize=4096 blocks=16384, version=2 = sectsz=512 sunit=0 blks, lazy-count=1 realtime =none extsz=4096 blocks=0, rtextents=0
第三步:创建挂载点并进行挂载
1 2 3 4 5 6 # 创建目录(建议按序号命名,方便管理),新增硬盘的所有节点都需要执行 # 如果机器上存在多个需要挂载的数据磁盘,则每个磁盘按以上步骤进行格式化和挂载磁盘,挂载目录按照data0/data1/../data999的顺序命名 mkdir -p /data1 # 挂载 mount /dev/nvme0n3 /data1
第四步:配置开机自动挂载 (持久化)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # 使用磁盘的UUID进行挂载,获取磁盘的UUID,新增硬盘的所有节点都需要执行 [root@k8s-master01 ~]# blkid /dev/nvme0n3 /dev/nvme0n3: UUID="18c753c3-cfbe-446f-b1c7-8a429c4bf43f" TYPE="xfs" # 加入开机自动挂载,写入/etc/fstab # /data1是新增的数据 [root@k8s-master01 ~]# vim /etc/fstab /dev/mapper/rl-root / xfs defaults 0 0 UUID=f1caf59f-95c2-4641-bd78-1f836b7b97fd /boot xfs defaults 0 0 UUID=329fe4ea-fc45-4670-ab81-5c9929a10978 /data0 xfs defaults 0 0 UUID=18c753c3-cfbe-446f-b1c7-8a429c4bf43f /data1 xfs defaults 0 0 # 重新加载系统配置 [root@k8s-master01 ~]# systemctl daemon-reload # 检查配置文件是否有错误,没有信息返回表示没问题 [root@k8s-master03 ~]# mount -a
第五步:CubeFS DataNode 配置更新
1 2 3 4 5 6 7 8 9 10 11 12 # 修改配置文件 [root@k8s-master01 templates]# cd cubefs-helm/cubefs # 编辑values.yaml文件 [root@k8s-master01 cubefs]# vim values.yaml # 找到datanode块,新增125中的配置 123 disks: 124 - /data0:2147483648 125 - /data1:2147483648 #这里同样预留大约2G的空间,避免爆盘 126 media_type: 1 127 resources: 128 enabled: false
更新配置前先查看集群总的空间大小:
1 2 3 4 5 6 # 集群总存储容量是134G [root@k8s-master01 cubefs]# cfs-cli cluster info|grep DataNode DataNode count (active/total) : 3/3 DataNode used : 9 GB DataNode available : 124 GB DataNode total : 134 GB
更新配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 [root@k8s-master01 cubefs]# helm upgrade cubefs -n cubefs . Release "cubefs" has been upgraded. Happy Helming! NAME: cubefs LAST DEPLOYED: Sat Dec 27 14:23:43 2025 NAMESPACE: cubefs STATUS: deployed REVISION: 2 DESCRIPTION: Upgrade complete TEST SUITE: None # 删除datanode的pod触发更新,可以通过标签同时删除pod,也可以挨个删除pod kubectl delete po -n cubefs -l app.kubernetes.io/component=datanode # 查看pod状态,datanode的pod已经成功运行 [root@k8s-master01 cubefs]# kubectl get po -n cubefs NAME READY STATUS RESTARTS AGE datanode-d9xqm 1/1 Running 0 7s datanode-pj2gm 1/1 Running 0 23s datanode-tqbvp 1/1 Running 0 62s master-0 1/1 Running 4 (104m ago) 10d master-1 1/1 Running 4 (99m ago) 10d master-2 1/1 Running 0 87m metanode-6st64 1/1 Running 4 (99m ago) 10d metanode-ksjh6 1/1 Running 4 (104m ago) 10d metanode-vg79k 1/1 Running 4 (88m ago) 10d objectnode-7669844df5-57fm5 1/1 Running 4 (99m ago) 10d objectnode-7669844df5-bbpvl 1/1 Running 4 (104m ago) 10d objectnode-7669844df5-s5dzp 1/1 Running 0 88m
第六步:检查cubefs集群的状态和空间大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # 通过cfs-cli cluster stat 和cfs-cli cluster info命令都可以查看 # 集群总空间已经扩容成269G,原来是134G [root@k8s-master01 cubefs]# cfs-cli cluster info|grep DataNode DataNode count (active/total) : 3/3 DataNode used : 7 GB DataNode available : 261 GB DataNode total : 269 GB [root@k8s-master01 cubefs]# cfs-cli cluster stat [Cluster Status] DataNode Status: TOTAL/GB USED/GB INCREASED/GB USED RATIO 269 7 0 0.029 MetaNode Status: TOTAL/GB USED/GB INCREASED/GB USED RATIO 19 0 0 0.017 Zone List: ZONE NAME ROLE TOTAL/GB USED/GB AVAILABLE/GB USED RATIO TOTAL NODES WRITEBLE NODES default DATANODE 269.63 7.72 261.91 0.03 3 3 METANODE 19.2 0.33 18.87 0.02 3 3
2.10.2基于主机的扩容 实际场景是cubefs服务器磁盘空间不足了,物理服务器上没有可用盘符,此时需要新增加一台机器用来扩容,新增加的这台机器尽量和之前的cubefs集群的机器规格保持一致,硬盘的数量,硬盘的大小和规格都保持一致。
步骤如下:
添加一个新节点,新的节点要加入到k8s集群中。已有节点可以忽略
在新节点上添加和当前配置一样的硬盘并挂载
在新节点上打component.cubefs.io/datanode=enabled标签即可
2.11CubeFS对象存储使用 下面将使用 MinIO Client(mc)工具完演示操作一个 S3 兼容的对象存储系统的全流程。这里虽然用的是 CubeFS 的对象存储(ObjectNode),但因为它完全兼容 S3 协议,所以可以直接用 MinIO 的官方客户端 mc 来管理,和操作 AWS S3、MinIO、Ceph RGW 等完全一致。
第一步:下载并安装 MinIO Client(mc)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 # 下载客户端工具 [root@k8s-master01 34-cubefs]# curl https://dl.minio.org.cn/client/mc/release/linux-amd64/mc --create-dirs -o /usr/local/bin/mc % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 29.1M 100 29.1M 0 0 45.3M 0 --:--:-- --:--:-- --:--:-- 45.4M # --create-dirs 表示自动创建目录,如果指定输出路径是 /usr/local/bin/mc在系统中不存在,会自动创建 # -o /usr/local/bin/mc 指定输出文件,-o (小写)允许自定义名字 # mc已经成功下载 [root@k8s-master01 34-cubefs]# ll /usr/local/bin/mc -rw-r--r-- 1 root root 30531768 Dec 27 18:20 /usr/local/bin/mc # 添加可执行权限 [root@k8s-master01 34-cubefs]# chmod +x /usr/local/bin/mc [root@k8s-master01 34-cubefs]# ll /usr/local/bin/mc -rwxr-xr-x 1 root root 30531768 Dec 27 18:20 /usr/local/bin/mc [root@k8s-master01 34-cubefs]# mc --version mc version RELEASE.2025-07-21T05-28-08Z (commit-id=ee72571936f15b0e65dc8b4a231a4dd445e5ccb6) Runtime: go1.24.5 linux/amd64 Copyright (c) 2015-2025 MinIO, Inc. License GNU AGPLv3 <https://www.gnu.org/licenses/agpl-3.0.html>
第二步:配置对象存储接入点(添加 host alias),别名可以随便起
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 # 我这里新创建一个用户,使用新用户的Access Key和Secret Key,也可以使用已经存在的用户。 [root@k8s-master01 volumedata]# cfs-cli user create object -y Create user success: [Summary] User ID : object Access Key : JE1AuJBjgaiCnYol Secret Key : 9LbwW6BP0HUZJDA5wT0xGVCVVMx9pnml Type : normal Create Time: 2025-12-27 20:15:47 [Volumes] VOLUME PERMISSION # 设置一个别名,方便后续通过简短的别名来管理该对象存储服务 # 直接在 ~/.mc/config.json 中创建一个名为 cubefs 的别名.指定 Endpoint、Access Key、Secret Key。 # 支持额外参数(如 --api S3v4 强制使用签名版本4,--path on 启用路径风格访问),对某些 S3 兼容服务(如 CubeFS)非常有用。 [root@k8s-master01 volumedata]# mc alias set cubefs http://10.96.96.158:1601 JE1AuJBjgaiCnYol 9LbwW6BP0HUZJDA5wT0xGVCVVMx9pnml --api S3v4 --path on Added `cubefs` successfully. # 命令解释 # 添加一个名为 cubefs 的别名。 # Endpoint:http://10.96.96.158:1601(这是你的 CubeFS ObjectNode 服务地址和端口),可以通过kubectl get svc -n cubefs查看,用的是objectnode-service的ip和端口。 # Access Key 和 Secret Key:用于身份认证(相当于用户名和密码),这里用的是object这个用户的AK和SK。 # 设置别名以后所有 mc 命令只需用 cubefs 这个别名就能操作你的 CubeFS 对象存储。 # 检查创建的别名是否存在 [root@k8s-master01 volumedata]# mc alias list|grep cubefs -A 6 cubefs URL : http://10.96.96.158:1601 AccessKey : JE1AuJBjgaiCnYol SecretKey : 9LbwW6BP0HUZJDA5wT0xGVCVVMx9pnml API : S3v4 Path : on Src : /root/.mc/config.json # 知识补充 可以随时用 mc alias list 查看所有别名。 可以用 mc alias get cubefs 查看具体配置,mc alias remove cubefs 进行删除。
第三步:创建桶(bucket)
1 2 3 4 5 6 7 8 9 10 11 12 # 在创建桶之前先查看有哪些桶 ,列出该用户拥有的所有 S3 存储桶(buckets) [root@k8s-master01 volumedata]# mc ls cubefs #没有结果返回说明还没有桶 # 接下来创建一个存储桶 # cubefs是设置的别名,video是创建的桶名,意思是在cubefs中创建了一个video名字的桶 [root@k8s-master01 volumedata]# mc mb cubefs/video Bucket created successfully `cubefs/video`. # 再次查看有哪些桶,此时video桶已经存在 [root@k8s-master01 volumedata]# mc ls cubefs/ [2025-12-27 20:50:19 CST] 0B video/
第四步:上传文件到桶中
1 2 3 4 5 6 7 8 9 10 11 12 13 mc cp ./helm-v4.0.4-linux-amd64.tar.gz cubefs/video # ./helm-v4.0.4-linux-amd64.tar.gz是要拷贝的文件路径 # cubefs是对象存储接入点的别名,是第二步中设置的别名 # video 是桶的名称,也就是把文件拷贝到了video这个桶中 [root@k8s-master01 34-cubefs]# mc cp ./helm-v4.0.4-linux-amd64.tar.gz cubefs/video ...v4.0.4-linux-amd64.tar.gz: 19.04 MiB / 19.04 MiB ┃▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓┃ 23.36 MiB/s 0s[root@k8s-master01 34-cubefs]# mc cp ./cubefs-3.5.0-linux-amd64.tar.gz cubefs/video ...-3.5.0-linux-amd64.tar.gz: 86.07 MiB / 86.07 MiB ┃▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓┃ 50.55 MiB/s 1s # 查看桶中的文件,确认文件已成功上传,并显示大小和存储类型(STANDARD)。 [root@k8s-master01 34-cubefs]# mc ls cubefs/video [2025-12-27 20:56:12 CST] 86MiB STANDARD cubefs-3.5.0-linux-amd64.tar.gz [2025-12-27 20:55:55 CST] 19MiB STANDARD helm-v4.0.4-linux-amd64.tar.gz
补充:
在 CubeFS 中,一个 Bucket 严格对应一个 Volume (官方描述:从对象存储视角,一个 volume 对应一个 bucket)。
当使用 mc 创建 Bucket 时(例如 mc mb cubefs/mybucket):
系统会自动创建一个同名的 Volume 。
该 Volume 的 owner 就是你当前使用的用户(AK/SK 对应的用户 ID)。
对象数据会存储在这个新创建的 Volume 中。
查看卷的列表,已经存在了一个名字是video的卷,如下图第五步:删除桶中文件
1 2 3 4 5 6 7 # 从 Bucket 中删除指定文件。 [root@k8s-master01 34-cubefs]# mc rm cubefs/video/helm-v4.0.4-linux-amd64.tar.gz Removed `cubefs/video/helm-v4.0.4-linux-amd64.tar.gz`. # 桶中只剩cubefs-3.5.0-linux-amd64.tar.gz文件 [root@k8s-master01 34-cubefs]# mc ls cubefs/video [2025-12-27 20:56:12 CST] 86MiB STANDARD cubefs-3.5.0-linux-amd64.tar.gz
2.12CubeFS对象存储生产建议 为每个项目创建用户:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # cfs-cli user create projecta Create a new CubeFS cluster user User ID : projecta Password : [default] Access Key: [auto generate] Secret Key: [auto generate] Type : normal Confirm (yes/no)[yes]: yes Create user success: [Summary] User ID : projecta Access Key : sTnnilRm1YPuohs0 Secret Key : ayIjC5uZYdRmVRFYey8lDy73Whdg716l Type : normal [Volumes] VOLUME PERMISSION
添加项目的host:
1 2 3 4 # mc alias set projecta http://10.96.96.158:1601 JE1AuJBjgaiCnYol 9LbwW6BP0HUZJDA5wT0xGVCVVMx9pnml --api S3v4 --path on # projecta是自定义的别名,可以根据实际情况起 # 生产环境中 http://10.96.96.158:1601地址应该通过域名访问,可以使用ingress对外暴露
为每个微服务创建桶:
1 2 # mc mb projecta/appa Bucket created successfully `projecta/appa`.
2.13为开发同事提供 CubeFS 对象存储使用权限的完整指南 作为 CubeFS 集群管理员,当开发同事需要使用对象存储(S3 接口)时,你不需要给他们 cfs-cli 或集群管理权限 ,只需提供安全的 S3 访问凭证和必要信息 ,让他们通过标准 S3 SDK 或工具自行操作。这样既安全,又符合生产规范。
创建专属用户(强烈推荐一人/一团队一用户)
目的:实现数据隔离、权限控制、审计追踪。
1 2 3 cfs-cli user create dev-teamA -y # 或指定更安全的 AK/SK(避免自动生成太短) cfs-cli user create dev-teamA --access-key DEV_TEAM_A_AK --secret-key DEV_TEAM_A_SK --password strongpass -y
输出会显示:
User ID(用户名,如 dev-teamA)
Access Key(AK)
Secret Key(SK)
(可选)预创建专用 Volume 并授权
如果你希望控制 Bucket 名称或提前规划容量:
1 cfs-cli volume create teamA-data dev-teamA # 创建 Volume,owner 为 dev-teamA
默认情况下,他们用 mc 创建 Bucket 时会自动创建 Volume,无需这一步。
准备一份“接入文档”发给开发同事 内容包括以下信息(复制粘贴即可):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 【CubeFS 对象存储(S3 兼容)接入信息】 1. Endpoint(服务地址): http://objectnode.example.com:17410 (或你的实际域名/IP + 端口) # 生产环境建议用域名 + HTTPS(已配置 Ingress TLS) 2. Access Key(AK): YOUR_ACCESS_KEY_HERE 3. Secret Key(SK): YOUR_SECRET_KEY_HERE (请妥善保存,勿泄露,建议存入密码管理器或 Vault) 4. 推荐操作工具: - 命令行:MinIO Client (mc) 下载:https://dl.min.io/client/mc/release/linux-amd64/mc - Python:boto3 - Go:minio-go 或 aws-sdk-go-v2 - Java:aws-sdk-java-v2 - Node.js:aws-sdk 5. 快速上手示例(mc 命令行): # 配置别名 mc alias set cubefs http://objectnode.example.com:17410 YOUR_AK YOUR_SK --api S3v4 --path on # 创建 Bucket(会自动创建对应 Volume) mc mb cubefs/my-app-data # 上传文件 mc cp app.log cubefs/my-app-data/ # 列出文件 mc ls cubefs/my-app-data/ 6. SDK 示例(Python boto3,最常用): import boto3 s3 = boto3.client('s3', endpoint_url='http://objectnode.example.com:17410', aws_access_key_id='YOUR_AK', aws_secret_access_key='YOUR_SK') # 上传 s3.upload_file('local.txt', 'my-bucket', 'remote.txt') # 下载 s3.download_file('my-bucket', 'remote.txt', 'local2.txt') 7. 注意事项: - Bucket 名称全局唯一(整个集群)。 - 数据默认三副本高可用。 - 如需共享 Bucket,请联系管理员授权。 - 严禁将 AK/SK 硬编码到代码仓库,使用环境变量或密钥管理服务。
你应该提供的信息清单(发给开发同事)
项目
是否必须
说明
Endpoint URL
必须
ObjectNode 的访问地址(域名优先)
Access Key
必须
Secret Key
必须
建议一次性提供后让他们自行修改存储方式
用户 ID(可选)
可选
用于 cfs-cli 查询时参考
mc 配置命令示例
强烈推荐
降低上手门槛
常用 SDK 示例代码
强烈推荐
尤其是 Python/Go/Java
Bucket 命名规范(建议)
推荐
如 prefix-teamname-appname
联系人(你自己)
推荐
如需扩容、授权、排查问题
你不应该提供的东西:
cfs-cli 工具和 Master 地址(避免他们误操作集群)。
admin 类型用户的 AK/SK。
其他用户的凭证。
额外生产建议:
如果团队较多,建议用一个用户对应一个应用/服务(如 service-backup、service-log),而非个人。
AK/SK 定期轮换(3-6 个月)。
关键应用将 AK/SK 注入到 Kubernetes Secret 或 HashiCorp Vault 中。
监控 Bucket 使用量(可用 cfs-cli volume list 或 Grafana 面板)。
这样操作既安全又高效,开发同事拿到信息后即可自助使用对象存储上传日志、备份、静态资源、模型文件等,完全不需要你的进一步干预。
2.14CubeFS对接k8s 2.14.1安装CSI 在部署cubefs集群时并没有安装CSI相关服务,这里对CSI服务进行安装。
CubeFS Helm chart 在部署 CSI 组件时,使用 nodeSelector 机制来决定 CSI Node 插件(DaemonSet)应该调度到哪些节点上,在values.yaml文件中定义的标签是"component.cubefs.io/csi": "enabled"。
CSI Node 插件必须运行在所有可能挂载 CubeFS 卷的节点 上,因为它负责节点本地操作(如 mount/unmount CubeFS 卷到 Pod),所以需要为所有可能用到cubefs存储的节点都打上"component.cubefs.io/csi": "enabled"标签 。
第一步:为所有节点打标签
1 2 3 4 5 6 7 8 9 10 11 12 # 为所有节点打上CSI的标签(需要使用存储的节点都需要打标签) [root@k8s-master01 34-cubefs]# kubectl label nodes component.cubefs.io/csi=enabled error: resource(s) were provided, but no name was specified #这里的报错是因为没有指定节点名称,可以忽略,会把所有节点都打上标签 [root@k8s-master01 34-cubefs]# kubectl label nodes component.cubefs.io/csi=enabled --all node/k8s-master01 labeled node/k8s-master02 labeled node/k8s-master03 labeled node/k8s-node01 labeled node/k8s-node02 labeled node/k8s-node03 labeled
第二步:更改 values 配置,启用 CSI 组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 vim values.yaml # 开启CSI插件,并修改相关配置 component: master: true datanode: true metanode: true objectnode: true client: false csi: true #将这里原来的false改为true,必须设为true monitor: false ## 监控通常也建议独立部署或复用现有,所以就不开启了 csi: driverName: csi.cubefs.com logLevel: error # If you changed the default kubelet home path, this # value needs to be modified accordingly 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: "1024Mi" cpu: "2000m" limits: memory: "2048Mi" cpu: "2000m" storageClass: # Whether automatically set this StorageClass to default volume provisioner setToDefault: true #true表示自动把这个StorageClass设置成集群的【默认 StorageClass】 # StorageClass reclaim policy, 'Delete' or 'Retain' is supported reclaimPolicy: "Delete" #定义 StorageClass 创建的 PV 的回收策略,动态存储建议用Delete # Override the master address parameter to connect to external cluster masterAddr: "" #如果cubefs和k8s在同一个集群时这里一定要留空;。 # 如果cubefs和k8s不在同一个集群, masterAddr: 地址要填写master-service这个svc对外暴露的地址 [root@k8s-master01 34-cubefs]# kubectl get svc -n cubefs NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE master-service ClusterIP 10.96.167.132 <none> 17010/TCP 11d objectnode-service ClusterIP 10.96.96.158 <none> 1601/TCP 11d # 通过 NodePort 方式对外暴露了 master(最常见、最简单),那 masterAddr 就应该这样填(填所有 master 节点真实 IP + NodePort),30010是NodePort的端口号。格式如下: storageClass: masterAddr: "192.168.0.81:30010,192.168.0.82:30010,192.168.0.83:30010"
第三步:执行 Helm upgrade 安装/升级
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@k8s-master01 cubefs]# helm upgrade cubefs -n cubefs . Release "cubefs" has been upgraded. Happy Helming! NAME: cubefs LAST DEPLOYED: Sun Dec 28 12:06:09 2025 NAMESPACE: cubefs STATUS: deployed REVISION: 3 DESCRIPTION: Upgrade complete TEST SUITE: None # 检查pod是否启动成功 [root@k8s-master01 cubefs]# kubectl get po -n cubefs |grep csi cfs-csi-controller-77f6467b68-fgnsb 4/4 Running 0 100s cfs-csi-node-7sw69 2/2 Running 0 100s cfs-csi-node-p87k6 2/2 Running 0 100s cfs-csi-node-qs75w 2/2 Running 0 100s cfs-csi-node-rhmgj 2/2 Running 0 100s cfs-csi-node-w7j9n 2/2 Running 0 100s cfs-csi-node-zjc4h 2/2 Running 0 100s
第四步:安装后查询创建的 StorageClass
StorageClass 是 K8s 使用 CSI 的入口:创建 PVC 时指定 storageClassName 为 cfs-sc,K8s 就会调用 CubeFS CSI 自动创建卷。它是对接成功的标志之一,没有它,PVC 无法动态 provision CubeFS 卷。
被标记为 default 意味着即使 PVC 不指定 storageClassName,也会默认用 CubeFS(方便测试)。
1 2 3 4 5 # cfs-sc就是新创建的sc [root@k8s-master01 cubefs]# kubectl get storageclasses.storage.k8s.io NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE cfs-sc (default) csi.cubefs.com Delete Immediate true 3m47s nfs-csi nfs.csi.k8s.io Delete Immediate true 14d
2.14.2pod中使用存储 成功部署了 CubeFS CSI 插件和默认 StorageClass cfs-sc,现在就可以在 Kubernetes 的 Pod 中非常方便地使用 CubeFS 提供的分布式存储了。下面演示完整的流程步骤。
在 Kubernetes 中使用任何 CSI 存储(包括 CubeFS)的标准流程是:
创建一个 PersistentVolumeClaim (PVC) → 向存储集群申请存储空间
在 Pod 中通过 volume 引用这个 PVC → Pod 挂载存储卷
Pod 启动后,CSI 自动完成卷的创建、格式化、挂载等工作
第一步:创建pvc资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 # 定义一个pvc资源 [root@k8s-master01 34-cubefs]# vim pvc-cubefs.yaml # pvc资源配置如下 kind: PersistentVolumeClaim apiVersion: v1 metadata: name: pvc-cubefs # namespace: default spec: accessModes: - ReadWriteMany # CubeFS 支持多节点同时读写(关键优势) #- ReadWriteOnce # 如果只需要单节点挂载,也可以用这个 volumeMode: Filesystem #如果不显式指定,它会默认使用 Filesystem。 resources: requests: storage: 10Gi # 申请 10Gi 空间(可以根据需要调整) storageClassName: cfs-sc # 必须指定使用 CubeFS 的 StorageClass
创建上述pvc:
1 2 3 # 应用资源 [root@k8s-master01 34-cubefs]# kubectl create -f pvc-cubefs.yaml persistentvolumeclaim/pvc-cubefs created
查看pvc:
pvc每个字段的意思:
1 2 3 4 5 6 7 8 NAME:PVC 的名字,Pod 里通过这个名字引用它 STATUS:状态,Bound表示已经绑定了pv VOLUME:卷,真正绑定的 PV 的名字(CubeFS CSI 自动生成的) CAPACITY:这个 PVC 当前实际分配到的容量(申请多少就是多少) ACCESS MODES:超级重要!决定这个卷能被几个 Pod 同时挂载、读写方式。CubeFS 目前支持的三种: RWO(ReadWriteOnce):只能被 1 个节点挂载读写;ROX(ReadOnlyMany):任意多个节点只读挂载;RWX(ReadWriteMany):任意多个节点同时读写 。 STORAGECLASS:告诉你是用哪个 StorageClass 创建的 VOLUMEATTRIBUTESCLASS:Kubernetes 1.29+ 的新特性,暂时不用管,CubeFS 还没用它 AGE:这个 PVC 已经存在多久了
第二步:创建deployment资源使用该pvc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 [root@k8s-master01 34 -cubefs ] kind: Deployment apiVersion: apps/v1 metadata: name: nginx-deployment labels: app: nginx spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/nginx ports: - containerPort: 80 volumeMounts: - name: nginx-vol mountPath: /app volumes: - name: nginx-vol persistentVolumeClaim: claimName: pvc-cubefs
创建上述资源:
1 2 3 4 5 6 7 [root@k8s-master01 34-cubefs]# kubectl create -f nginx-deploy.yaml deployment.apps/nginx-deployment created # 查看pod 状态 [root@k8s-master01 34-cubefs]# kubectl get po |grep nginx-deployment nginx-deployment-6d45989866-5jr8f 1/1 Running 0 2m59s nginx-deployment-6d45989866-8nnpw 1/1 Running 0 2m59s
第三步:验证挂载是否成功
1 2 3 4 5 6 7 8 9 10 11 12 13 14 # 进入任意pod,写入任意数据,在另一个pod中查看是否存在相同的数据 [root@k8s-master01 34-cubefs]# kubectl exec -ti nginx-deployment-6d45989866-8nnpw -- bash root@nginx-deployment-6d45989866-8nnpw:/# cat /app/mount/cfs.txt cubefs root@nginx-deployment-6d45989866-8nnpw:/# echo hello >> /app/mount/cfs.txt root@nginx-deployment-6d45989866-8nnpw:/# cat /app/mount/cfs.txt cubefs hello # 进入另一个pod查看数据 [root@k8s-master01 34-cubefs]# kubectl exec -ti nginx-deployment-6d45989866-5jr8f -- bash root@nginx-deployment-6d45989866-5jr8f:/# cat /app/mount/cfs.txt cubefs hello
2.14.3在线扩容 动态存储大部分都支持在线扩容,可以直接编辑PVC即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 # 这里以名字是data-mysql-0的pvc演示,扩容前容量data-mysql-0绑定的卷(pvc-c67787cf-3430-4551-8dbc-729c3c23b93d)是20G [root@k8s-master01 ~]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE data-mysql-0 Bound pvc-c67787cf-3430-4551-8dbc-729c3c23b93d 20Gi RWO cfs-sc <unset> 19h nfs-pvc Bound nfs-pv 10Gi RWX nfs-sc <unset> 16d pvc-cubefs Bound pvc-375f416e-ccbf-43b8-8f90-e7e31a66a50a 10Gi RWX cfs-sc <unset> 26h [root@k8s-master01 ~]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE nfs-pv 10Gi RWX Retain Bound default/nfs-pvc nfs-sc <unset> 16d pv-hostpath 1Gi RWO Retain Available hostpath-sc <unset> 17d pvc-375f416e-ccbf-43b8-8f90-e7e31a66a50a 10Gi RWX Delete Bound default/pvc-cubefs cfs-sc <unset> 26h pvc-c67787cf-3430-4551-8dbc-729c3c23b93d 20Gi RWO Delete Bound default/data-mysql-0 cfs-sc <unset> 19h # 扩容直接修改pvc配置中定义的大小,这里以名字是data-mysql-0的pvc演示 [root@k8s-master01 ~]# kubectl edit pvc data-mysql-0 22 spec: 23 accessModes: 24 - ReadWriteOnce 25 resources: 26 requests: 27 storage: 50Gi #修改为50G 28 storageClassName: cfs-sc 29 volumeMode: Filesystem # 再次查看data-mysql-0(pvc)绑定的pvc-c67787cf-3430-4551-8dbc-729c3c23b93d(pv)已经扩容为50G [root@k8s-master01 ~]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE data-mysql-0 Bound pvc-c67787cf-3430-4551-8dbc-729c3c23b93d 50Gi RWO cfs-sc <unset> 19h nfs-pvc Bound nfs-pv 10Gi RWX nfs-sc <unset> 16d pvc-cubefs Bound pvc-375f416e-ccbf-43b8-8f90-e7e31a66a50a 10Gi RWX cfs-sc <unset> 26h [root@k8s-master01 ~]# kubectl get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE nfs-pv 10Gi RWX Retain Bound default/nfs-pvc nfs-sc <unset> 16d pv-hostpath 1Gi RWO Retain Available hostpath-sc <unset> 17d pvc-375f416e-ccbf-43b8-8f90-e7e31a66a50a 10Gi RWX Delete Bound default/pvc-cubefs cfs-sc <unset> 26h pvc-c67787cf-3430-4551-8dbc-729c3c23b93d 50Gi RWO Delete Bound default/data-mysql-0 cfs-sc <unset> 19h # 进入容器中验证,瓜子啊目录/var/lib/mysql已经扩容为50G [root@k8s-master01 ~]# kubectl exec -ti mysql-0 -- bash bash-5.1# df -h Filesystem Size Used Avail Use% Mounted on overlay 99G 15G 85G 15% / tmpfs 64M 0 64M 0% /dev /dev/mapper/rl-root 99G 15G 85G 15% /etc/hosts shm 64M 0 64M 0% /dev/shm cubefs-pvc-c67787cf-3430-4551-8dbc-729c3c23b93d 50G 201M 50G 1% /var/lib/mysql tmpfs 4.0G 12K 4.0G 1% /run/secrets/kubernetes.io/serviceaccount tmpfs 3.8G 0 3.8G 0% /proc/acpi tmpfs 3.8G 0 3.8G 0% /proc/scsi tmpfs 3.8G 0 3.8G 0% /sys/firmware
扩容注意事项:
只能增大,不能缩小 (Kubernetes 和 CubeFS 都只支持扩容)。
MySQL 支持在线扩容 :数据目录扩容后 MySQL 无感知,继续正常运行。
如果扩容后 df -h 仍显示旧大小,99% 是没重启 Pod 或节点 kubelet 版本太旧——重启 Pod 即可解决。
CubeFS 底层卷扩容是即时完成的,不影响性能。
2.15数据持久化实战案例 2.15.1MySQL数据持久化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 [root@k8s-master01 34 -cubefs ] kind: Service apiVersion: v1 metadata: name: mysql labels: app: mysql spec: clusterIP: None ports: - port: 3306 targetPort: 3306 selector: app: mysql --- kind: Service apiVersion: v1 metadata: name: mysql-nodeport labels: app: mysql spec: type: NodePort ports: - port: 3306 targetPort: 3306 selector: app: mysql --- kind: StatefulSet apiVersion: apps/v1 metadata: name: mysql spec: selector: matchLabels: app: mysql serviceName: mysql replicas: 1 template: metadata: labels: app: mysql spec: containers: - name: mysql image: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/mysql:8.0.37 env: - name: MYSQL_ROOT_PASSWORD value: "root" - name: TZ value: "Asia/Shanghai" ports: - containerPort: 3306 name: mysql volumeMounts: - name: data mountPath: /var/lib/mysql startupProbe: tcpSocket: port: 3306 initialDelaySeconds: 300 periodSeconds: 50 failureThreshold: 3 successThreshold: 1 timeoutSeconds: 5 livenessProbe: tcpSocket: port: 3306 initialDelaySeconds: 30 periodSeconds: 10 failureThreshold: 3 successThreshold: 1 timeoutSeconds: 5 readinessProbe: tcpSocket: port: 3306 initialDelaySeconds: 5 periodSeconds: 10 failureThreshold: 3 successThreshold: 1 timeoutSeconds: 5 resources: requests: cpu: "1" memory: "2Gi" limits: cpu: "2" memory: "4Gi" volumeClaimTemplates: - metadata: name: data spec: accessModes: - ReadWriteOnce storageClassName: cfs-sc resources: requests: storage: 20Gi
部署服务 :
1 2 3 4 [root@k8s-master01 34-cubefs]# kubectl create -f mysql.yaml service/mysql created service/mysql-nodeport created statefulset.apps/mysql created
检查资源并验证数据是否真的持久化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 # pod正常 [root@k8s-master01 34-cubefs]# kubectl get po -l app=mysql NAME READY STATUS RESTARTS AGE mysql-0 1/1 Running 0 18m # pvc(data-mysql-0)正常 [root@k8s-master01 34-cubefs]# kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE data-mysql-0 Bound pvc-c67787cf-3430-4551-8dbc-729c3c23b93d 20Gi RWO cfs-sc <unset> 18m nfs-pvc Bound nfs-pv 10Gi RWX nfs-sc <unset> 16d pvc-cubefs Bound pvc-375f416e-ccbf-43b8-8f90-e7e31a66a50a 10Gi RWX cfs-sc <unset> 7h19m # 在数据中写入数据,然后删除pod,pod会自动重建,进入重建后的pod查看之前写入的数据是否还存在 [root@k8s-master01 34-cubefs]# kubectl exec -ti mysql-0 -- bash bash-5.1# mysql -uroot -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 281 Server version: 8.0.37 MySQL Community Server - GPL Copyright (c) 2000, 2024, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | +--------------------+ 4 rows in set (0.00 sec) mysql> CREATE DATABASE IF NOT EXISTS user CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; Query OK, 1 row affected (0.02 sec) mysql> USE user; Database changed mysql> CREATE TABLE IF NOT EXISTS users ( -> id INT AUTO_INCREMENT PRIMARY KEY, -> username VARCHAR(50) NOT NULL UNIQUE, -> email VARCHAR(100) NOT NULL, -> age INT, -> created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -> ) ENGINE=InnoDB; #在user库中创建users表 Query OK, 0 rows affected (0.24 sec) mysql> INSERT INTO users (username, email, age) VALUES -> ('alice', '[email protected] ', 28), -> ('bob', '[email protected] ', 32), -> ('charlie', '[email protected] ', 25), -> ('diana', '[email protected] ', 30); #向users表中插入数据 Query OK, 4 rows affected (0.02 sec) Records: 4 Duplicates: 0 Warnings: 0 mysql> SELECT * FROM users ; +----+----------+---------------------+------+---------------------+ | id | username | email | age | created_at | +----+----------+---------------------+------+---------------------+ | 1 | alice | [email protected] | 28 | 2025-12-28 22:15:18 | | 2 | bob | [email protected] | 32 | 2025-12-28 22:15:18 | | 3 | charlie | [email protected] | 25 | 2025-12-28 22:15:18 | | 4 | diana | [email protected] | 30 | 2025-12-28 22:15:18 | +----+----------+---------------------+------+---------------------+ 4 rows in set (0.00 sec) # 删除pod,pod会自动创建,创建成功后进入数据库中查看表中的数据是否还存在 [root@k8s-master01 34-cubefs]# kubectl delete pod mysql-0 pod "mysql-0" deleted [root@k8s-master01 34-cubefs]# kubectl get po NAME READY STATUS RESTARTS AGE mysql-0 1/1 Running 0 6m6s # 进入容器中查看mysql数据是否还在 [root@k8s-master01 34-cubefs]# kubectl exec -ti mysql-0 -- bash bash-5.1# mysql -uroot -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 20 Server version: 8.0.37 MySQL Community Server - GPL Copyright (c) 2000, 2024, Oracle and/or its affiliates. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql> use user; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> select * from users ;+----+----------+---------------------+------+---------------------+ | id | username | email | age | created_at | +----+----------+---------------------+------+---------------------+ | 1 | alice | [email protected] | 28 | 2025-12-28 22:15:18 | | 2 | bob | [email protected] | 32 | 2025-12-28 22:15:18 | | 3 | charlie | [email protected] | 25 | 2025-12-28 22:15:18 | | 4 | diana | [email protected] | 30 | 2025-12-28 22:15:18 | +----+----------+---------------------+------+---------------------+ 4 rows in set (0.00 sec)
知识补充:
在传统的MySQL部署方式可以通过my.cnf文件中指定参数来优化MySQL的性能,那用pod运行的MySQL服务要是想优化怎么办呢?
生产环境真正有效的 4 种方式:
优先级
方法
是否推荐
说明 + 真实性能对比
1
自定义镜像 + 最佳实践 my.cnf (王炸!强烈推荐)
★★★★★
性能最高、最稳定、所有公司真正落地的都是这招
2
ConfigMap 挂载 my.cnf
★★★★☆
推荐第二选择,适合多环境复用
3
直接在 env 里写参数(不推荐)
★☆☆☆☆
只能改少量参数,基本没用
4
官方镜像默认参数
☆☆☆☆☆
不推荐使用
方案一:
第一步:自定义镜像 + 生产级 my.cnf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 # 编写优化后的 my.cnf cat > cubefs-mysql.cnf << 'EOF' # 这里的优化配置只是案例,仅供参考,根据实际的情况配置 [mysqld] # 基本优化(必开) user=mysql default_storage_engine=InnoDB innodb_flush_log_at_trx_commit=1 sync_binlog=1 # CubeFS 专属超级加速参数(重点!) innodb_flush_method=O_DIRECT # 绕过系统 cache,直击 CubeFS,性能提升 3~8 倍! transaction_isolation=READ-COMMITTED innodb_log_file_size=1G innodb_log_buffer_size=32M innodb_buffer_pool_size=6G # 关键!建议占容器内存 70%(你给 8Gi 就写 6G) innodb_buffer_pool_instances=8 # CubeFS 必调参数(解决延迟) innodb_io_capacity=2000 innodb_io_capacity_max=4000 innodb_read_io_threads=16 innodb_write_io_threads=16 innodb_thread_concurrency=0 # 连接数与线程 max_connections=2000 thread_cache_size=256 table_open_cache=4096 table_definition_cache=4096 # 临时表与排序 tmp_table_size=64M max_heap_table_size=64M sort_buffer_size=4M join_buffer_size=4M # 二进制日志(如果要主从) server-id=1 log-bin=mysql-bin binlog_format=ROW expire_logs_days=7 # 慢查询 slow_query_log=1 slow_query_log_file=/var/lib/mysql/slow.log long_query_time=1 EOF
第二步:写 Dockerfile(最简版)
1 2 3 4 5 6 7 8 9 FROM registry.cn-beijing.aliyuncs.com/k8s-liujunwei/mysql:8.0 .37 COPY cubefs-mysql.cnf /etc/mysql/conf.d/ USER rootRUN chmod 644 /etc/mysql/conf.d/cubefs-mysql.cnf && chown mysql:mysql /etc/mysql/conf.d/cubefs-mysql.cnf USER mysql
第三步:构建并推送到你的私有仓库
1 2 docker build -t registry.cn-beijing.aliyuncs.com/k8s-liujunwei/mysql:8.0.37-cfs-mysql . docker push registry.cn-beijing.aliyuncs.com/k8s-liujunwei/mysql:8.0.37-cfs-mysql
第四步:StatefulSet 里修改镜像
1 2 3 containers: - name: mysql image: registry.cn-beijing.aliyuncs.com/k8s-liujunwei/mysql:8.0.37-cfs-mysql # 改这行!这里的镜像用自己制作好的镜像
方案二:
不想改镜像?用 ConfigMap 也行(推荐第二选择)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # 把mysql的优化参数通过ConfigMap挂载到容器中 apiVersion: v1 kind: ConfigMap metadata: name: mysql-config data: cubefs-mysql.cnf: | [mysqld] innodb_flush_method=O_DIRECT innodb_buffer_pool_size=6G innodb_io_capacity=2000 innodb_io_capacity_max=4000 # ... 把上面所有参数全复制进来 --- # 在 StatefulSet 里加这句 volumes: - name: config #这里定义的是卷的名字 configMap: name: mysql-config #这里指定的是configmap的名字 volumeMounts: - name: config #这里指定要挂载的卷名,要和上面volumes.name的值一致 mountPath: /etc/mysql/conf.d/ #指定容器内的挂载点
效果和自定义镜像一模一样,就是升级时需要改两份资源,先修改configmap资源,在删除pod触发更新。
步骤
操作
说明
1
修改 ConfigMap(mysql-config)里的内容
这是你真正改参数的地方
2
手动删 Pod(或 rolling-update)让 Pod 重启
因为 ConfigMap 更新后,旧 Pod 不会自动重载配置,必须重建 Pod 才行
所以用 ConfigMap 方式时,你每次想修改 MySQL 参数,都必须同时改 2 个地方(2 个 k8s 资源对象),才会真正生效。