《Apache Flink 案例集(2022版)》——4.云原生——小红书-Native Flink on Kubernetes 在小红书的实践(2) https://developer.aliyun.com/article/1228080
2. Native Flink on Kubernetes
小红书书选择Native Flink on K8s部署模式的原因是因为它具备如下三个特征:
更短的 Failover 时间;
可以实现资源托管,不需要手动创建 TaskManager 的 pod,也可以自动完成销毁;
具有更加便捷的高可用(HA)方案。
上图是 Native Flink on K8s 的体系架构图。Flink客户端里面集成了一个K8s客户端,它可以直接和K8s API Server进行通讯,完成JobManager部署以及ConfigMap的创建。JobManager部署完成之后,它里面的 ResourceManager模块可以直接和K8s API Server进行通讯,完成 TaskManager Pod 的创建和销毁工作,这也是它与Session集群模式比较大的不同之处。
在新的模式下,小红书对Flink作业状态维护机制做了一次重构,引入了一个Headless类型的服务以及一个状态数据库。在JobManager模块,通过JobManager状态监听器不断监听作业状态变化,并将这个变化上传到作业的状态数据库中,百川平台(小红书实时计算平台)可以通过查询数据库来获取任务的状态。另外在某些场景下,可能因为作业状态上传失败导致百川无法获取到任务的状态,百川还是可以走原来的路径,通过Ingress去访问JobManager来获取任务的状态。此时的Ingress和之前不同之处在于它绑定的是一个Headless服务,不需要占用集群的Cluster IP,这就解决了之前模式下K8s ClusterIP以及NodePort不足的问题。
此外,在Helm管理模式下镜像管理是通过将所有代码统一打包到一个大的 镜像里,但这样会存在一个问题,对任何模块的修改都需要对整个代码库进行一次编译打包,而这个过程是非常耗时的。
在新的模式下,小红书针对镜像版本管理做了一些优化,主要是将 Flink 的镜像拆分为了三个部分,分别是Flink引擎、Connector 以及第三方插件。这三个部分都有各自版本号,并且可以自由进行拼装组合。这项优化降低了引擎打包的频率,也意味着可以提升发版效率。
拆分之后,Flink 如何将这些镜像组合成一个可以运行的镜像呢?下面以加载一个 Kafka SDK 插件为例来进行阐述。作业运行时会从一个动态配置仓库中获取当前作业应该使用的 Kafka SDK 版本,并将其传递给百川的后端,这个 SDK 版本对应了Docker仓库里面的一个镜像,镜像只包含一个 SDK 对应的 JAR 包,百川的后端在渲染Pod模板的时候,会在InitContainer阶段将镜像加载进来,同时将Kafka的JAR 包移动到Flink container某个指定的目录下去,以此完成加载。
在实际Application Mode的应用过程中,小红书也发现了原生Flink的一些问题,并做了对应的处理方案。例如 JobManager 在作业failover的时候会重新拉起一批新的TaskManager从而导致资源翻倍。如果资源池的资源不足以满足翻倍的需求,就有可能导致failover失败。此外,即使这一次failover成功了,但是新启动的作业会基于首次启动时指定的recover path来进行恢复,这个时候的位点可能已经是一个十天以前的位点了,这会导致数据重复消费的问题。针对这个问题,在检测到 JobManager 发生 failover 的时候就会在引擎侧直接将作业状态置为失败并告警,然后通过人工手动介入来进行处理。
未来规划
动态资源调整。目前, Flink job 一旦提交运行,就无法在运行期间修改某个 operator 占用的资源。所以希望未来能够在 job 不进行 restart 的情况下,调整某个算子所占用的资源;
跨云多活方案。目前公司核心 P0 作业基本都是双链路的,但都仅限于在单朵云上。希望针对这些核心任务,实现跨云双活方案,其中一个云上任务出现问题的时候,能够稳定切换到另外一朵云上;
批任务资源调度优化。因为批任务大多是在凌晨以后开始执行,同时会调度很多任务,有的任务可能因为抢占不到资源导致无法及时运行,在任务调度执行策略上仍有可以优化的空间。