☰
Flink 上 Kubernetes 实战:flink-operator 部署、配置与高可用关键点
2026/10/21 13:17:56 网站建设 项目流程

如果你正在做实时计算,迟早要面对一个问题:Flink 集群怎么在 Kubernetes 上稳定地跑起来。自己手写 YAML 一条条部署当然也能跑,但当作业数量上来之后,升级、扩缩容、故障恢复全都变成手工活,稍不注意就把线上搞挂。flink-operator 的出现,就是用 Kubernetes 声明式的方式统一管理 Flink 集群,你只需要提交一个 FlinkDeployment 对象,创建、更新、销毁一套 Flink 集群的活都交给 Operator 控制器去处理。这篇文章面向正在调研或准备落地的开发和运维同学,讲清楚 operator 的安装、第一个 Flink 集群的部署、核心参数怎么配,以及一堆实际部署中才会遇到的坑。

1. 为什么选择 flink-operator 而不是自己写 YAML

1.1 手动部署 Flink 到 Kubernetes 的真实痛点

先说说我自己最早是怎么干的。一个 Flink 集群在 Kubernetes 上跑起来,表面上只需要拉起 JobManager 和 TaskManager 两组 Pod,但真正要交付生产环境时,需要准备的东西远不止两个 Deployment。

JobManager 要暴露 REST 和 Web UI 端口,TaskManager 之间要互相通信,这意味着至少三个 Service;Flink 的配置文件、日志配置、高可用元数据存储,通常要拆成 ConfigMap、PVC 等对象;如果你要把 Dashboard 暴露给外部访问,还得再补 Ingress。一个最小可用的集群,YAML 加在一起轻松超过两百行。这还只是初始创建,真正痛苦的是后面的变更。

升级 Flink 版本的时候,你要改镜像 tag,还要担心 JobManager 和 TaskManager 镜像不一致导致的兼容问题;调整并行度的时候,你得手动算 TaskManager 副本数,改完 Deployment 的 replicas 之后还要盯着滚动更新;JobManager 单点挂掉的时候,Kubernetes 只会帮你重新拉起一个 Pod,但状态、Checkpoint 元数据、作业恢复这些事它一概不管。做过一轮之后你就会意识到,这种方式适合尝鲜,不适合长期运维。

1.2 Operator 模式带来的变化

Operator 模式本质上解决的是“应用生命周期管理自动化”的问题。它把运维人员对 Flink 集群的认知固化到一段控制逻辑里,通过自定义资源(CRD)暴露给上层用户。你只需要提交一个 FlinkDeployment 对象,声明“我要一个 Flink 1.17 的集群,2 个 TaskManager slot,作业并行度 4”,Operator 会负责生成底层所有 Kubernetes 资源,并持续保证实际状态向期望状态收敛。

用一个生活化的类比,传统手动部署就像你每次开店都要自己进货、摆货架、算库存;而 Operator 模式就像一个店长,你只需要告诉它“我要开一家什么样的店”,它自己去搞定货架、补货、盘点,甚至打折促销。省下来的时间,你可以真正去关心业务代码和作业稳定性。

在实际项目中,我最大的体感是:以前需要写脚本去完成的发布动作,现在变成了一条kubectl apply -f xxx.yaml;以前升级需要人工编排的“先起新 JobManager、再做 Savepoint、再切换流量”流程,变成了 Operator 内部的一个内置步骤。它更适合多团队、多作业、需要频繁发布和回滚的中大型实时计算场景。

2. flink-kubernetes-operator 的原理与版本选型

2.1 一次调谐流程是怎么跑起来的

flink-kubernetes-operator 安装到集群后,核心是一组控制器 Pod,它们监听 FlinkDeployment 资源的变化。当用户执行kubectl apply提交一个 FlinkDeployment 时,请求会先经过准入 Webhook 做参数校验和默认值填充,然后写入 etcd。

控制器通过 List-Watch 机制拿到这个新对象,进入调谐循环。它把期望状态(CR 里的 spec)和当前状态(集群中已存在的 Deployment、Service、ConfigMap)做对比,然后执行一系列操作:创建 JobManager Deployment、创建 REST Service、计算 TaskManager 副本数并创建对应 Deployment、写入 Flink 配置文件、配置 Ingress 等。操作完成后,控制器把结果写回 FlinkDeployment 的 status 字段。

所以你会看到 FlinkDeployment 对象上有类似jobManagerDeploymentStatus: READY、jobStatus.state: RUNNING这样的状态信息。这些状态不是人工维护的,而是控制器每次调谐后自动更新的。理解了这一点,排障的时候就会很自然地去看 status 和 events,而不是一头扎进 YAML 里瞎改。

2.2 CRD 版本选择:FlinkCluster 还是 FlinkDeployment

这是很多人第一次接触时会踩的坑。早期版本的 flink-kubernetes-operator 使用FlinkCluster作为核心 CRD,API 组是flink.apache.org/v1alpha1。后来社区为了更好地区分“集群管理”和“作业管理”,引入了FlinkDeployment,API 版本升级为v1beta1,把集群和应用的生命周期统一到一个对象里。

如果你是从网上翻到的一些老教程,里面很可能是FlinkCluster的写法,直接套用到新版 Operator 上通常会报no matches for kind "FlinkCluster"。我建议你以官方文档当前版本为准,新项目一律使用FlinkDeployment。它不仅能描述 JobManager 和 TaskManager 的拓扑,还能描述作业的 jar 包、并行度、升级模式,一个 CR 覆盖所有诉求。

如果你在集群里已经存在旧的 FlinkCluster 资源,升级 Operator 之前需要先确认兼容性。社区通常会在 release notes 里说明哪些旧 API 仍然可用,但长期维护的角度看,还是尽早迁移到 FlinkDeployment 更省心。

2.3 版本组合怎么选

Operator、Flink、Kubernetes 三者之间存在版本匹配关系,不能随意组合。根据我目前的经验,以下几个组合比较稳:

组件建议版本说明
Kubernetes1.25 及以上1.27 / 1.28 实测正常,旧版本需要确认 CRD 兼容性
Helm3.8 及以上安装和升级 Operator 的主要方式
flink-kubernetes-operator1.7.x 系列API 稳定,支持 Flink 1.17 / 1.18
Flink1.17 或 1.18与 Operator 1.7.x 配合成熟,社区支持度好
共享存储支持 ReadWriteMany生产环境做高可用和 Checkpoint 的硬性要求

选版本时最怕的是“看着差不多就上了”。比如拿一个较新的 Operator 去管理旧版 Flink 集群,API 里新增的字段会被忽略,但某些默认行为可能直接导致集群起不来。我自己的习惯是先到官方文档查看版本兼容矩阵,再在小集群上做一次冒烟测试,确认没问题后再推到生产。

3. 环境准备与 Operator 安装

3.1 动手前先过一遍环境清单

安装 Operator 之前,先把基础环境确认好。Kubernetes 集群要保持可用状态,kubectl能正确连接集群;Helm 版本不要太老,我建议 3.8 以上;集群内部需要允许创建和更新 CRD 资源,也就是你要具备足够的 RBAC 权限。

另外要确认集群是否启用了 admission webhook 支持。flink-kubernetes-operator 安装时会注册一个 ValidatingWebhookConfiguration 用于校验 FlinkDeployment 参数。如果你的集群环境比较特殊,比如某些受管集群默认禁用了 webhook 扩展能力,安装后 Operator 可能无法正常工作。遇到这种情况,需要先查询集群是否支持admissionregistration.k8s.io/v1这个 API 组。

存储方面,如果只是本地测试,可以暂时不准备共享存储;但如果你打算跑生产集群,尤其是要开启高可用模式,一定要提前准备一个 ReadWriteMany 的 StorageClass。这个点后面讲 HA 时会详细说,现在先记在心里。

3.2 Helm 方式安装 Operator

安装 flink-kubernetes-operator 最主流的方式是 Helm。把官方 Helm 仓库添加到本地后,直接安装即可。由于仓库地址在不同版本中可能会调整,我建议你在动手前到项目官方文档里复制最新的仓库地址,别凭记忆拼写。

helm repo add flink-operator <FLINK_OPERATOR_REPO_URL> helm repo update

然后创建命名空间并安装:

kubectl create namespace flink helm install flink-kubernetes-operator flink-operator/flink-kubernetes-operator \ --namespace flink \ --create-namespace

安装过程会创建 FlinkDeployment 相关的 CRD、服务账号、RBAC 权限、Operator Deployment 和 Webhook 配置。默认情况下,Operator 会监听所有命名空间中的 FlinkDeployment 资源,如果你希望限制监听范围,可以设置watchNamespaces参数。比如只允许在default和flink-demo两个命名空间里创建 Flink 集群:

helm install flink-kubernetes-operator flink-operator/flink-kubernetes-operator \ --namespace flink \ --set watchNamespaces="{default,flink-demo}"

这个参数在生产环境很有用,可以避免 Operator 的服务账号权限被滥用。但要注意,设置之后其他命名空间里的 FlinkDeployment 就不会被处理,如果你后续有新增命名空间的需求,需要重新升级 Helm release。

3.3 验证安装是否正常

安装完成后,先确认 Pod 状态和 CRD 是否注册成功。

kubectl -n flink get pods kubectl get crd | grep flink.apache.org

正常情况下,Opeartor 的 Pod 应该处于 Running 状态,CRD 列表里能看到flinkdeployments.flink.apache.org等条目。接着查看 Operator 日志,确认没有证书相关的报错:

kubectl -n flink logs deployment/flink-kubernetes-operator

这里我想提醒一句:Operator 的启动日志里如果出现了 x509 证书过期或 webhook 注册失败的提示,多半是 Helm chart 自带的证书生成逻辑出了问题。比较常见的解决办法是删除相关 Secret 后重新执行helm upgrade,让 chart 重新生成证书。如果集群里已经安装了 cert-manager,也可以配置 chart 使用 cert-manager 管理证书,但这需要额外修改 values 文件,建议在正式使用前多做一次验证。

4. 部署第一个 Flink 集群

4.1 准备命名空间和服务账号

Operator 安装好之后,先创建一个专门跑 Flink 集群的命名空间,并准备一个服务账号。FlinkDeployment 里可以指定serviceAccount,这个服务账号会被 JobManager 和 TaskManager 使用。如果你的作业需要访问 Kubernetes API(比如使用 Kubernetes 原生 HA),这个服务账号还需要绑定相应 RBAC 权限。

kubectl create namespace flink-demo kubectl -n flink-demo create serviceaccount flink

如果暂时不需要作业访问集群内的其他资源,使用默认服务账号也可以,但我还是建议显式创建一个专用服务账号,方便后续做权限隔离和审计。

4.2 写一个最小 FlinkDeployment

创建一个basic-example.yaml,内容如下:

apiVersion: flink.apache.org/v1beta1 kind: FlinkDeployment metadata: name: basic-example namespace: flink-demo spec: image: flink:1.17.2 flinkVersion: v1_17 flinkConfiguration: taskmanager.numberOfTaskSlots: "2" serviceAccount: flink jobManager: replicas: 1 resource: memory: "1024m" cpu: 1 taskManager: resource: memory: "1024m" cpu: 1

注意几个关键点。image使用的是官方 Flink 镜像,flinkVersion的格式是v1_17,下划线而不是点。这个字段必须和镜像里的 Flink 大版本保持一致,否则 Operator 在生成配置时可能计算出错误的内存参数。taskmanager.numberOfTaskSlots表示每个 TaskManager 提供的 slot 数量,这里设置了 2,意味着后续作业如果并行度是 4,Operator 会拉起 2 个 TaskManager。

4.3 查看状态与访问 Dashboard

用kubectl apply提交 CR 后,观察资源创建情况:

kubectl apply -f basic-example.yaml kubectl -n flink-demo get flinkdeployments kubectl -n flink-demo get pods

第一次创建时,JobManager 和 TaskManager 的 Pod 会依次启动。你可以用kubectl describe flinkdeployment basic-example查看详细状态,status 里如果出现jobManagerDeploymentStatus: READY,说明集群已经正常拉起。

访问 Web UI 最简单的方式是端口转发:

kubectl -n flink-demo port-forward svc/basic-example-rest 8081:8081

浏览器打开localhost:8081就能看到 Flink Dashboard。这里要注意 Service 的名称规则:如果 FlinkDeployment 叫basic-example,对应的 REST Service 一般是basic-example-rest,不是basic-example。我见过不少人在这里卡住,反复检查 Service 配置,其实只是名字没找对。

4.4 用示例作业跑通全链路

集群起来了,接着验证作业提交能力。官方 Flink 镜像自带了示例 jar,可以在 FlinkDeployment 里直接声明要运行的作业。修改 YAML,加上job部分:

spec: job: jarURI: local:///opt/flink/examples/streaming/WindowWordCount.jar parallelism: 2 upgradeMode: savepoint state: running

重新 apply 之后,Operator 会在集群启动完成后自动提交该作业。查看作业状态:

kubectl -n flink-demo get flinkdeployments basic-example -o jsonpath='{.status.jobStatus.state}'

如果输出是RUNNING,说明从集群创建到作业提交的全链路已经跑通。upgradeMode: savepoint这个配置很关键,它决定了后续你更新作业或升级版本时,Operator 是否会先做 Savepoint 再优雅重启。对于生产环境,我强烈建议配置成savepoint,不要图省事用stateless。

5. 核心配置深度解析:资源、高可用与监控

5.1 内存和 CPU 配置的正确姿势

资源配额是部署 FlinkDeployment 时最容易出问题的地方。很多人的第一反应是给 Pod 设置一个很大的内存,然后发现 JobManager 反复重启。原因很简单:Flink 的 JVM 内存分为堆内、堆外、JVM Overhead、网络缓冲区等多个部分,仅设置 Pod 总内存并不能决定各个部分如何分配。

Operator 在生成 Flink 配置时,会根据jobManager.resource.memory和taskManager.resource.memory自动推导 JVM 参数。如果你同时在flinkConfiguration里手动写了jobmanager.memory.process.size、taskmanager.memory.process.size等参数,就很可能和 Pod 资源产生冲突。我的经验是:二选一。要么只设置 Pod 的resource.memory和resource.cpu,让 Operator 和官方镜像的 entrypoint 脚本自动计算;要么你把flinkConfiguration里的内存参数填得明明白白,同时把 Pod 内存也对应调整。

内存大小的选择也没有绝对标准。JobManager 主要承担调度和协调工作,一般 1-2G 足够;TaskManager 是真正干活的地方,建议至少给到 2-4G,slot 数量根据自己的作业并发情况设置。注意,slot 数量并不是越多越好,一个 slot 对应一个执行线程,如果 CPU 和内存不足,slot 再多也只是把线程切换开销变大。

5.2 高可用:从单副本到多副本

默认情况下,jobManager.replicas是 1,也就是只有一个 JobManager。这个配置在测试环境没问题,生产环境却是绝对不可接受的。JobManager 一旦异常宕机,集群会失去调度能力,所有运行中的作业都会失败。

Flink 在 Kubernetes 上支持原生 HA 模式,原理是利用 Kubernetes 的 ConfigMap 和 Leader Election 机制,让多个 JobManager 实例选举出一个 Leader,其余实例作为 Standby。配置方式如下:

spec: jobManager: replicas: 2 flinkConfiguration: high-availability: kubernetes high-availability.storageDir: <共享存储路径>

high-availability.storageDir是 HA 元数据的存放位置,JobManager 会把作业图、Checkpoint 元数据等信息写到这里。Standby JobManager 启动时会从这个目录恢复状态,接管后无缝继续运行。

这里有个非常容易踩的坑:storageDir必须是所有 JobManager 副本都能访问的共享存储,而且存储必须支持多节点同时挂载。如果你选了普通的单节点读写 PVC,第二个 JobManager 在另一台机器上启动时根本读不到数据。生产环境请务必准备 ReadWriteMany 的存储方案,比如共享文件系统或对象存储。

5.3 监控与日志的接入思路

Flink 自身暴露了大量的 Metrics,包括吞吐量、延迟、Checkpoint 耗时、背压情况等。要采集这些数据,最常用的方案是 Prometheus。在flinkConfiguration里配置 Prometheus Reporter:

spec: flinkConfiguration: metrics.reporter.prom.factory.class: org.apache.flink.metrics.prometheus.PrometheusReporterFactory metrics.reporter.prom.port: "9249"

然后通过 Prometheus 的 ServiceMonitor 或手工配置抓取任务来收集指标。如果你还没有 Prometheus,也可以先在本地端口转发 9249 端口看一眼指标是否正常输出。日志方面,Flink 默认会输出到 stdout,容器运行时可以收集这些日志。如果你需要更精细的日志配置,可以通过 FlinkDeployment 的spec.logConfig字段覆盖默认的 log4j 配置。实际项目中,我通常是把日志输出到 stdout,再由日志采集组件统一收集和检索,这样省心且不会丢日志。

6. 日常运维:升级、扩缩容与资源回收

6.1 Flink 版本和作业升级

使用 Operator 之后,升级 Flink 版本变成了一件“改一行配置”的事。你只需要修改 FlinkDeployment 中的image为新的 Flink 镜像版本,然后kubectl apply。Operator 检测到字段变化后,会执行滚动升级:先处理 JobManager,再根据计算结果调整 TaskManager。

如果作业配置了upgradeMode: savepoint,升级过程中 Operator 会先让作业停止并生成 Savepoint,然后用新镜像重新启动作业,并从该 Savepoint 恢复状态。这个过程通常不用人工干预,是 Operator 最有价值的能力之一。

但升级过程中有一个细节需要注意:新版本镜像里必须存在jarURI指向的文件。如果你把作业 jar 放在自定义镜像里,升级前一定要确认新镜像已经包含新的 jar。否则,作业会因为没有提交物而启动失败,然后一直处于 RESTARTING 状态。我踩过一次这个坑,排查了半天才发现是 jar 路径在新镜像里不存在。

6.2 并行度与 TaskManager 数量调整

很多人会误以为 TaskManager 数量是手动设置的,其实在 Operator 模式下,TaskManager 的数量是根据作业并行度和每个 TaskManager 的 slot 数自动计算的。并行度 4、每个 TaskManager 2 个 slot,就会自动拉起 2 个 TaskManager。这个设计让用户只需要关心“作业要多少并行度”,而不需要关心底层 Pod 数量。

当你调整spec.job.parallelism时,Operator 会触发一次带 Savepoint 的作业重启,并根据新的并行度和 slot 数自动调整 TaskManager 数量。如果你只调整taskmanager.numberOfTaskSlots,同样会触发资源拓扑变化。生产环境中调整并行度前,最好先评估一下状态大小和状态后端,因为并行度变化可能会影响状态分布,Savepoint 恢复速度慢会直接影响作业可用性。

6.3 删除集群后如何处理残留资源

删除 FlinkDeployment 时,Operator 会清理对应的 Deployment、Service、ConfigMap 等资源,但 PVC 和 Savepoint 数据可能不会自动删除,这是很多运维同学容易忽略的地方。

如果你创建集群时配置了 PVC 用于存储,删除 CR 后 PVC 依然存在。这是有意设计的,为了防止误删数据。想彻底清理时,需要手动删除 PVC:

kubectl -n flink-demo get pvc kubectl -n flink-demo delete pvc <PVC名称>

同时,如果你用了对象存储保存 Savepoint,那部分数据也需要根据清理策略单独处置。我的建议是,在删除测试集群前,先确认没有需要保留的作业状态,再一次性清理 PVC 和存储目录。否则重建同名集群时,可能会因为旧 PVC 被占用或元数据残留,导致新集群起不来。

7. 常见问题排查速查表与实战心得

7.1 高频问题排查速查表

部署和维护过程中,我积累了一些高频问题的排查经验,整理成表格供你参考:

现象常见原因排查思路
Operator Pod CrashLoopBackOffWebhook 证书生成异常或过期查看 Operator 日志,删除证书 Secret 后重新执行helm upgrade
FlinkDeployment 创建后一直 Pending资源配额不足、StorageClass 不存在kubectl describe pod查看 Events,确认 PVC 和节点资源
JobManager 反复 CrashLoopBackOff内存配置自相矛盾删掉flinkConfiguration里手动设置的 memory 参数,只保留 Pod 资源
作业一直 RESTARTING用户代码异常、Checkpoint 存储不可用查看 JobManager 日志,检查 Savepoint 和 Checkpoint 路径的连通性
Dashboard 无法访问Service 名称不对、端口转发目标错误用kubectl get svc确认 Service 名称,REST 服务通常以-rest结尾

这套速查表覆盖了大部分初级问题。归根结底,排障的顺序一定是先看事件,再看日志,最后才去怀疑 Operator 本身。大多数问题并不是 Operator 的 bug,而是配置不当或环境不满足预期。

7.2 两个容易被忽略的坑

第一个坑是证书过期。flink-kubernetes-operator 安装时生成的 Webhook 证书通常有有效期限制。证书过期后,你再向集群提交 FlinkDeployment 时,请求会被 Webhook 拦截,报出类似failed calling webhook的错误。这时候最直接的处理方法是重新执行一次helm upgrade让证书重新生成,或者按官方文档配置证书自动续期。

第二个坑是删除集群后立即重建同名集群。由于旧集群的 PVC 和服务资源可能还处于 Terminating 状态,新集群如果同名,控制器在创建时可能因为资源冲突而一直等待。最简单的方法是把kubectl delete flinkdeployment等你确定资源完全清理后再重新创建。

7.3 最后的实操心得

我个人在实际操作中有几条心得。第一,版本一定要成套升级,不要只升级 Operator 或只升级 Flink,两者对不上是最难排查的隐形问题。第二,任何新方案先在一个小集群上跑通全流程再引入生产环境。我见过不少团队在 UAT 环境验证都没问题,一上生产就翻车,差距往往就在存储、RBAC 这类容易被忽视的基建上。第三,生产环境必须开启 HA,哪怕业务规模再小,也不要用单副本 JobManager 赌稳定性。这四个问题处理完之后,flink on k8s 这条路基本就比较顺畅了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询