义乌做站外推广的公司,网红营销套路,ftp发布asp.net网站,潍坊建设网站公司电话前言本文对Fluid基础功能-数据编排能力进行原理解析。其中涉及到Fluid架构和k8s csi driver相关知识。建议先了解相关概念#xff0c;为了便于理解#xff0c;本文使用JuiceFS作为后端runtime引擎。原理概述Fuild数据编排能力#xff0c;主要是在云原生环境中#xff0c;能…前言本文对Fluid基础功能-数据编排能力进行原理解析。其中涉及到Fluid架构和k8s csi driver相关知识。建议先了解相关概念为了便于理解本文使用JuiceFS作为后端runtime引擎。原理概述Fuild数据编排能力主要是在云原生环境中能让用户在使用远端存储时只需简单声明几个对象即可像使用本地存储一样简单。无需关心后端的繁琐配置和数据存储、拉取过程。甚至无需关心后端的存储实现方式。该能力主要利用dataSet和Runtime以及对应的controller组件实现。dataset是用于告诉Fluid在哪里能够找到所需要的数据比如对于JuiceFS指的是 JuiceFS 的子目录是用户在 JuiceFS 文件系统中存储数据的目录。runtime这里根据后端实际引擎不同runtime的实现形式有多种比如AlluxioRuntime、JuiceFSRuntime等这些都是k8s的CRD。以JuiceFSRuntime为例它用于声明一个juiceFS的最小化集群包括worker副本数量worker的缓存形式mem、ssd、hddworker的缓存大小等。runtime-controller根据该声明部署相应的juiceFS组件。用户使用时只需要在pod中使用同名的pvc即可。工作流程梳理若字体看不清可点击图片查看大图详细流程解析一、用户创建DatasetapiVersion: data.fluid.io/v1alpha1
kind: Dataset
metadata:name: jfsdemo
spec:mounts:- name: testmountPoint: juicefs:///demooptions:bucket: bucketmounts字段namejuiceFS中创建的文件系统名称mountPoint指的是 JuiceFS 的子目录是用户在 JuiceFS 文件系统中存储数据的目录以 juicefs:// 开头如 juicefs:///demo 为 JuiceFS 文件系统的 /demo 子目录。options.bucketBucket URL。例如使用 S3 作为对象存储bucket 为对象存储URL如果使用其他比如miniomysql也为对应URL。二、Dataset Controller处理Datasetcontroller监听到dataset被创建将dataset状态设置为NotBound表示还没有与任何runtime绑定。三、用户创建JuiceFSRuntimeapiVersion: data.fluid.io/v1alpha1
kind: JuiceFSRuntime
metadata:name: jfsdemo
spec:replicas: 1tieredstore:levels:- mediumtype: MEMpath: /dev/shmquota: 40960mudiumtypeworker缓存形式MEMSSDHDDpathworker的缓存目录quota缓存大小单位Mi四、runtime controller处理JuiceFSRuntimecontroller监听到JuiceFSRuntime被创建开始一系列对于juiceFS 集群的创建操作。1查找对应的dataset同名同ns。并将dataset设置为该runtime的ownerReferences。...
if !utils.ContainsOwners(objectMeta.GetOwnerReferences(), dataset) {return r.AddOwnerAndRequeue(ctx, dataset)}
...2创建runtime 的master负载。根据runtime填入的参数通过helm进行安装valuefileName, err : j.generateJuicefsValueFile(runtime)
...found, err : helm.CheckRelease(j.name, j.namespace)
...return helm.InstallRelease(j.name, j.namespace, valuefileName, chartName)3更新runtime的状态为NotReady4创建runtime的worker负载根据runtime填入的参数通过helm进行安装同时设置pod 反亲和性worker分散在不同节点func (e *Helper) SetupWorkers(runtime base.RuntimeInterface,currentStatus datav1alpha1.RuntimeStatus,workers *appsv1.StatefulSet) (err error) {desireReplicas : runtime.Replicas()if *workers.Spec.Replicas ! desireReplicas {// workerToUpdate, err : e.buildWorkersAffinity(workers)workerToUpdate, err : e.BuildWorkersAffinity(workers)if err ! nil {return err}workerToUpdate.Spec.Replicas desireReplicaserr e.client.Update(context.TODO(), workerToUpdate)if err ! nil {return err}
5创建daemonset fuse组件且只会在有labelfluid.io/f-default-jfsdemotrue 的node上运行pod。目前所有node都没有该label因此fuse 的ds虽然部署成功但是pod运行数量暂时为0。什么时候会给node打上该label呢继续往后看。6等待worker和master的pod全部启动设置runtime的状态为Readyfunc (j *JuiceFSEngine) CheckAndUpdateRuntimeStatus() (ready bool, err error) {......runtimeToUpdate.Status.WorkerNumberReady int32(workers.Status.ReadyReplicas)runtimeToUpdate.Status.WorkerNumberUnavailable int32(*workers.Spec.Replicas - workers.Status.ReadyReplicas)runtimeToUpdate.Status.WorkerNumberAvailable int32(workers.Status.CurrentReplicas)if workers.Status.ReadyReplicas 0 {if runtime.Replicas() workers.Status.ReadyReplicas {runtimeToUpdate.Status.WorkerPhase data.RuntimePhaseReadyworkerReady true} else if workers.Status.ReadyReplicas 1 {runtimeToUpdate.Status.WorkerPhase data.RuntimePhasePartialReadyworkerReady true}7更新dataset状态由pending-boundfunc (j *JuiceFSEngine) BindToDataset() (err error) {return j.UpdateDatasetStatus(datav1alpha1.BoundDatasetPhase)
}8创建pv和pvccontroller接下来创建pv和pvc创建pvPersistentVolumeSource: v1.PersistentVolumeSource{CSI: v1.CSIPersistentVolumeSource{Driver: common.CSIDriver,VolumeHandle: pvName,VolumeAttributes: map[string]string{common.FluidPath: mountPath,common.MountType: mountType,其中pv的参数需要注意driver所使用的csi driver的名称。这个值必须与 CSI 驱动程序在 GetPluginInfoResponse 中返回的值相对应CSI 驱动程序也使用该值来辨识哪些 PV 对象属于该 CSI 驱动程序。这里common.CSIDriver就是fuse.csi.fluid.ioVolumeHandle唯一标识卷的字符串值。 该值必须与 CSI 驱动在 CreateVolumeResponse 的 volume_id 字段中返回的值相对应在所有对 CSI 卷驱动程序的调用中引用该 CSI 卷时都使用此值作为 volume_id 参数spec:
......csi:driver: fuse.csi.fluid.io //csi drivervolumeAttributes:fluid_path: /runtime-mnt/juicefs/default/jfsdemo/juicefs-fusemount_type: JuiceFSvolumeHandle: default-jfsdemopersistentVolumeReclaimPolicy: RetainstorageClassName: fluidvolumeMode: Filesystem9周期性同步runtime和dataset状态通过查询fuse podworker pod。更新runtime和dataset的状态。包括根据查询worker pod的metrics监控信息查询缓存状态。并更新runtime和dataset的缓存数据状态缓存进度。根据worker pod数量是否正常设置runtime和dataset的状态是否健康。10同步worker所在节点由于worker具有缓存能力因此需要对所在的node打上label标志该node具有数据缓存。方便对业务pod进行调度。查询所有worker所在节点并与当前已经打上缓存label的节点进行对比worker所在节点没有label的需要加上。worker不在节点上有label的需要删除。五、创建业务pod并使用该pvcapiVersion: v1
kind: Pod
metadata:name: demo-app
spec:containers:- name: demoimage: nginxvolumeMounts:- mountPath: /dataname: demovolumes:- name: demopersistentVolumeClaim:claimName: jfsdemok8s调度器将其调度到某个node上运行。将pod信息与node绑定接下来该节点上的kubelet接手pod开始pod的真正创建流程。六、kubelet向csi driver请求NodeStageVolumekubelet发现pod有pvc的需求kubelet的volumemanager组件会根据pvc声明所使用的csi driver名称fuse.csi.fluid.io。查询当前集群中注册的csi driver一旦发现匹配就根据注册的信息向csi driver发送请求让csi driver开始进行数据卷挂载。请求的接口是NodeStageVolume。该接口也是csi drvier规范中必须实现的方法。该接口用于如果存储卷没有格式化首先要格式化。然后把存储卷mount到一个临时的目录(这个目录通常是节点上的一个全局目录)。kubelet代码k8s.io/kubernetes/pkg/volume/csi/csi_attacher.go func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string) error {.....fsType : csiSource.FSTypeerr csi.NodeStageVolume(ctx,csiSource.VolumeHandle,publishContext,deviceMountPath,fsType,accessMode,nodeStageSecrets,csiSource.VolumeAttributes,mountOptions)....}七、csi driver 设置Node labelfluid的csi driver接收到kubelet发送的NodeStageVolume请求后设置所在Node的labelfluid.io/f-default-jfsdemotruefluid/pkg/csi/plugins/nodeserver.go // 2. Label nodefuseLabelKey : common.LabelAnnotationFusePrefix namespace - namevar labelsToModify common.LabelsToModifylabelsToModify.Add(fuseLabelKey, true)node, err : kubeclient.GetNode(ns.client, ns.nodeId)if err ! nil {glog.Errorf(NodeStageVolume: cant get node %s: %v, ns.nodeId, err)return nil, errors.Wrapf(err, NodeStageVolume: cant get node %s, ns.nodeId)}if _, ok : node.Labels[fuseLabelKey]; !ok {_, err utils.ChangeNodeLabelWithPatchMode(ns.client, node, labelsToModify)if err ! nil {glog.Errorf(NodeStageVolume: error when patching labels on node %s: %v, ns.nodeId, err)return nil, errors.Wrapf(err, NodeStageVolume: error when patching labels on node %s, ns.nodeId)}设置该label后之前部署的fuse daemonset会检测到该node存在符合条件label就会在node上拉起fuse pod。八、juice fuse pod进行本地目录挂载fuse pod在节点上启动后会使用juiceFS的命令将远端存储挂载到本地临时目录。fuse内部执行命令/usr/local/bin/juicefs format --storagemysql --bucketmysql2.redis.svc.linux.local:3306/test --access-key${ACCESS_KEY} --secret-key${SECRET_KEY} ${METAURL} mysql/bin/mount.juicefs redis://:123456mymaster,redis-0.redis.redis.svc.linux.local,redis-1.redis.redis.svc.linux.local,redis-2.redis.redis.svc.linux.local:26379/3 /runtime-mnt/juicefs/default/jfsdemo/juicefs-fuse /runtime-mnt/juicefs/default/jfsdemo/juicefs-fuse 就是本节点的临时存储目录。九、kubelet向csi driver请求NodePublishVolumecsi 规范的NodePublishVolume 方法。将存储卷从节点临时目录mount到目标目录(pod目录)。k8s.io/kubernetes/pkg/volume/csi/csi_mounter.gofunc (c *csiMountMgr) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {err csi.NodePublishVolume(ctx,volumeHandle,readOnly,deviceMountPath,dir,accessMode,publishContext,volAttribs,nodePublishSecrets,fsType,mountOptions,)
}十、csi driver 执行NodePublishVolume方法fluid的csi driver接收到kubelet发送的NodePublishVolume请求后将本节点临时目录mount bind到pod目录。func (c *csiMountMgr) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {err csi.NodePublishVolume(ctx,volumeHandle,readOnly,deviceMountPath,dir,accessMode,publishContext,volAttribs,nodePublishSecrets,fsType,mountOptions,)
}比如pod目录/var/lib/kubelet/pods/15b00274-11f2-4dde-9fdf-e590a6284e20/volumes/kubernetes.io~csi/default-jfsdemo/mount该目录也是在节点上也通过docker -v的形式挂载到pod内部因此该目录的改动也能够在pod内部感知到。以上就完成了将远端存储挂载到pod内部的操作。