前言
在Fluid中,Dataset资源对象中所定义的远程文件是可被调度的,这意味着你能够像管理你的Pod一样管理远程文件缓存在Kubernetes集群上的存放位置。另外,Fluid同样支持对于应用的数据缓存亲和性调度,这种调度方式将应用(e.g. 数据分析任务、机器学习任务等)与所需要的数据缓存放置在一起,以尽可能地减少额外的开销。
本文对Fluid功能-数据缓存亲和性调度能力进行原理解析。其中涉及到Fluid架构和k8s csi driver相关知识。建议先了解相关概念。
为了便于理解,本文使用JuiceFS作为后端runtime引擎。
原理概述
实际上,fluid是利用node label和k8s原生提供的pod亲和性、反亲和性实现的。
在之前文章中,我们知道runtime 缓存worker pod所在的node上,都会被打上label:s-runtime.namespace-runtime.name,表示该node具有数据缓存能力。
对于业务pod使用webhook拦截,在pod的spec配置中,加上对具有该label的node的亲和性。最后借助k8s调度器将pod尽可能调度到具有缓存数据的Node上。
工作流程梳理
详细流程解析
一、创建业务pod,并使用dataset pvc
apiVersion: v1
kind: Pod
metadata:
name: demo-app
spec:
containers:
- name: demo
image: nginx
volumeMounts:
- mountPath: /data
name: demo
volumes:
- name: demo
persistentVolumeClaim:
claimName: jfsdemo
二、fluid webhook拦截Pod
1)检测pod是否符合要求
排除fluid自身组件的pod:
func (a *CreateUpdatePodForSchedulingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
......
if pod.Labels["app"] == "alluxio" || pod.Labels["app"] == "jindofs" || pod.Labels["app"] == "goosefs" || pod.Labels["app"] == "juicefs" {
setupLog.Info("skip mutating the pod because it's fluid Pods", "Pod", pod.Name, "Namespace", pod.Namespace)
return admission.Allowed("skip mutating the pod because it's fluid Pods")
......
}
2)检测是否具有dataset的PVC
对于不具有的,需要添加Node 反亲和性,最好不要调度到有缓存的节点上。
func getPreferredSchedulingTermForPodWithoutCache() corev1.PreferredSchedulingTerm {
return corev1.PreferredSchedulingTerm{
Weight: 100,
Preference: corev1.NodeSelectorTerm{
MatchExpressions: []corev1.NodeSelectorRequirement{
{
Key: common.GetDatasetNumLabelName(),
Operator: corev1.NodeSelectorOpDoesNotExist,
},
},
},
}
}
3)添加Node亲和性:必须要在fuse pod所在的Node上
因为只有fuse pod,才能与底层的runtime进行通信,因此业务pod必须要与fuse pod在同一节点:
requiredSchedulingTerm = corev1.NodeSelectorTerm{
MatchExpressions: []corev1.NodeSelectorRequirement{
{
Key: runtimeInfo.GetCommonLabelName(),
Operator: corev1.NodeSelectorOpIn,
Values: []string{"true"},
},
},
4)添加Node亲和性:最好要在worker所在Node
这里就是缓存亲和性调度了。最好与worker pod在同一节点。
preferredSchedulingTerm = &corev1.PreferredSchedulingTerm{
Weight: 100,
Preference: corev1.NodeSelectorTerm{
MatchExpressions: []corev1.NodeSelectorRequirement{
{
Key: runtimeInfo.GetCommonLabelName(),
Operator: corev1.NodeSelectorOpIn,
Values: []string{"true"},
},
},
},
}
}