数据平台上线只是开始。进入“Day 2”之后,补丁、升级、容量规划、依赖冲突和故障响应会持续消耗团队精力。Checkout.com 将自托管 Apache Airflow 迁移到 Google Cloud 的 Managed Service for Apache Airflow(Gen 3,Cloud Composer 3)后,把编排器维护转交给托管平台,并在可靠性、成本和开发效率上获得了可量化的改善。
自托管 Airflow 的成本不只体现在服务器账单上
Checkout.com 原先在另一家云服务商上自行管理 Airflow。环境可以工作,但平台团队必须持续处理服务器管理、补丁、版本升级和事故响应。高负载期间的稳定性问题尤其明显,而 Python 包及其兼容关系也需要人工反复验证。
这种模式还带来了几项直接影响开发者体验的问题:
- DAG 部署到 S3 后,大约需要六分钟才能同步到调度器,拉长了修改与验证之间的反馈周期。
- Worker 按峰值容量预留,即使负载下降,团队仍要为闲置资源付费。
- 扩容需要人工干预,新团队接入 dbt 时还要由平台团队手工创建 Secret 和 Airflow Variable。
- 一个行为异常或消耗过多资源的 DAG,可能影响整个 Airflow 环境。
- 不同 dbt 版本依赖不同的 Python 环境,升级和共存越来越困难。
因此,真正昂贵的并非某一台服务器,而是平台团队被切碎的工程时间,以及业务团队等待平台操作的交付延迟。
动态扩缩容改变了容量与成本模型
迁移前,Checkout.com 按峰值任务量配置 Worker。这个方案能承受流量高峰,却意味着系统全天维持最高容量。Managed Airflow Gen 3 根据工作负载动态调整 Worker:任务增加时扩容,队列回落后缩容。
从固定预留切换到动态伸缩后,Checkout.com 估算月度成本降低了约 30%。这个数字不应直接套用到其他平台,因为实际收益取决于任务峰谷差、任务持续时间、并发配置和底层资源价格。不过,它揭示了一个重要判断标准:负载越呈现明显的周期性或突发性,动态伸缩越可能优于长期持有峰值容量。
迁移评估时,不要只比较环境的标价,还应记录以下指标:
- 每小时运行任务数与队列长度;
- Worker CPU、内存的峰值和中位数;
- 调度延迟与任务启动时间;
- 平台工程师处理升级、扩容和事故的工时;
- 空闲容量占总容量的比例。
这些数据能帮助团队判断成本下降来自资源利用率改善,还是仅仅来自计费方式变化。
隔离、可观测性与更短的反馈周期
可靠性是此次迁移的主要驱动力。根据案例描述,Gen 3 提供的 DAG 隔离能力让 DAG 在独立执行环境中运行,因此单个 DAG 失败或消耗过多资源时,不再轻易拖累整个环境。补丁和升级则由 Google Cloud 在计划维护窗口中处理,团队无需自行组织完整的升级流程。
Cloud Monitoring 和 Cloud Logging 的集成也改变了故障处理方式。数据工程师可以直接查看任务执行信息并自行定位问题,减少对平台团队的升级求助。对于复杂故障,Checkout.com 还可以从 Google Cloud 控制台中的 Airflow DAG 界面发起 Gemini Cloud Assist 调查。它会针对不同故障假设生成评分,并同时列出支持与反对证据,帮助工程师缩短恢复时间。但 AI 诊断仍应被视为排障辅助,涉及数据正确性、权限或生产变更时,最终判断仍需由工程师完成。
开发流程也随之缩短:DAG 改用 Cloud Storage 后可以近乎即时同步;新增角色或更新 Python 包时,不必再重新部署整个 Airflow 环境;团队接入时也减少了预先创建变量的人工步骤。
可以这样实践:同步 DAG,并用容器隔离 dbt
下面是一个可改造的最小实践。假设 Cloud Composer 环境已经创建,并且本机安装、认证了 gcloud。先设置自己的项目、区域和环境名称,再查询 DAG 存储桶并同步代码:
set -euo pipefail
PROJECT_ID="your-project-id"
REGION="us-central1"
ENVIRONMENT="your-composer-environment"
DAGS_PREFIX=$(gcloud composer environments describe "$ENVIRONMENT" \
--project "$PROJECT_ID" \
--location "$REGION" \
--format='value(config.dagGcsPrefix)')
echo "Syncing DAGs to: $DAGS_PREFIX"
gcloud storage rsync ./dags "$DAGS_PREFIX" \
--recursive \
--delete-unmatched-destination-objects
运行前需要将三个变量替换为实际值,并确保当前身份拥有读取 Composer 环境及写入对应 Cloud Storage 路径的权限。--delete-unmatched-destination-objects 会删除目标端多余文件,生产流水线使用前应先确认仓库中的 dags/ 是完整发布源;不需要镜像同步时可以移除该参数。
针对 dbt,案例中的关键变化是从手工维护多个虚拟环境转向容器化执行。下面的 DAG 展示一种可行的实现方式。它属于实践示例,镜像地址、Kubernetes 连接、Service Account 和 dbt 项目路径需要按实际环境调整:
from datetime import datetime
from airflow import DAG
from airflow.providers.cncf.kubernetes.operators.pod import KubernetesPodOperator
with DAG(
dag_id="dbt_daily_orders",
start_date=datetime(2024, 1, 1),
schedule="0 2 * * *",
catchup=False,
tags=["dbt", "orders"],
) as dag:
run_dbt = KubernetesPodOperator(
task_id="run_dbt",
name="dbt-daily-orders",
namespace="default",
image="us-central1-docker.pkg.dev/your-project/data/dbt:1.8.0",
cmds=["dbt"],
arguments=[
"build",
"--project-dir",
"/workspace/dbt",
"--profiles-dir",
"/workspace/dbt",
"--select",
"tag:daily_orders",
],
env_vars={
"DBT_TARGET": "prod",
},
get_logs=True,
is_delete_operator_pod=True,
)
这种方式将 dbt 版本、适配器和 Python 依赖固化在镜像中。不同团队可以使用不同镜像标签,而不必把所有依赖塞进 Airflow 调度器环境。生产环境还需要通过 Secret Manager、Workload Identity 或平台批准的机制注入数据库凭据,不能把密码写入 DAG 或镜像。
迁移不能只做一次“搬家”
托管 Airflow 可以减少基础设施工作,但不会自动修复设计不良的 DAG、失控的并发参数或缺少幂等性的任务。采用前应完成一轮清点:
- 盘点所有 DAG、插件、Provider 和 Python 依赖,验证目标 Airflow 版本的兼容性。
- 为关键 DAG 建立成功率、调度延迟、运行时长和数据新鲜度基线。
- 将 dbt 等依赖复杂的工作负载容器化,缩小 Airflow 环境本身的依赖面。
- 验证网络、IAM、Secret、日志留存和跨云数据传输成本。
- 先迁移低风险 DAG,再逐步扩大范围,并为切换阶段保留回退方案。
- 迁移后重新调整并发、重试和资源请求,避免沿用自托管环境中的峰值配置。
Checkout.com 的经验表明,编排平台现代化的价值不只是少维护几台服务器。更重要的是改变责任边界:托管服务负责基础设施、升级和弹性,平台团队则把时间投入数据管道、治理和开发者工具。只有把成本、隔离能力、依赖管理和团队工作流一起纳入评估,这类迁移才会从基础设施替换变成真正的平台升级。