别再手写运维脚本了:Operator 才是数据平台的“自动驾驶系统”
说句可能有点扎心的话:
很多团队嘴上喊着“数据平台自动化”,实际上干的还是“高级手工运维”。
每天不是在改 YAML,就是在补脚本;
不是在重启任务,就是在修集群状态;
甚至一出问题,全靠“某位老哥的肌肉记忆”。
这不是自动化,这是“人肉控制系统”。
今天我们聊点实在的——基于 Operator 的数据平台自动化运维实践,以及为什么它可能是你从“运维打工人”进化为“系统设计者”的分水岭。
一、Operator 的本质:不是工具,是“认知升级”
很多人第一次接触 Operator,会觉得它不过是 Kubernetes 上的一个扩展控制器。
但我更喜欢一个更接地气的理解:
Operator = 把运维经验写成代码,让系统自己干活
你平时怎么运维一个 Flink/Spark 集群?
- 部署集群
- 检查状态
- 失败重启
- 扩容缩容
- 滚动升级
这些事情,本质上就是一套“规则 + 状态判断 + 动作执行”。
而 Operator 做的事情就是:
👉 监听状态 → 对比期望 → 自动调节
这其实就是一个典型的控制循环(control loop)。
二、没有 Operator 的世界:一地鸡毛
我们先看一个典型“传统运维”的场景:
# 手动部署 Spark
kubectl apply -f spark-cluster.yaml
# 出问题了
kubectl get pods | grep Error
# 手动重启
kubectl delete pod xxx
# 扩容
kubectl edit deployment spark-worker
问题在哪?
- 没有“期望状态”的概念
- 操作不可复用
- 容易出错
- 人就是单点故障
说白了:系统没有“自愈能力”
三、Operator 模型:让系统自己“活过来”
我们来看看 Operator 的核心逻辑(简化版):
while True:
desired_state = get_cr_spec() # 用户定义的期望状态
current_state = get_cluster_state() # 当前集群状态
if desired_state != current_state:
reconcile(desired_state, current_state)
sleep(5)
这段代码看似简单,但威力巨大:
👉 运维变成了“声明式”
你只需要说:
我要一个 3 节点的 Flink 集群
而不是:
创建 Pod → 配置 → 检查 → 修复 → 扩容...
四、实战:写一个简单的数据平台 Operator
我们用 Python(基于 Kopf 框架)写一个极简 Operator。
1. 定义 CRD(自定义资源)
apiVersion: data.platform/v1
kind: FlinkCluster
metadata:
name: demo-cluster
spec:
replicas: 3
image: flink:1.17
2. 编写 Operator
import kopf
from kubernetes import client, config
config.load_incluster_config()
@kopf.on.create('data.platform', 'v1', 'flinkclusters')
def create_fn(spec, name, namespace, **kwargs):
replicas = spec.get('replicas', 1)
image = spec.get('image')
apps_v1 = client.AppsV1Api()
deployment = client.V1Deployment(
metadata=client.V1ObjectMeta(name=name),
spec=client.V1DeploymentSpec(
replicas=replicas,
selector={
'matchLabels': {
'app': name}},
template=client.V1PodTemplateSpec(
metadata=client.V1ObjectMeta(labels={
'app': name}),
spec=client.V1PodSpec(
containers=[
client.V1Container(
name='flink',
image=image
)
]
)
)
)
)
apps_v1.create_namespaced_deployment(namespace, deployment)
3. 自动扩缩容(Reconcile)
@kopf.on.update('data.platform', 'v1', 'flinkclusters')
def update_fn(spec, name, namespace, **kwargs):
replicas = spec.get('replicas', 1)
apps_v1 = client.AppsV1Api()
apps_v1.patch_namespaced_deployment(
name=name,
namespace=namespace,
body={
'spec': {
'replicas': replicas}}
)
五、这套机制到底改变了什么?
很多人写完第一个 Operator,会有一个顿悟:
原来运维可以“抽象”成一个系统问题
我总结 Operator 带来的三个本质变化:
1. 从“执行命令” → “定义状态”
以前:
kubectl scale --replicas=5
现在:
spec:
replicas: 5
👉 操作变成声明
2. 从“人盯系统” → “系统盯系统”
Operator 会持续做:
- 状态检查
- 自动修复
- 生命周期管理
👉 这就是“自愈系统”
3. 从“经验依赖” → “知识固化”
以前:
这个问题只有老王会修
现在:
修复逻辑已经写进 Operator 了
👉 组织能力被代码化
六、真实场景:数据平台为什么必须用 Operator?
在数据平台里,有几个典型痛点:
1. 作业生命周期复杂(Flink / Spark)
- 启动慢
- 状态多
- checkpoint 复杂
👉 Operator 可以自动处理恢复逻辑
2. 资源调度不稳定
- 高峰期爆掉
- 低谷浪费
👉 Operator + HPA = 自动弹性
3. 升级风险极高
- 手动升级容易翻车
- 回滚困难
👉 Operator 可以实现:
def rolling_update():
upgrade_one_pod()
wait_until_healthy()
七、我踩过的坑(说点真实的)
讲点实话,不然你看完觉得太美好了。
坑 1:Operator 写复杂了 = 另一个系统
很多人一上来就:
- 状态机
- 多层抽象
- 各种 CRD
结果:
👉 Operator 本身变成最难维护的系统
建议:
从一个最小闭环开始(部署 + 重启)
坑 2:忽略幂等性
Operator 的核心是:
Reconcile 必须可重复执行
错误写法:
create_resource()
正确写法:
if not exists():
create_resource()
坑 3:没有“可观测性”
Operator 出问题时最难受的不是 bug,而是:
👉 你根本不知道它在干嘛
建议:
- 打日志
- 暴露 metrics
- 加事件记录
八、最后说点人话:Operator 不是银弹,但它是方向
很多人问我:
Operator 值不值得学?
我的答案很直接:
👉 如果你还在写运维脚本,那你已经落后了
但也别走极端:
- 小团队 → 不要过度设计
- 简单系统 → 不一定要 Operator
九、总结一句话
Operator 的本质,不是自动化,而是“把运维这件事,变成一门可编程的工程学”
当你开始用 Operator 思考问题时,你会发现:
- 你不再是“修系统的人”
- 而是在“设计系统如何自我修复的人”
这就是分水岭。