4. Flink on Kubernetes实战:从零搭建到滚动升级
Kubernetes 现在已经是 Flink 部署的“标配”了。我最早接触 Flink on K8s 是在 2020 年,那时候还流行手动写 Deployment 和 Service,后来 Flink Operator 出来之后,整个体验好了不止一个档次。这一章,我们就从 K8s 集群搭建开始,一步步走到 Flink Operator 部署、配置文件编写,最后演示滚动升级。
4.1 K8s 集群搭建:别在生产环境用 minikube
我个人习惯用 kubeadm 来搭集群。minikube 和 kind 更适合本地开发,生产环境还是老老实实用 kubeadm 或者托管的云服务。
这里我给出一个最小化集群的搭建步骤:
- 节点准备:至少 1 台 Master + 2 台 Worker,操作系统 Ubuntu 20.04+ 或 CentOS 7.9+。
- 安装容器运行时:我推荐 containerd,Docker 虽然也能用,但 containerd 更轻量。
- 安装 kubeadm、kubelet、kubectl:版本要统一,我一般用 1.27 或 1.28。
- 初始化 Master 节点:
kubeadm init --pod-network-cidr=10.244.0.0/16 - 安装网络插件:Flannel 或 Calico,我习惯用 Flannel,简单。
- Worker 节点加入:用
kubeadm join命令。
kubelet 启动失败,先检查 /var/log/messages 或者 journalctl -u kubelet。我曾经因为 swap 没关,卡了半小时。
集群搭好后,验证一下:
kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane 12m v1.28.2
k8s-worker-01 Ready <none> 8m v1.28.2
k8s-worker-02 Ready <none> 8m v1.28.2
看到所有节点都是 Ready 状态,就说明集群没问题了。
4.2 Flink Operator 部署:别再用 Helm 踩坑了
Flink Operator 本质上是一个 Kubernetes 的 自定义控制器,它监听 FlinkDeployment 这个 CRD(自定义资源),然后自动帮你管理 Flink 集群的生命周期。
部署方式有两种:
| 方式 | 命令 | 适用场景 |
|---|---|---|
| Helm Chart | helm install flink-operator flink-operator/ |
生产环境,参数可定制 |
| YAML 直接部署 | kubectl apply -f flink-operator.yaml |
快速验证,测试环境 |
我个人推荐用 Helm,因为可以方便地调整资源限制和日志配置。但要注意,Helm 版本和 Operator 版本要匹配,我曾经因为用了最新的 Helm 3.12 搭配旧版 Operator,结果 CRD 注册失败。
安装命令如下:
helm repo add flink-operator https://downloads.apache.org/flink/flink-kubernetes-operator-1.8.0/
helm install flink-kubernetes-operator flink-operator/flink-kubernetes-operator
安装完成后,检查 Pod 状态:
kubectl get pods -n flink
NAME READY STATUS RESTARTS AGE
flink-kubernetes-operator-7d8f9c6b9c-abc12 1/1 Running 0 2m
看到 Running 就说明 Operator 已经就绪了。
4.3 配置文件编写:FlinkDeployment 才是核心
Flink Operator 的核心资源是 FlinkDeployment。你想想看,以前我们部署 Flink 集群,要写 Deployment、Service、ConfigMap、Ingress……现在只需要一个 YAML 文件。
下面是一个典型的配置文件:
apiVersion: flink.apache.org/v1beta1
kind: FlinkDeployment
metadata:
name: my-flink-job
spec:
image: flink:1.17.1
flinkVersion: v1_17
serviceAccount: flink
jobManager:
resource:
memory: "2048m"
cpu: 1
taskManager:
resource:
memory: "4096m"
cpu: 2
job:
jarURI: local:///opt/flink/examples/streaming/WordCount.jar
parallelism: 4
upgradeMode: stateless
这里有几个关键点:
- image:Flink 镜像版本,要和 flinkVersion 对应。我踩过坑,镜像用 1.17.0,但 flinkVersion 写 v1_16,结果 JobManager 启动失败。
- upgradeMode:升级模式,有
stateless、savepoint、last-state三种。生产环境我建议用savepoint,保证状态不丢。 - job.parallelism:并行度,不要超过 TaskManager 的总 slot 数。
upgradeMode 设置为 savepoint,必须配置 spec.job.savepointTriggerNonce 来触发新的 savepoint,否则升级时不会自动保存状态。
4.4 滚动升级:零停机才是真本事
滚动升级是 Flink Operator 最实用的功能之一。你只需要修改 FlinkDeployment 的配置,然后 kubectl apply,Operator 就会自动完成升级。
升级流程大致如下:
- Operator 检测到配置变更。
- 如果
upgradeMode是savepoint,先触发 savepoint。 - 停止旧的 JobManager 和 TaskManager。
- 启动新的 JobManager,从 savepoint 恢复。
- 启动新的 TaskManager,开始消费数据。
我曾经在生产环境做过一次升级,从 Flink 1.16 升级到 1.17,整个过程大概 3 分钟,业务没有感知。但要注意,如果状态量很大(比如几百 GB),savepoint 的时间会很长,建议提前规划好维护窗口。
下面是一个升级示例:
# 修改镜像版本
kubectl patch flinkdeployment my-flink-job --type='json' \
-p='[{"op": "replace", "path": "/spec/image", "value": "flink:1.17.2"}]'
或者直接编辑 YAML:
kubectl edit flinkdeployment my-flink-job
升级过程中,可以用 kubectl describe flinkdeployment my-flink-job 查看状态:
Status:
Job Status:
State: RUNNING
Upgrade History:
- Mode: SAVEPOINT
Savepoint: s3://my-bucket/savepoints/savepoint-123456
State: SUCCEEDED
Time: 2024-03-15T10:30:00Z
看到 State: SUCCEEDED,就说明升级成功了。
4.5 避坑指南:我踩过的三个坑
- 坑一:镜像拉取策略。默认是
IfNotPresent,如果你改了镜像 tag 但没改版本号(比如都用latest),Pod 不会拉取新镜像。我建议显式设置imagePullPolicy: Always。 - 坑二:资源限制。TaskManager 的 memory 如果设置得太小,Flink 会频繁 GC 甚至 OOM。我一般给 TaskManager 留 20% 的余量,比如实际需要 3GB,我就设 4GB。
- 坑三:网络插件冲突。如果 K8s 集群用了 Calico,Flink Operator 的 Pod 之间可能无法通信。解决办法是检查 Calico 的
IPIP模式是否开启,如果开启,改成VXLAN模式。
4.6 本章小结
这一章我们走完了 Flink on Kubernetes 的完整流程:从 K8s 集群搭建,到 Flink Operator 部署,再到配置文件编写和滚动升级。说白了,Flink Operator 把原来需要手动管理的复杂操作都自动化了,你只需要关注业务逻辑和配置。
嗯,这里要提醒一句:生产环境一定要先做压力测试,别直接上大流量。我见过有人没做压测,结果升级后 TaskManager 资源不够,整个集群挂了。