CCE集群使用开源juicefs文件存储
一、背景
客户系统在IDC k8s集群使用JuiceFS作为容器存储引擎,迁移到CCE 集群需要使用对象存储作为底层存储,JuiceFS作为存储引擎驱动。
二、JuiceFS介绍
2.1 架构预览
说明:JuiceFS 文件系统由三个部分组成JuiceFS 客户端、数据存储元数据引擎
- JuiceFS 客户端(Client)
客户端是文件系统最重要的部分,因为所有文件读写,包括碎片合并、回收站文件过期删除等后台任务,均在客户端中发生。服务端(也就是下面将会介绍的「元数据引擎」)不会也不能直接修改任何文件系统数据。可想而知,客户端需要同时与对象存储和元数据存储服务打交道。 - 数据存储(Data Storage)
文件将会切分上传保存在对象存储服务。JuiceFS 支持几乎所有的公有云对象存储,同时也支持 OpenStack Swift、Ceph、MinIO 等私有化的对象存储。
在 JuiceFS 中,只有客户端会直接与存储打交道,服务端(元数据引擎)与对象存储完全解耦,不关心也不会访问对象存储服务,所有实际的文件数据都坐落在你自己选择的对象存储上。
− FUSE 挂载:使用 JuiceFS 客户端将文件系统挂载到本地,在服务器直接访问海量云端存储
− Hadoop Java SDK:直接替代 HDFS,为 Hadoop 提供低成本的海量存储
− Kubernetes CSI 驱动:接入 Kubernetes 集群,为容器提供弹性存储
− S3 网关:对外暴露 S3 API,已有的支持 S3 应用可直接无缝接入 - 元数据引擎(Metadata Engine)
Juicedata 自研的高性能元数据服务,用于存储文件元数据(metadata),包含以下内容:
− 常规文件系统的元数据:文件名、文件大小、权限信息、创建修改时间、目录结构、文件属性、符号链接、文件锁等。
− JuiceFS 独有的元数据:文件的 chunk 及 slice 映射关系、客户端 session 等。
Juicedata 已经在大多数公有云都部署了元数据服务,供云服务用户开箱即用。客户端默认通过公网直接访问同区域的元数据服务,但在对网络延迟有着更高要求的大规模场景下,也可以通过内网打通的方式来进一步优化元数据服务的访问延迟
2.2 架构原理
JuiceFS CSI 驱动包含以下组件:
JuiceFS CSI Controller(StatefulSet)和 JuiceFS CSI Node Service(DaemonSet)
CSI 默认采用容器挂载(Mount Pod)模式,也就是让 JuiceFS 客户端运行在独立的 Pod 中,其架构如下:
采用独立 Mount Pod 来运行 JuiceFS 客户端,并由 CSI Node Service 来管理 Mount Pod 的生命周期。这样的架构提供如下好处:
- 多个 Pod 共用 PV 时,不会新建 Mount Pod,而是对已有的 Mount Pod 做引用计数,计数归零时删除 Mount Pod。
- CSI 驱动组件与客户端解耦,方便 CSI 驱动自身的升级。
在同一个节点上,一个 PVC 会对应一个 Mount Pod。而使用了相同 PV 的容器,则可以共享一个 Mount Pod。PVC、PV、Mount Pod 之间的关系如下图所示:
2.3 使用方式介绍
2.3.1 静态配置
一般在以下场景使用静态配置:
- 你在JuiceFS中已经存储了大量数据,想要直接在 Kubernetes 容器中访问;
- 对 CSI 驱动功能做简单验证;
静态配置方式最为简单直接
- 需要 Kubernetes 管理员创建 PersistentVolume(PV)以及文件系统认证信息(以 Kubernetes Secret 形式保存),
- 用户创建 PersistentVolumeClaim(PVC),在定义中绑定该 PV,
- 最后在 Pod 定义中引用该 PVC。
资源间关系如下图所示:
2.3.2 动态配置
考虑到静态配置的管理更加复杂,规模化使用 CSI 驱动时,一般会以「动态配置」方式使用。
- 管理员会负责创建一个或多个 StorageClass,
- 用户只需要创建 PVC,指定 StorageClass,
- 在 Pod 中引用该 PVC,CSI 驱动就会按照 StorageClass 中配置好的参数,为你自动创建 PV。
资源间关系如下:
以容器挂载模式为例,从创建到使用的流程大致如下:
- 用户创建 PVC,指定已经创建好的 StorageClass;
- CSI Controller 负责在 JuiceFS 文件系统中做初始化,默认以 PV ID 为名字创建子目录,同时创建对应的 PV。该过程所需的配置,都在 StorageClass 中指定或引用;
- Kubernetes (PV Controller 组件) 将上述用户创建的 PVC 与 CSI Controller 创建的 PV 进行绑定,此时 PVC 与 PV 的状态变为「Bound」;
- 用户创建应用Pod,声明使用先前创建的 PVC;
- CSI Node Service 负责在应用 Pod 所在节点创建 Mount Pod;
- Mount Pod 启动,执行 JuiceFS 客户端挂载,将挂载点暴露给宿主机,路径为 /var/lib/juicefs/volume/[pv-name];
- CSI Node Service 等待 Mount Pod 启动成功后,将 PV 对应的 JuiceFS 子目录 bind 到容器内,路径为其声明的 VolumeMount 路径;
- Kubelet 启动应用 Pod。
三、在CCE部署和使用JuiceFS
3.1 前提条件
- Kubernetes 集群是 1.14 及以上版本
- 集群能从外网拉取镜像,比如 Docker Hub 和 Quay,如果无法从这两个镜像仓库下载资源,考虑先「搬运镜像」,具体见:https://juicefs.com/docs/zh/csi/administration/offline#copy-images
- 在集群中一台节点上安装 Helm 3.1.0 及以上版本
- 已经创建了obs桶或者并行文件系统
- 已获取redis服务地址
- 在 JuiceFS 企业版私有部署环境下,需要在「文件系统认证信息」中需要填写 envs 字段,指定私有部署的控制台地址
3.2 使用helm安装JuiceFS
3.2.1 拉取chart包
步骤1: 加入 JuiceFS CSI 驱动的 Helm 仓库
helm repo add juicefs https://juicedata.github.io/charts/
helm repo update
步骤2: 然后查看charts资源包情况
# helm repo list |grep juicefs
juicefs https://juicedata.github.io/charts
# helm search repo juicefs
NAME CHART VERSION APP VERSION DESCRIPTION
juicefs/juicefs-csi-driver 0.19.3 0.23.0 A Helm chart for JuiceFS CSI Driver
juicefs/juicefs-s3-gateway 0.11.2 1.6.0 A Helm chart for JuiceFS S3 Gateway
步骤3: 检查 kubelet 根目录,用于更改values.yaml的kubeletDir配置
在CCE集群中任意一个worknode节点上执行以下命令:
ps -ef | grep kubelet |grep root-dir
以实际看到的为准,当前集群kubelet 根目录是:/mnt/paas/kubernetes/kubelet
3.2.2 修改values.yaml
新创建values-new.yaml,原values.yaml可以从chart包解压获取。
编辑 values-new.yaml 文件
vim values-new.yaml
image:
repository: juicedata/juicefs-csi-driver
tag: "v0.23.0"
pullPolicy: "IfNotPresent"
dashboardImage:
repository: juicedata/csi-dashboard
tag: "v0.23.0"
pullPolicy: "IfNotPresent"
# The kubelet working directory, can be set using --root-dir when starting kubelet
# 必须更改为当前集群节点kubelet 工作目录
kubeletDir: /mnt/paas/kubernetes/kubelet
3.2.3 执行helm install进行安装
指定配置文件进行安装
# helm install juicefs-csi-driver juicefs/juicefs-csi-driver -n kube-system -f ./values-new.yaml
安装结果如下图所示
3.2.4 安装完后查看运行的pod
使用 kubectl 查看JuiceFS CSI 驱动 服务pod状态:
# kubectl -n kube-system get pod -l app.kubernetes.io/name=juicefs-csi-driver
NAME READY STATUS RESTARTS AGE
juicefs-csi-controller-0 4/4 Running 0 5m15s
juicefs-csi-controller-1 4/4 Running 0 4m9s
juicefs-csi-dashboard-6cfdb7fd69-n78wp 1/1 Running 0 5m15s
juicefs-csi-node-mwfjw 3/3 Running 0 5m15s
3.3 动态创建
本次仅介绍动态配置方式,如果要使用静态配置方式创建pv请参考: https://juicefs.com/docs/zh/csi/guide/pv#static-provisioning
3.3.1 创建secret
场景:社区版,即元数据引擎使用自建的数据库
本次使用社区版,创建 Kubernetes Secret参考文档:https://juicefs.com/docs/zh/csi/guide/pv
apiVersion: v1
kind: Secret
metadata:
name: juicefs-secret-test1
type: Opaque
stringData:
name: juicefs-obs-test11
# 填写存储元数据的数据库地址,支持多种类型数据库,具体以官方为准
metaurl: "redis://:1********G@192.168.xx.xx:6379/1"
storage: obs
# 填写提前创建的OBS 桶的访问地址
bucket: "https://aaaaa-hzj.obs.cn-north-4.myhuaweicloud.com"
# 填写IAM用户的Access Key Id
access-key: WL**********RU
# 填写IAM用户的Secret Access Key
secret-key: Ye*********************************Gf
# 设置 Mount Pod 时区,默认为 UTC。
envs: "{TZ: Asia/Shanghai}"
# kubectl apply -f secret1.yaml
secret/juicefs-secret-test1 created
3.3.2 创建StorageClass
用 kubectl 创建 StorageClass,需要提前创建好secret,创建完毕后,将相关信息按照下方示范填入对应字段。
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: juicefs-sc
provisioner: csi.juicefs.com
parameters:
# 指定创建的secret名称juicefs-secret-test1
csi.storage.k8s.io/provisioner-secret-name: juicefs-secret-test1
# 指定创建的secret所在名称空间
csi.storage.k8s.io/provisioner-secret-namespace: default
# 指定创建的secret名称juicefs-secret-test1
csi.storage.k8s.io/node-publish-secret-name: juicefs-secret-test1
# 指定创建的secret所在名称空间
csi.storage.k8s.io/node-publish-secret-namespace: default
# kubectl apply -f sc.yaml
storageclass.storage.k8s.io/juicefs-sc created
3.3.3 创建pvc
kubectl apply -f - <<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: juicefs-pvc
namespace: default
spec:
accessModes:
- ReadWriteMany
resources:
requests:
# 从 StorageClass 中申请 10GiB 存储容量
storage: 10Gi
storageClassName: juicefs-sc
EOF
创建后,查看pvc 和 pv
433.4 创建deployment 挂载pvc
如果pod能挂载 pvc ,说明创建的secret和storageClass 正确。deployment yaml参考如下
kind: Deployment
apiVersion: apps/v1
metadata:
name: nginx-t1
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: nginx-t1
version: v1
template:
metadata:
labels:
app: nginx-t1
version: v1
spec:
containers:
- name: container-1
image: swr.cn-north-4.myhuaweicloud.com/testapp/my-nginx:1.22.1-perl
command:
- nginx
- '-g'
- daemon off;
ports:
- containerPort: 80
protocol: TCP
resources:
limits:
cpu: 100m
memory: 100Mi
requests:
cpu: 100m
memory: 100Mi
lifecycle:
postStart:
exec:
command:
- /bin/bash
- '-c'
- sleep 1; echo $(date -u) >> /data/out.txt;
volumeMounts:
- mountPath: /data
name: juicefs-pv
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: IfNotPresent
securityContext:
runAsUser: 0
restartPolicy: Always
terminationGracePeriodSeconds: 30
dnsPolicy: ClusterFirst
securityContext: {}
imagePullSecrets:
- name: default-secret
schedulerName: default-scheduler
volumes:
- name: juicefs-pv
persistentVolumeClaim:
claimName: juicefs-pvc
创建deployment后,查看pod状态
# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-t1-755675f4db-tghpd 1/1 Running 0 88s
查看pod 事件,显示挂载卷成功
查看对象存储,自动创建了一个目录:juicefs-obs-test11
- 点赞
- 收藏
- 关注作者
评论(0)