Standalone模式需要提前确认好每个任务需要使用的资源,并在配置文件里面配置,每一个任务都是固定资源大小,申请多了浪费,少了怕出问题。
Native模式不需要预先确定需要使用的资源数量,系统会实时根据任务需要自动去k8s集群申请能申请到的资源。
Application模式资源隔离性强,每个人物都是单独的集群,不会出现并发问题。每个任务都需要启动一个集群,会先启动JobManager,然后启动TaskManager,效率会比较低。适合流处理任务
对比yarn环境下的perjob提交任务速度快很多,大约是十几秒就能提交执行;yarn环境下提交任务需要一分多钟。
Session模式需要提前创建好集群,所有任务共享集群资源,并发下可能会有问题。共用集群,只需要启动TaskManager,效率高。适合批处理任务
还有一种方式叫operator模式,这种方式的优点是有一个开源服务,这个服务来帮你管理yml配置文件,你不需要自己去管理各种资源的配置。但是需要单独启动这个服务,然后调用这个服务的api去管理yml文件的配置功能。
优点:
管理 Flink 集群更加便捷
flink-operator 更便于我们管理 Flink 集群,我们不需要针对不同的 Flink 集群维护 Kubenretes 底层各种资源的部署脚本,唯一需要的,就是 FlinkCluster 的一个自定义资源的描述文件。用户只需要在该文件中声明期望的 Flink 集群配置,flink-operator 会自动完成 Flink 集群的创建和维护工作。如果创建 Per Job 集群,也只需要在该 yaml 中声明 Job 的属性,如 Job 名称,Jar 包路径即可。通过 flink-operator,上文提到的四种 Flink 运行模式,分别对应一个 yaml 文件即可,非常方便。
声明式
通过执行脚本命令式的创建 Flink 集群各个底层资源,需要用户保证资源是否依次创建成功,往往伴随着辅助的检查脚本。借助 flink operator 的控制器模式,用户只需声明所期望的 Flink 集群的状态,剩下的工作全部由 Flink operator 来保证。在 Flink 集群运行的过程中,如果出现资源异常,如 JobMaster 意外停止甚至被删除,Flink operator 都会重建这些资源,自动的修复 Flink 集群。
自定义保存点
用户可以指定 autoSavePointSeconds 和保存路径,Flink operator 会自动为用户定期保存快照。
自动恢复
流式任务往往是长期运行的,甚至 2-3 年不停止都是常见的。在任务执行的过程中,可能会有各种各样的原因导致任务失败。用户可以指定任务重启策略,当指定为 FromSavePointOnFailure,Flink operator 自动从最近的保存点重新执行任务。
Ingress 集成
用户可以定义 Ingress 资源,flink operator 将会自动创建 Ingress 资源。云厂商托管的 Kubernetes 集群一般都有 Ingress 控制器,否则需要用户自行实现 Ingress controller。
Prometheus 集成
通过在 Flink 集群的 yaml 文件里指定 metric exporter 和 metric port,可以与 Kubernetes 集群中的 Prometheus 进行集成。
缺点:
需要单独启动一个服务
它的很多优点基于api的方式也能实现
Standalone模式:定义好配置文件,然后通过kubectl命令去创建集群,目前没找到api方式创建
Native模式:
通过flink客户端去创建集群
也可以使用api的方式去创建
Flink 在 K8s 上最简单的方式是以 Standalone 方式进行部署。这种方式部署的好处在于不需要对 Flink 做任何改动,同时 Flink 对 K8s 集群是无感知的,通过外部手段即可让 Flink 运行起来。
Standalone方式在k8s运行步骤:
如图所示:
步骤1, 使用 Kubectl 或者 K8s 的 Dashboard 提交请求到 K8s Master。
//创建session集群 kubectl create -f flink-configuration-configmap.yaml kubectl create -f jobmanager-service.yaml kubectl create -f jobmanager-rest-service.yaml kubectl create -f jobmanager-deployment.yaml kubectl create -f taskmanager-deployment.yaml //提交任务到集群 ./bin/flink run -m localhost:8081 ./examples/streaming/WordCount.jar |
现在我们看一下 Perjob 的部署,因为 Session Cluster 和 Perjob 分别都有不同的适用场景,一个 Session 里面可以跑多个任务,但是每个任务之间没有办法达到更好的隔离性。而 Perjob 的方式,每个job都会有一个自己独立的 Flink Cluster 去运行,它们之间相互独立。
■ Perjob 的特点:
■ 执行步骤:
由 Standalone JobCluster EntryPoint 执行,从 classpath 找到用户 Jar,执行它的 main 方法得到 JobGrapth 。再提交到 Dispathcher,这时候走 Recover Job 的逻辑,提交到 JobMaster。JobMaster 向 ResourceManager 申请资源,请求 slot,执行 Job。
kubectl create -f flink-configuration-configmap.yaml kubectl create -f jobmanager-service.yaml kubectl create -f jobmanager-rest-service.yaml kubectl create -f jobmanager-job.yaml kubectl create -f taskmanager-job-deployment.yaml |
为什么叫 Native 方式?包括如下几个含义。
具体如何工作?主要分 Session 和 Perjob 两个方面来给大家介绍。
首先 Session 的方式。
Session方式代码
// 启动session集群,可以指定clusterId,image地址,还有一些CPU,内存的设定 ./bin/kubernetes-session.sh \ -Dkubernetes.cluster-id=k8s-session-1 \ -Dkubernetes.container.image=flink-on-kubernetes-job:1.0.2 \ -Dkubernetes.container.image.pull-policy=Always \ -Djobmanager.heap.size=4096m \ -Dtaskmanager.memory.process.size=4096m \ -Dtaskmanager.numberOfTaskSlots=4 \ -Dkubernetes.jobmanager.cpu=1 -Dkubernetes.taskmanager.cpu=2 // 提交任务到session集群,需要指定clusterId,而且session集群的service必须暴露为8081端口,应该是flink客户端默认值就是提交到8081端口 ./bin/flink run \ --target kubernetes-session \ -Dkubernetes.cluster-id=flink-session-first-cluster-v1 \ ./examples/streaming/WordCount.jar |
我们再来看一下 Perjob 的方式,如图所示,Perjob 方式其实和之前是有一些类似,差别在于不需要先去起一个 Session Cluster,再提交任务,而是一步的。
Application模式提交任务
// 不需要提前启动集群,直接提交任务创建集群执行任务 ./bin/flink run-application -p 10 -t kubernetes-application \ -Dkubernetes.cluster-id=k8s-app1 \ -Dkubernetes.container.image=flink-on-kubernetes-job:1.0.2 \ -Dkubernetes.container.image.pull-policy=Always \ -Djobmanager.heap.size=4096m -Dtaskmanager.memory.process.size=4096m \ -Dkubernetes.jobmanager.cpu=1 -Dkubernetes.taskmanager.cpu=2 \ -Dtaskmanager.numberOfTaskSlots=4 \ local:///opt/flink/examples/streaming/WindowJoin.jar |
我们来看一下 Session 和 Perjob 方式有哪些不同?
背景:目前大多数服务都基于k8s去一键部署,可以解决环境带来的问题并大大提高部署效率,更优的方案是基于云原生的方式去部署,解决动态扩缩容问题,提高资源利用率。所以大数据服务也需要能基于k8s云原生的方式去部署。
调研:目前比较常见的解决方案都是基于k8s上面部署yarn,然后在yarn里面启动flink集群。这个方案解决了k8s部署问题,但是没办法解决资源利用率问题,任务启动的时候必须指定资源数量,资源少了不够用,资源多了浪费,没法实现动态扩缩容。
实现方案:直接基于k8s的云原生方案去实现,去除yarn层,而且可以基于API的方式启动任务,还可以动态配置容器资源,目前可以设置CPU和内存参数。但是还有一个比较棘手的问题需要解决:APP方式提交任务,需要提前把任务代码的jar包打到镜像里面,启动任务的时候指定jar包路径和名称,而且需要一个任务一个jar包,N个任务N个jar包。这种方式比较麻烦,而且没法动态实现任务的启动。
方案一:网上找了一下方案,都是说任务启动的时候动态去下载需要的jar包,这样也需要提前把一个任务打成jar包,放到可以下载的服务上,还是不够灵活。
方案二:翻看源码,发现flink1.11到1.12版本支持一个特殊参数:kubernetes.container-start-command-template,defaultValue:"%java% %classpath% %jvmmem% %jvmopts% %logging% %class% %args% %redirects%",参数说明:"Template for the kubernetes jobmanager and taskmanager container start invocation.",通过参数说明可以发现,这个参数可以配置k8s启动容器时执行jar服务的命令。其中包括classpath设置、jvm相关的参数设置、日志配置,启动类class设置、main函数的args参数设置等等。基于这个发现,大胆做了一个设想方案,开发一个jar服务,获取java服务启动jvmopts里面或者args里面的参数,两种方式都可以,然后根据参数去数据库读取任务信息,根据获取到的信息执行任务。
最终采取了方案二实现,方案一不符合整体FlinkSP架构的易用性这一点,方案二更符合我们整体架构的思路,通过任务管理平台去创建任务,任务数据保存到MySQL数据库,然后Flink任务解析服务通过任务名称去获取任务详情,并提交任务到Flink环境执行任务。
方案实现图: