flink原理源码分析(一) 集群与资源@k8s

1 简介

集群和资源模块提供动态资源能力,是分布式系统关键基础设施,分布式datax,分布式索引,事件引擎都需要集群和资源的弹性资源能力,提高扩展和作业处理能力。本文分析flink的集群和资源的k8s模块,深入了解其设计原理,为开发自有的集群和资源组件做技术准备, 同时涉及作业管理器,slot管理,不深入调度器。

本文分析基于flink 1.17版本,不同版本代码差异比较大

2 关键词

作业管理器

资源管理器

任务管理器

3 参考资料

flink官方网站 https://flink.apache.org/

4 flink整体架构

flink原理源码按板块逐一分析,本文分析**++集群和资源++**

API板块 Table&SQL, DataStream,DataSet将被丢弃

作业构建和转换板块

作业提交和接收 client,作业打包,提交,接收和分发

作业调度板块 执行图(Execution Graph), 调度器,作业管理器

作业执行板块 高可用,容错,状态,作业执行

5 flink运行架构

运行架构,按调用顺序展示flink集群启动,作业提交处理组件互动

总体上,集群是mater-worker架构,上图是flink的抽象架构,一个优秀架构可以抽象,第六章介绍架构"具体" k8s实现

6 flink@k8s运行架构

++作业管理器++ 得到**++任务管理器++**提供的资源(slot),启动task,资源申请完成。

7 启动集群

7.1 场景

下面详细分析各用例

7.2 启动k8s集群

k8s集群支持session和application模式,job模式将会被废弃,本文分析session模式集群

Configuration作为配置容器,几乎所有的构建需要从配置类获取配置项,这里不显示关联关系

  1. 用户命令行执行kubernates-session.sh,主入口是KubernetesSessionCli main

2.ClusterClientServiceLoader SPI机制载入ClusterClientFactory,k8s环境下实现类是KubernetesClusterClientFactory

  1. ClusterClientFactory是ClusterClient工厂,首先创建ClusterDescriptor集群描述,该类负责部署集群,最终返回ClusterClient,k8s环境下实现类是

KubernetesClusterDescriptor

4.KubernetesClusterDescriptor新建集群规格ClusterSpecification,该类不是针对k8s,定义了flink master和**++任务管理器++** 的内存容量等技术参数,通用于容器类的集群;

KubernetesSessionClusterEntrypoint类型设置为ENTRY_POINT_CLASS参数,后面《部署集群》使用到

5.KubernetesClusterDescriptor的deploySessionCluster部署k8s集群,参看《部署集群》用例,最后返回ClusterClient,用于后续提交作业和其他集群操作

7.3 部署集群

部署集群是启动的第二步,

KubernetesClusterDescriptor的deploySessionCluster方法的一部分,构建部署规格,flink kubeclient提交到k8s集群管理器,触发flink master构建和启动

  1. 首先更新配置,主要是对外端口,更新为固定的,支持对外Service通讯

  2. 构建

KubernetesJobManagerParameters,该类封装Configuration和ClusterSpecification,为获取参数提供便利

  1. 构建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分析

  1. 最后客户端调用

createClusterClientProvider返回ClusterClient

8 运行时

运行时提供了Flink作业运行过程依赖的基础执行环境,包含Dispatcher、ResourceManager、JobManager和TaskManager等核心组件,本节分析资源相关运行时组件构建和启动。

flink没有使用spring,缺少ioc的构建过程相当复杂,所有依赖手动关联和置入,为了共享组件,flink使用了很多中间持有共享组件的中间对象。

8.1 场景

部署集群通过flink kubeclient向k8s集群管理提交flink master的Deployment,触发flink master构建和启动

上图是构建和启动的交互图,参看《构建作业管理器部署规格》的CmdJobManagerDecorator设置了初始容器脚本和参数,集群初始入口ClusterEntryPoint

  1. 容器初始执行kubernetes-jobmanager.sh

ClusterEntryPoint设置为

KubernetesSessionClusterEntrypoint

2.KubernetesSessionClusterEntrypoint继承SessionClusterEntrypoint,只重新实现createDispatcherResourceManagerComponentFactory方法,设置ResourceManagerFactory为KubernetesResourceManagerFactory,就是说集群初始化逻辑与其他session集群区别不大

  1. 经过容错日志,插件文件系统初始化,进入ClusterEntrypoint的runCluster,该方法主要做两个事,initializeServices
    DispatcherResourceManagerComponent

  2. initializeServices构建基础服务,Rpc服务,Jmx服务,ha服务,blob服务,metics等

5.DispatcherResourceManagerComponent运行时组件,高可用组件的初始化/启动;同时也是持有者,用于后面关闭和清理

8.3 构建和启动任务管理器

构建和启动任务管理器是申请资源的一部分,按请求新worker的延申,类型分属于运行时一部分。

  1. createTaskManagerPod与10.2 新建作业管理组件类似,CmdTaskManagerDecorator,装饰器实现了pod装饰,装饰主容器,设置shell执行命令

设置执行脚本

KUBERNETES_TASK_MANAGER_SCRIPT_PATH= "kubernetes-jobmanager.sh"

主入口

KubernetesTaskExecutorRunner main方法

  1. 任务管理器启动比较简单,主要启动rpc服务和高可用组件,高可用触发9.5 注册任务管理器/报告资源

9 资源

本章分析资源管理的场景,服务级实现;核心数据流,资源槽从新增,报告,匹配,分配,到释放的完整生命周,详细了解每个场景下资源槽的状态型态 , 型态指资源类结构,类直接转换,状态属性,存储。

9.1 场景

资源处理有++声明式处理资源++ 和++细粒度处理资源++ 是两个实现,两者不是并行的两种实现策略,声明式是资源申请和分配方式,粒度是指资源分割方式,细粒度按需可变的资源,粗粒度是固定的资源,本文只分析++声明式粗粒度处理资源++

9.2 申请和分配资源(simple allocator)

用户提交作业,++分发器++ 接收并分发作业到**++作业管理器++** ,**++调度器++**确定所需资源,申请资源,检查当前可用资源是否足够,如果不足,请求新资源,动态增加资源;若足够,分配资源给任务。

分配还有另一个实现,slot sharing,有比较复杂的资源分配策略,分配策略跟本文主题无关,因此选了比较简单simple allocator

9.2.1 服务分析

DeclarativeSlotPoolBridge桥接DeclarativeSlotPool,声明式资源池,真正处理资源,用声明式SlotPool实现SlotPool,为了简化描述,描述不区分DeclarativeSlotPoolBridge和DeclarativeSlotPool

  1. **++调度器++**调用PhysicalSlotProvider的allocatePhysicalSlot分配资源

  2. allocatePhysicalSlot首先tryAllocateFromAvailable,从当前可用资源分配;若当前可用资源不够9.3 请求新资源

  3. tryAllocateFromAvailable调用DeclarativeSlotPoolBridge的

getAvailableSlotsInformation获取资源池的可用资源,其实际最终调用AllocatedSlotPool的getFreeSlotsInformation并组装为SlotInfoAndResources,该类组合了SlotInfo,分配信息;ResourceProfile,资源信息

  1. SlotSelectionStrategy选择策略在可用资源选择一个最合适的,目前基于位置策略

  2. 选出最合适的资源后,PhysicalSlotProvider调用DeclarativeSlotPoolBridge的allocateAvailableSlot分配资源

  3. DeclarativeSlotPoolBridge的allocateAvailableSlot分配资源,该方法调用DeclarativeSlotPool的

increaseResourceRequirementsBy增加资源请求(声明),该方法触发异步处理资源请求,9.2.21深入分析

  1. 最后保留资源,真正的分配在处理资源请求,保留资源AllocatedSlotPool的reserveFreeSlot登记未已分配资源AllocatedSlot

  2. 最后调整资源,保留不一定是最终分配,最终分配后调整实际资源情况

9.2.1.1 notifyNewResourceRequirements

最后分析一下

notifyNewResourceRequirements

notifyNewResourceRequirements类型Consumer,函数方法

图1

上图是

notifyNewResourceRequirements设置和调用的方法,函数是怎么设置?

图2

图1是图2的 connect调用,设置Service

设置方法是哪里调起?

ResourceManagerLeaderListener监听**++资源管理器++**选主,获取新主节点的地址

ResourceManagerLeaderListener通知JobMaster,调用JobMaster的

notifyOfNewResourceManagerLeader方法

notifyOfNewResourceManagerLeader启动rpc(重新)连接

  1. rpc连接后,触发onRegistrationSuccess事件方法,然后

DeclarativeSlotPoolService的

connectToResourceManager方法,而后者即图1,设置Conumer

总结,++请求新资源++ 主要是调用**++资源管理器++**的declareRequiredResources方法,该方法用

ResourceManagerLeaderListener和DeclareResourceRequirementServiceConnectionManager绕一下,是为了适应分布式环境下**++资源管理器++**上线下线,主节点选举后获取新主的地址,重新连接,每次连接完成重新设置ResourceManagerGateway

9.3 请求新资源(request new slots)

请求新资源是9.1 分配资源的延申,当前没有足够的可用资源,调度器请求新的资源

9.3.1 服务分析

类图跟9.2 申请和分配资源基本相同,场景实现由相同的类实现

**1. ++调度器++**在9.11 分配资源没有获得足够可用资源,调用SlotPool的requestNewAllocatedSlot,请求新的资源;这里的SlotPool是DeclarativeSlotPoolBridge桥接 DeclarativeSlotPool实现的SlotPool,实现声明式管理的资源池,下面不区分两者

2. DeclarativeSlotPoolBridge调用

increaseResourceRequirementsBy增加资源请求,触发检查资源请求,参看9.2.2.1深入分析

9.4 请求新worker(require new worker)

请求新worker是**++请求新资源++** 的一部分,检查资源声明判断需要新增资源,向集群管理请求部署新worker,触发**++任务管理器++**部署,任务管理器启动后报告新的资源。

  1. **++检查资源声明++**是9.2请求新资源的延申,判断需要增加资源时调用requestNewWorker

  2. requestNewWorker调用ResourceManagerDriver的requestResource方法,k8s环境下,ResourceManagerDriver的实现

KubernetesResourceManagerDriver,requestResource使用kubeclient向k8s集群管理发起《构建和启动任务管理器》,任务管理器启动后,9.5 注册任务管理器/报告资源

9.5 注册任务管理器/报告资源

++任务管理器++ 启动后注册到**++资源管理器++**,报告自身资源,资源通过这个方式新增的

9.5.1 服务分析

  1. ++任务管理器++ 启动,同时启动高可用组件,触发

ResourceManagerLeaderListener监听机制,注意,ResourceManagerLeaderListener有两个,一个是**++作业管理器++** ,一个是**++任务管理器++**,这里毫无疑问用任务管理器相关的那个

ResourceManagerLeaderListener调用TaskExecutor的notifyOfNewResourceManagerLeader方法

notifyOfNewResourceManagerLeader方法使用RegisteredRpcConnection连接到**++资源管理器++**

  1. RegisteredRpcConnection连接完成后,触发事件onRegistrationSuccess,onRegistrationSuccess调用**++资源管理器++**的sendSlotReport(rpc)方法报告资源

  2. ResourceManager的sendSlotReport方法,调用SlotManager注册**++任务管理器++**和登记资源

  3. 最后采用延时异步方式执行《检查资源请求》,登记新资源,触发处理资源请求

9.6 请求使用资源(request slots)/提供资源(offer slots)

++调度器++ 申请和分配资源,匹配了合适的空闲资源,++资源管理器++ 请求资源所属的**++任务管理器++** 使用资源,**++任务管理器++**确认提供资源

资源请求使用和提供两步操作,++资源管理器++ 不直接使用资源,主要让资源使用情况放在**++任务管理器++** ,++资源管理器++ 下线, 选出新**++资源管理器++** master,**++任务管理器++**重新注册,恢复资源使用情况

9.6.1 服务分析

  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

  1. 最后AllocatedSlot放入资源slot分配池AllocatedSlotPool,返回接收的offerSlots给TaskExecutor处理,一部分没有匹配不接收

关于

matchOfferWithOutstandingRequirements(TBD)

9.7 检查资源需求/检查资源声明

检查资源请求/检查资源声明是flink声明式资源管理 的关键方法,上面的资源场景分为两类,++提出资源需求++ 和**++提供资源++**, 检查资源请求/检查资源声明是交汇点,处理资源请求,该分配的分配,该请求新的请求新的资源;检查资源声明,哪些资源可以释放,需要新资源请求新worker。

本章深入分析两方法,上游提出资源需求和下游提供资源的串联,资源状态演变,存储型态

9.7.1 检查资源请求(checkResourceRequirements)

**++检查资源请求++**是真正的分配资源,上面说的分配实际只是请求分配,增加请求

1) 获取作业的未完成资源请求

2) 尝试分配可用资源到作业

之所以尝试,资源变更触发调用检查资源请求,但不一定是增加,可能是无效分配

slotTracker获取所有可用资源,与请求匹配,合适的分配allocateSlot,该方法对应场景9.6 请求使用资源/提供资源

3) 尝试使用待定的资源

待定资源是指申请了新的worker或者将要申请新worker所产生的资源,目前没有物理上的对应资源,通俗说就是先占个"坑",等申请的资源下来填回去

同样,首先匹配现有的待定资源,若还有未分配打开新的待定资源

tryAllocateWorkerAndReserveSlot调用TaskExecutorManager的allocateWorker,预先挖好"坑"

declareNeededResourcesWithDelay方法下节介绍,按需要申请新的worker,增加物理资源

到此还有一个问题,物理资源到位后怎样填"坑"

首先,自然想到9.5 注册任务管理器/报告资源,使用新增资源抵消待定

TaskExecutorManager的registerTaskManager方法

9.7.2 检查资源声明(checkResourceDeclarations)

声明资源,要申请多少资源,可释放多少资源,上一节检查资源请求打开新待定资源,最终调用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

10.1 场景

10.2 新建作业管理器组件

KubernetesJobManagerFactory构建KubernetesJobManagerSpecification

KubernetesJobManagerSpecification有两属性Deployment和accompanyingResources,前者Deployment是k8s资源对象,后者是类型是List<HasMetadata>,用来管理发布的控制器,详情可参考k8s文档;accompanyingResources定义k8s资源对象,如 ConfigMap,Service等

  1. KubernetesStepDecorator装饰器,装饰器列表实现责任链模式,装饰用模板构建的FlinkPod,包括主容器和其他容器,增加其特性,用户使用此机制定制pod,后面几章分析几个典型的装饰器

  2. Fabric8FlinkKubeClient的createJobManagerComponent,请求k8s集群构建和启动flink master组件,输入参数是

KubernetesJobManagerSpecification,其Deployment属性直接用于Fabric8 kubeclient创建Deployment对象

下面分析几个作业管理组件重要的容器装饰器

10.2.1InitJobManagerDecorator

读入配置属性,设置pod的容器

10.2.2CmdJobManagerDecorator

装饰器只实现了pod装饰,装饰主容器,设置shell执行命令

设置执行脚本

KUBERNETES_JOB_MANAGER_SCRIPT_PATH = "kubernetes-jobmanager.sh"

deploymentTarget kubernetes-session或kubernetes-application

10.2.3ExternalServiceDecorator

装饰器构建k8s Service对象

读入ServiceType,flink对应k8s Service的抽象类,实现

buildUpExternalRestService方法,构建k8s Service

10.2.4FlinkConfMountDecorator/PodTemplateMountDecorator

两个装饰器功能相同,构建ConfigMap,非主容器作为存储卷,主容器挂载存储卷

10.3 总结

flink kubeclient要点

  1. 模板构建初始的FlinkPod,拥有初始的完整的特性和属性

  2. 可配置的/可扩展的装饰器增加FlinkPod的特性和属性,以及相应的资源,ConfigMap,Service等

  3. Resource是k8s资源的简单封装;ServiceType是 k8s Service对象的构建器基类

11回顾

最后,本章以运行架构图串联起分析场景

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 请求使用资源/提供资源

12 附录

flink分析系列

Ø 系列(二) 作业提交,接收和分发

Ø 系列(三) 作业执行,高可用,状态,容错

Ø 系列(四) 作业调度

Ø 系列(五) 作业构建和转换

Ø 系列(六) 作业API-DataStream

Ø 系列(七) 作业API-Table&SQL

Ø 其他,内存管理,rpc

相关推荐
Patrick_Wilson21 小时前
从「改个端口」到 502:Next.js on k8s 的容器端口、Service 映射与 env 覆盖
docker·kubernetes·next.js
tonyabasy1 天前
Flink 实时数仓开发实战:SQL中也能做到资源精细化管理
flink
探索云原生1 天前
K8s 1.36 这个 GA 特性,把 initContainer 拉模型的 hack 干掉了
ai·云原生·kubernetes
云恒要逆袭1 天前
运行你的第一个Docker容器
后端·docker·容器
大大大大晴天1 天前
浅聊Flink实时关联计算的不适用场景
flink
Java之美2 天前
一次k8s升级引发的DevicePlugin注册失败
云原生·kubernetes
大大大大晴天2 天前
深入解析 Flink Kafka Connector:原理、配置与最佳实践
flink
程序员老赵3 天前
10 分钟部署 OpenCode:Docker 一键安装,浏览器打开就能用 AI 写代码(附完整命令与排错)
docker·容器·ai编程
武子康6 天前
调查研究-183 Apple container:Mac 上用轻量 VM 跑 Linux 容器,Swift 会改写本地容器体验吗?
docker·容器·apple