本文作者: Maples7
本文链接: http://maples7.com/2019/12/03/develop-etl-via-airflow-on-k8s/
背景介绍
稳定高效的进行数据处理几乎是如今每一家互联网公司都要面临的课题,尤其是对于专注于气象数据研究的我司而言,做数据分析和 ETL 的工作是整个公司业务很重要的一部分。在脱离了原始的「刀耕火种」的时代之后,我们内部一直在使用 Airflow 作为数据处理流程的框架来管理日常的数据流任务。其实我们也调研了很多其他的方案,最后还是选定了看起来相对比较可靠也比较符合我们业务需求的开源项目 Airflow 来做这个事情,虽然在使用的过程中确实也遇到了不少坑。当时这个项目还在 Apache Incubator,目前已经顺利毕业了。
简单介绍一下 Airflow,一般由 WebServer(一套完整的 UI 界面用于随时查看任务的执行状态并可以手动执行一些操作)、Scheduler(用来做任务的调度和管理)、Worker(真正执行任务的部分,可能有很多个)组成。这是一个常见的分布式架构,你只需要把任务流的 DAG 用 Python 代码写好,然后配置好触发条件就可以让它长期运行下去。在实际生产环境中,我们大量使用了 Celery Executor 来把任务动态分布到多个 Worker 上执行。
如果数据处理任务长期不变,这样的系统已经可以满足我们的需求了。但实际上随着越来越多的数据任务被添加到整个系统,任务负载变得越来越重,很多时候固定数量的 Worker 已经不能及时的处理完被 scheduled 的任务,造成任务队列堆积,一段时间后如果一直不能改善负载情况甚至会拖垮整个系统。这种情况下,只能手动增加更多的 Worker 来分担任务处理工作。然后不同类型的数据处理越多,Worker 所需要安装的依赖也越多,每手工增加一个 Worker 的成本也越来越高。甚至对于气象数据的处理而言,有很多非常古老的数据处理工具(很多还是 Fortran 写的),经常出现依赖相互冲突的情况(版本冲突,编译通不过等等)。如何隔离各个任务之间的运行环境,以及如何根据负载需求动态的伸缩 Worker 的数量日益成为了这个系统的一个痛点。
动态伸缩,环境隔离,自然让人联想到 Docker 和 Kubernetes 这样的技术。好在 Airflow 1.10 版本引入了 Kubernetes Executor 和 Kubernetes Operator 允许为每一个任务创建新的 Pod 来处理,而执行完之后新创建的 Pod 会被清理掉,并且每一个任务都可以指定不同的 Docker image 来处理,这样看起来就可以完全解决我们前面的问题。
目前这部分功能似乎还很不稳定,官方文档和讨论都还不多,这篇博客也是为了记录下我们的踩坑过程。
在 Kubernetes 上搭建 Airflow
我们使用 Helm 来管理在 Kubernetes 上个各个应用以及它们的依赖,目前官方也已经给出了 stable/airflow 的 Chart,我们可以直接使用或部分参考。下面我们介绍一下从零开始的搭建过程。
- 安装 Kubernetes
本地安装的可以选择 Minikube,主要它解决了跨平台的问题。我在 macOS 上使用的是 Docker Desktop for Mac 里自带的 Kubernetes 集群,如果你安装遇到了问题可以看看这里是不是解决了你的问题,或者自行 Google 也可。 - 安装 Helm
Helm 3 已经是正式的稳定版本,可惜我们线上还在使用 Helm 2,而 Helm 2 的版本不兼容是没法使用的。为了跟线上保持一致,我本地也是安装的 Helm 2,所以后面都是以 Helm 2 的操作来执行的,第一次进行的朋友可以直接用 Helm 3。当然安装过程也很可能出现问题,可以自行 Google 解决。 - 在 Kubernetes 上用 Helm 安装 Airflow Chart
直接根据官方的 Airflow Chart 步骤执行helm install --namespace "airflow" --name "airflow" stable/airflow
即可在 Kubernetes 上安装一个标准的 Airflow 集群,之后可以查看各个 Pod 的状态是不是已经 ready(一切正常的情况下会在airflow
的 Namespace 下安装 airflow-web、airflow-scheduler、airflow-postgresql、airflow-flower、airflow-redis)。还可以根据 NOTES 的提示在浏览器中查看 Airflow 的 UI 界面是不是也启动良好:
NOTES:
Congratulations. You have just deployed Apache Airflow
export POD_NAME=$(kubectl get pods --namespace airflow -l "component=web,app=airflow" -o jsonpath="{.items[0].metadata.name}")
echo http://127.0.0.1:8080
kubectl port-forward --namespace airflow $POD_NAME 8080:8080
Open Airflow in your web browser