集群和资源模块提供动态资源能力,是分布式系统关键基础设施,分布式datax,分布式索引,事件引擎都需要集群和资源的弹性资源能力,提高扩展和作业处理能力。本文分析flink的集群和资源的k8s模块,深入了解其设计原理,为开发自有的集群和资源组件做技术准备, 同时涉及作业管理器,slot管理,不深入调度器。
本文分析基于flink 1.17版本,不同版本代码差异比较大
作业管理器
资源管理器
任务管理器
flink官方网站 https://flink.apache.org/
flink原理源码按板块逐一分析,本文分析集群和资源
API板块 Table&SQL, DataStream,DataSet将被丢弃
作业构建和转换板块
作业提交和接收 client,作业打包,提交,接收和分发
作业调度板块 执行图(Execution Graph), 调度器,作业管理器
作业执行板块 高可用,容错,状态,作业执行
运行架构,按调用顺序展示flink集群启动,作业提交处理组件互动
总体上,集群是mater-worker架构,上图是flink的抽象架构,一个优秀架构可以抽象,第六章介绍架构”具体” k8s实现
作业管理器得到任务管理器提供的资源(slot),启动task,资源申请完成。
下面详细分析各用例
k8s集群支持session和application模式,job模式将会被废弃,本文分析session模式集群
Configuration作为配置容器,几乎所有的构建需要从配置类获取配置项,这里不显示关联关系
1. 用户命令行执行kubernates-session.sh,主入口是KubernetesSessionCli main
2.ClusterClientServiceLoader SPI机制载入ClusterClientFactory,k8s环境下实现类是KubernetesClusterClientFactory
3. ClusterClientFactory是ClusterClient工厂,首先创建ClusterDescriptor集群描述,该类负责部署集群,最终返回ClusterClient,k8s环境下实现类是
KubernetesClusterDescriptor
4.KubernetesClusterDescriptor新建集群规格ClusterSpecification,该类不是针对k8s,定义了flink master和任务管理器的内存容量等技术参数,通用于容器类的集群;
KubernetesSessionClusterEntrypoint类型设置为ENTRY_POINT_CLASS参数,后面《部署集群》使用到
5.KubernetesClusterDescriptor的deploySessionCluster部署k8s集群,参看《部署集群》用例,最后返回ClusterClient,用于后续提交作业和其他集群操作
部署集群是启动的第二步,
KubernetesClusterDescriptor的deploySessionCluster方法的一部分,构建部署规格,flink kubeclient提交到k8s集群管理器,触发flink master构建和启动
1. 首先更新配置,主要是对外端口,更新为固定的,支持对外Service通讯
2. 构建
KubernetesJobManagerParameters,该类封装Configuration和ClusterSpecification,为获取参数提供便利
3. 构建FlinkPod,首先载入pod模板构建KubernetesPod,KubernetesPod是一个包装了k8s Pod对象的Resource类;构建FlinkPod,定义了mainContainer ,podWithoutMainContainer,其中主容器固定名称,用于运行作业管理器,任务管理器,其他容器运行相关的资源,如ConfigMap,Service等,详细分析参考10 flink kubeclient
4.KubernetesJobManagerFactory《构建作业管理器部署规格》构建部署规格,Fabric8FlinkKubeClient使用部署规格《新建作业管理器组件(flink master)》部署flink master组件,两用例在10 flink kubeclient分析
5. 最后客户端调用
createClusterClientProvider返回ClusterClient
运行时提供了Flink作业运行过程依赖的基础执行环境,包含Dispatcher、ResourceManager、JobManager和TaskManager等核心组件,本节分析资源相关运行时组件构建和启动。
flink没有使用spring,缺少ioc的构建过程相当复杂,所有依赖手动关联和置入,为了共享组件,flink使用了很多中间持有共享组件的中间对象。
部署集群通过flink kubeclient向k8s集群管理提交flink master的Deployment,触发flink master构建和启动
上图是构建和启动的交互图,参看《构建作业管理器部署规格》的CmdJobManagerDecorator设置了初始容器脚本和参数,集群初始入口ClusterEntryPoint
1. 容器初始执行kubernetes-jobmanager.sh
ClusterEntryPoint设置为
KubernetesSessionClusterEntrypoint
2.KubernetesSessionClusterEntrypoint继承SessionClusterEntrypoint,只重新实现createDispatcherResourceManagerComponentFactory方法,设置ResourceManagerFactory为KubernetesResourceManagerFactory,就是说集群初始化逻辑与其他session集群区别不大
3. 经过容错日志,插件文件系统初始化,进入ClusterEntrypoint的runCluster,该方法主要做两个事,initializeServices和
DispatcherResourceManagerComponent
4. initializeServices构建基础服务,Rpc服务,Jmx服务,ha服务,blob服务,metics等
5.DispatcherResourceManagerComponent运行时组件,高可用组件的初始化/启动;同时也是持有者,用于后面关闭和清理
构建和启动任务管理器是申请资源的一部分,按请求新worker的延申,类型分属于运行时一部分。
1. createTaskManagerPod与10.2 新建作业管理组件类似,CmdTaskManagerDecorator,装饰器实现了pod装饰,装饰主容器,设置shell执行命令
设置执行脚本
KUBERNETES_TASK_MANAGER_SCRIPT_PATH= "kubernetes-jobmanager.sh"
主入口
KubernetesTaskExecutorRunner main方法
2. 任务管理器启动比较简单,主要启动rpc服务和高可用组件,高可用触发9.5 注册任务管理器/报告资源
本章分析资源管理的场景,服务级实现;核心数据流,资源槽从新增,报告,匹配,分配,到释放的完整生命周,详细了解每个场景下资源槽的状态和型态, 型态指资源类结构,类直接转换,状态属性,存储。
资源处理有声明式处理资源和细粒度处理资源 是两个实现,两者不是并行的两种实现策略,声明式是资源申请和分配方式,粒度是指资源分割方式,细粒度按需可变的资源,粗粒度是固定的资源,本文只分析声明式粗粒度处理资源
用户提交作业,分发器接收并分发作业到作业管理器,调度器确定所需资源,申请资源,检查当前可用资源是否足够,如果不足,请求新资源,动态增加资源;若足够,分配资源给任务。
分配还有另一个实现,slot sharing,有比较复杂的资源分配策略,分配策略跟本文主题无关,因此选了比较简单simple allocator
DeclarativeSlotPoolBridge桥接DeclarativeSlotPool,声明式资源池,真正处理资源,用声明式SlotPool实现SlotPool,为了简化描述,描述不区分DeclarativeSlotPoolBridge和DeclarativeSlotPool
1. 调度器调用PhysicalSlotProvider的allocatePhysicalSlot分配资源
2. allocatePhysicalSlot首先tryAllocateFromAvailable,从当前可用资源分配;若当前可用资源不够9.3 请求新资源
3. tryAllocateFromAvailable调用DeclarativeSlotPoolBridge的
getAvailableSlotsInformation获取资源池的可用资源,其实际最终调用AllocatedSlotPool的getFreeSlotsInformation并组装为SlotInfoAndResources,该类组合了SlotInfo,分配信息;ResourceProfile,资源信息
4. SlotSelectionStrategy选择策略在可用资源选择一个最合适的,目前基于位置策略
5. 选出最合适的资源后,PhysicalSlotProvider调用DeclarativeSlotPoolBridge的allocateAvailableSlot分配资源
6. DeclarativeSlotPoolBridge的allocateAvailableSlot分配资源,该方法调用DeclarativeSlotPool的
increaseResourceRequirementsBy增加资源请求(声明),该方法触发异步处理资源请求,9.2.21深入分析
7. 最后保留资源,真正的分配在处理资源请求,保留资源AllocatedSlotPool的reserveFreeSlot登记未已分配资源AllocatedSlot
8. 最后调整资源,保留不一定是最终分配,最终分配后调整实际资源情况
最后分析一下
notifyNewResourceRequirements
notifyNewResourceRequirements类型Consumer,函数方法
图1
上图是
notifyNewResourceRequirements设置和调用的方法,函数是怎么设置?
图2
图1是图2的 connect调用,设置Service
设置方法是哪里调起?
1.
ResourceManagerLeaderListener监听资源管理器选主,获取新主节点的地址
2.
ResourceManagerLeaderListener通知JobMaster,调用JobMaster的
notifyOfNewResourceManagerLeader方法
3.
notifyOfNewResourceManagerLeader启动rpc(重新)连接
4. rpc连接后,触发onRegistrationSuccess事件方法,然后
DeclarativeSlotPoolService的
connectToResourceManager方法,而后者即图1,设置Conumer
总结,请求新资源主要是调用资源管理器的declareRequiredResources方法,该方法用
ResourceManagerLeaderListener和DeclareResourceRequirementServiceConnectionManager绕一下,是为了适应分布式环境下资源管理器上线下线,主节点选举后获取新主的地址,重新连接,每次连接完成重新设置ResourceManagerGateway
请求新资源是9.1 分配资源的延申,当前没有足够的可用资源,调度器请求新的资源
类图跟9.2 申请和分配资源基本相同,场景实现由相同的类实现
1. 调度器在9.11 分配资源没有获得足够可用资源,调用SlotPool的requestNewAllocatedSlot,请求新的资源;这里的SlotPool是DeclarativeSlotPoolBridge桥接 DeclarativeSlotPool实现的SlotPool,实现声明式管理的资源池,下面不区分两者
2. DeclarativeSlotPoolBridge调用
increaseResourceRequirementsBy增加资源请求,触发检查资源请求,参看9.2.2.1深入分析
请求新worker是请求新资源的一部分,检查资源声明判断需要新增资源,向集群管理请求部署新worker,触发任务管理器部署,任务管理器启动后报告新的资源。
1. 检查资源声明是9.2请求新资源的延申,判断需要增加资源时调用requestNewWorker
2. requestNewWorker调用ResourceManagerDriver的requestResource方法,k8s环境下,ResourceManagerDriver的实现
KubernetesResourceManagerDriver,requestResource使用kubeclient向k8s集群管理发起《构建和启动任务管理器》,任务管理器启动后,9.5 注册任务管理器/报告资源
任务管理器启动后注册到资源管理器,报告自身资源,资源通过这个方式新增的
1. 任务管理器启动,同时启动高可用组件,触发
ResourceManagerLeaderListener监听机制,注意,ResourceManagerLeaderListener有两个,一个是作业管理器,一个是任务管理器,这里毫无疑问用任务管理器相关的那个
2.
ResourceManagerLeaderListener调用TaskExecutor的notifyOfNewResourceManagerLeader方法
3.
notifyOfNewResourceManagerLeader方法使用RegisteredRpcConnection连接到资源管理器
4. RegisteredRpcConnection连接完成后,触发事件onRegistrationSuccess,onRegistrationSuccess调用资源管理器的sendSlotReport(rpc)方法报告资源
5. ResourceManager的sendSlotReport方法,调用SlotManager注册任务管理器和登记资源
6. 最后采用延时异步方式执行《检查资源请求》,登记新资源,触发处理资源请求
调度器申请和分配资源,匹配了合适的空闲资源,资源管理器请求资源所属的任务管理器使用资源,任务管理器确认提供资源
资源请求使用和提供两步操作,资源管理器不直接使用资源,主要让资源使用情况放在任务管理器,资源管理器下线, 选出新资源管理器master,任务管理器重新注册,恢复资源使用情况
1. 调度器分配资源完成,向资源所在的任务管理器TaskExecutor发出使用请求requestSlot
2. TaskExecutor调用allocateSlotForJob,登记资源使用jobId, slotId, allocationId, resourceProfile, targetAddress
3. TaskExecutor rpc-offerSlots通知JobMaster,确认提供资源
4. JobMaster转交SlotPool的offerSlots处理,同样,最终处理是DeclarativeSlotPool
5. DeclarativeSlotPool的
matchOfferWithOutstandingRequirements匹配offerSlot与未完成的资源请求,构建AllocatedSlot
6. 最后AllocatedSlot放入资源slot分配池AllocatedSlotPool,返回接收的offerSlots给TaskExecutor处理,一部分没有匹配不接收
关于
matchOfferWithOutstandingRequirements(TBD)
检查资源请求/检查资源声明是flink声明式资源管理的关键方法,上面的资源场景分为两类,提出资源需求和提供资源, 检查资源请求/检查资源声明是交汇点,处理资源请求,该分配的分配,该请求新的请求新的资源;检查资源声明,哪些资源可以释放,需要新资源请求新worker。
本章深入分析两方法,上游提出资源需求和下游提供资源的串联,资源状态演变,存储型态
检查资源请求是真正的分配资源,上面说的分配实际只是请求分配,增加请求
1) 获取作业的未完成资源请求
2) 尝试分配可用资源到作业
之所以尝试,资源变更触发调用检查资源请求,但不一定是增加,可能是无效分配
slotTracker获取所有可用资源,与请求匹配,合适的分配allocateSlot,该方法对应场景9.6 请求使用资源/提供资源
3) 尝试使用待定的资源
待定资源是指申请了新的worker或者将要申请新worker所产生的资源,目前没有物理上的对应资源,通俗说就是先占个“坑”,等申请的资源下来填回去
同样,首先匹配现有的待定资源,若还有未分配打开新的待定资源
tryAllocateWorkerAndReserveSlot调用TaskExecutorManager的allocateWorker,预先挖好”坑”
declareNeededResourcesWithDelay方法下节介绍,按需要申请新的worker,增加物理资源
到此还有一个问题,物理资源到位后怎样填”坑”
首先,自然想到9.5 注册任务管理器/报告资源,使用新增资源抵消待定
TaskExecutorManager的registerTaskManager方法
声明资源,要申请多少资源,可释放多少资源,上一节检查资源请求打开新待定资源,最终调用checkResourceDeclarations,实际申请新worker获得物理资源,为了支持动态/静态资源申请,中间ResourceAllocator转接了一下,这里不详细分析
现有的worker数量-需要的worker数量,大于0,worker多了可以释放;反之,worker少了,需要打开新worker
requestNewWorker参看9.4 请求新worker
ResourceDeclaration怎么来?
主要是计算totalWorkerNum,目前worker总数量
totalWorkerNum = pendingWorkerNum + neededRegisteredWorkers
pendingWorkerNum 待定的slots除以每个worker的slots,向上修正,只多不少
neededRegisteredWorkers是已经注册的worker减去待释放的worker
flink kubeclient是面向flink应用的fabric8 kubeclient的封装,本节分析flink如何封装kubeclient,核心组件是装饰器,资源和ServiceType,下面通过分析业务创建作业管理器组件(createJobManagerComponent)了解flink kubeclient
1.
KubernetesJobManagerFactory构建KubernetesJobManagerSpecification
2.
KubernetesJobManagerSpecification有两属性Deployment和accompanyingResources,前者Deployment是k8s资源对象,后者是类型是List
3. KubernetesStepDecorator装饰器,装饰器列表实现责任链模式,装饰用模板构建的FlinkPod,包括主容器和其他容器,增加其特性,用户使用此机制定制pod,后面几章分析几个典型的装饰器
4. Fabric8FlinkKubeClient的createJobManagerComponent,请求k8s集群构建和启动flink master组件,输入参数是
KubernetesJobManagerSpecification,其Deployment属性直接用于Fabric8 kubeclient创建Deployment对象
下面分析几个作业管理组件重要的容器装饰器
读入配置属性,设置pod的容器
装饰器只实现了pod装饰,装饰主容器,设置shell执行命令
设置执行脚本
KUBERNETES_JOB_MANAGER_SCRIPT_PATH = "kubernetes-jobmanager.sh"
deploymentTarget kubernetes-session或kubernetes-application
装饰器构建k8s Service对象
读入ServiceType,flink对应k8s Service的抽象类,实现
buildUpExternalRestService方法,构建k8s Service
两个装饰器功能相同,构建ConfigMap,非主容器作为存储卷,主容器挂载存储卷
flink kubeclient要点
1. 模板构建初始的FlinkPod,拥有初始的完整的特性和属性
2. 可配置的/可扩展的装饰器增加FlinkPod的特性和属性,以及相应的资源,ConfigMap,Service等
3. Resource是k8s资源的简单封装;ServiceType是 k8s Service对象的构建器基类
最后,本章以运行架构图串联起分析场景
1 7.2 启动k8s集群,7.3 部署集群,10.2 新建作业管理器组件
2 8.2 构建和启动flink mater组件
3 提交作业,N/A
4 分发作业,N/A
5 9.2 申请和分配资源
6 9.3 请求新资源
7 9.4 请求新worker,8.3 构建和启动任务管理器
8 9.5 注册任务管理器/报告资源
9 /10 9.6 请求使用资源/提供资源
Ø 系列(二) 作业提交,接收和分发
Ø 系列(三) 作业执行,高可用,状态,容错
Ø 系列(四) 作业调度
Ø 系列(五) 作业构建和转换
Ø 系列(六) 作业API-DataStream
Ø 系列(七) 作业API-Table&SQL
Ø 其他,内存管理,rpc