Airflow Helm Chart:Kubernetes 上部署 Apache Airflow 的生产级实践指南
1. 从零到一:为什么选择 Airflow Helm Chart?
如果你正在 Kubernetes 上寻找一个稳定、可扩展的 Apache Airflow 部署方案,并且已经被官方 Helm Chart 的复杂性劝退过,那么你找对地方了。今天要聊的这个airflow-helm/charts项目,可以说是社区里“用脚投票”选出来的事实标准。我最早接触它是在 2019 年,当时团队需要一个能快速上生产、后续又好维护的 Airflow 部署方案,在对比了官方 Chart 和几个社区版本后,最终选择了它。几年用下来,从测试环境到支撑日均数千个 DAG 任务的生产集群,它都表现得相当稳健。
简单来说,airflow-helm/charts就是一个用 Helm 来打包和部署 Apache Airflow 到 Kubernetes 集群的“配方”。它的核心价值在于,把 Airflow 这个由多个组件(Web Server、Scheduler、Worker 等)构成的复杂系统,以及它与 K8s 的深度集成(如 Kubernetes Executor),通过一套高度可配置的 Helm Values 文件抽象出来。你不需要再去手动编写一堆繁琐的 K8s Deployment、Service、ConfigMap,而是通过修改几个关键的配置项,就能快速得到一个生产就绪的 Airflow 环境。这对于运维团队和开发团队来说,极大地降低了部署和管理的认知负担与操作成本。
这个项目最初诞生于 2017 年,正是 K8s 和 Airflow 开始在企业中普及的时期。它精准地解决了当时的一个痛点:官方提供的 Helm Chart 更偏向于展示基础功能,在安全、高可用、生产级配置等方面需要大量二次开发。而这个社区 Chart 从一开始就带着强烈的生产视角,内置了诸如 Pod 安全策略(现为 Pod Security Standards)、资源管理、网络策略、数据库连接池、日志持久化等企业级特性。经过这么多年的迭代和数千家公司的实战检验,它的可靠性和成熟度是毋庸置疑的。无论你是想快速搭建一个开发测试环境,还是规划一个需要弹性伸缩、高可用的生产系统,这个 Chart 都能提供一个坚实的起点。
2. 核心架构与设计哲学解析
2.1 组件部署模型:清晰的责任分离
这个 Helm Chart 对 Airflow 的架构理解得非常透彻,并将其清晰地映射到 Kubernetes 的各个工作负载上。部署完成后,你通常会看到以下几个核心组件:
- Webserver:作为用户交互的入口,运行 Airflow 的 Web 界面。Chart 会为其创建一个 Deployment 和 Service。在高可用场景下,你可以轻松地通过
replicaCount参数扩展多个副本,前面通常搭配一个 Ingress 控制器来提供外部访问和负载均衡。 - Scheduler:Airflow 的大脑,负责解析 DAG 文件、调度任务、触发执行。这是最关键的组件,Chart 同样使用 Deployment 来部署。生产环境中,强烈建议将其副本数设置为 1,以避免多个 Scheduler 实例之间产生任务竞争导致重复执行。它的高可用需要通过 K8s 本身来保证(即 Pod 故障后重启)。
- Worker(当使用
CeleryExecutor时):任务的实际执行者。Chart 会创建一个或多个 Worker Deployment。这是水平扩展的核心,你可以根据任务队列的压力,手动或通过 HPA(Horizontal Pod Autoscaler)动态调整 Worker 的数量。每个 Worker Pod 内部会运行多个 Celery 进程。 - Triggerer(Airflow 2.2+):用于运行延迟的触发器,是支持 Deferrable Operators 的关键组件。在 Chart 中也是一个独立的 Deployment。
- PostgreSQL / Redis:作为元数据库和消息队列(Celery Broker)。Chart 提供了使用内置 Chart 依赖(如
bitnami/postgresql)或连接外部数据库的灵活选项。对于生产环境,我个人的经验是务必使用外部的、有专人维护的数据库和 Redis 服务,而不是部署在集群内的。这关系到元数据的安全和服务的稳定性。 - Flower(可选):Celery 任务队列的监控工具。Chart 可以一键部署,方便你查看任务执行状态和 Worker 状态。
所有这些组件通过精心设计的 ConfigMap 和环境变量共享配置,特别是airflow.cfg的核心参数,都被抽象到了values.yaml文件中。这种设计使得配置管理变得集中且版本化。
2.2 与 Kubernetes 的深度集成:不止于部署
这个 Chart 的另一个精髓在于它对 Kubernetes 原生特性的深度利用,这远不止是“把 Airflow 放进容器里”那么简单。
Kubernetes Executor 开箱即用:这是将 Airflow 与 K8s 能力结合最紧密的模式。Chart 完美支持了这种模式。在这种模式下,每一个 Airflow Task 都会动态地在一个独立的、崭新的 Kubernetes Pod 中执行。这意味着:
- 资源隔离:任务之间完全隔离,一个任务的内存泄漏不会拖垮其他任务或核心组件。
- 弹性资源:可以为不同的 DAG 或 Task 定义不同的 CPU/内存请求和限制,资源利用更精细。
- 环境纯净:每个任务 Pod 都从指定镜像启动,避免了长期运行的 Worker 环境被污染的问题。
- Chart 提供了
kube.worker等配置段,让你能全局或按需定义这些任务 Pod 的模板,包括镜像、资源、节点选择器、容忍度等。
安全的秘密管理:连接数据库、外部 API 所需的密码、密钥等敏感信息,绝不会硬编码在
values.yaml或镜像里。Chart 鼓励并提供了与 Kubernetes Secrets 集成的标准方式。你可以通过env和secretEnv配置,将 Secret 中的键值对作为环境变量注入到任意的 Airflow 组件容器中。更进一步,对于 Airflow Connections 和 Variables,Chart 支持通过airflow.extraEnv来设置AIRFLOW__SECRETS__BACKEND,从而使用 K8s Secret Backend,实现连接信息的动态、安全拉取。可观测性内置:生产系统离不开监控。Chart 为每个组件都预设了合理的存活探针(Liveness Probe)和就绪探针(Readiness Probe),确保不健康的 Pod 能被及时重启或从服务端点中剔除。同时,它也将各个组件的日志标准输出(stdout/stderr),方便你通过集群的日志收集方案(如 EFK、Loki)进行统一采集。对于指标,Airflow 本身暴露了 Prometheus 格式的指标,你可以通过配置 ServiceMonitor(如果使用 Prometheus Operator)来轻松抓取。
2.3 配置哲学:约定优于配置,同时提供无限灵活性
airflow-helm/charts的values.yaml文件虽然庞大(超过 2000 行),但它的结构非常清晰。其设计哲学是:为绝大多数常见场景提供合理的默认值(“约定”),同时为任何特殊需求敞开所有可能的配置入口(“灵活性”)。
例如,默认情况下,它会使用一个轻量级的 Airflow 镜像,并配置好基础的环境变量。但如果你需要自定义镜像,安装额外的 Python 包(pip install),或者挂载特定的配置文件、Volume,都可以在images.*.repository/tag、airflow.extraPipPackages、airflow.config、extraVolumes和extraVolumeMounts等字段中轻松实现。
这种设计使得这个 Chart 既能“开箱即用”,快速搭建一个可用环境,又能经得起复杂生产环境的考验,满足各种定制化需求。你需要做的,就是根据你的实际场景,去覆盖那些默认的 Values。
3. 实战部署:手把手搭建生产就绪的 Airflow
理论说了这么多,我们来点实际的。下面我将以一个基于Kubernetes Executor和外部 PostgreSQL/Redis的生产倾向配置为例,带你走一遍部署流程。假设你已经有一个运行中的 K8s 集群,并安装了 Helm。
3.1 前期准备与价值观定制
首先,添加仓库并获取 Chart 的默认配置,这是了解所有可配置项的最佳方式:
helm repo add airflow-helm https://airflow-helm.github.io/charts helm repo update # 拉取最新的 Chart 到本地,方便查看和定制 helm pull airflow-helm/airflow --untar --version 8.10.0 # 建议指定一个稳定版本 cd airflow # 最重要的步骤:将默认 values.yaml 复制出来,作为我们定制的基础 cp values.yaml my-production-values.yaml现在,打开my-production-values.yaml,我们将进行关键修改。以下是我根据经验总结的几个核心配置区块:
1. 镜像与基础配置:
images: airflow: repository: your-registry.example.com/apache/airflow # 建议使用私有仓库中的镜像 tag: 2.8.1-python3.10 # 指定明确的版本,避免使用 latest pullPolicy: IfNotPresent airflow: executor: KubernetesExecutor # 使用K8s Executor config: AIRFLOW__DATABASE__SQL_ALCHEMY_CONN: postgresql+psycopg2://airflow_user:${DATABASE_PASSWORD}@prod-postgres.example.com:5432/airflow_db AIRFLOW__CELERY__RESULT_BACKEND: db+postgresql://airflow_user:${DATABASE_PASSWORD}@prod-postgres.example.com:5432/airflow_db # 核心:定义K8s命名空间和Worker模板 AIRFLOW__KUBERNETES__NAMESPACE: airflow-namespace AIRFLOW__KUBERNETES__WORKER_CONTAINER_REPOSITORY: your-registry.example.com/apache/airflow AIRFLOW__KUBERNETES__WORKER_CONTAINER_TAG: 2.8.1-python3.10 AIRFLOW__KUBERNETES__DELETE_WORKER_PODS: "True" # 任务完成后删除Pod,节省资源 AIRFLOW__LOGGING__REMOTE_LOGGING: "True" AIRFLOW__LOGGING__REMOTE_BASE_LOG_FOLDER: s3://your-airflow-logs-bucket/logs # 日志持久化到S3或类似对象存储注意:数据库连接字符串中的密码应使用占位符,实际值通过 K8s Secret 注入。
AIRFLOW__KUBERNETES__NAMESPACE必须设置为 Airflow 组件所在的命名空间,否则任务 Pod 会创建在default命名空间。
2. 外部数据库与Redis(Celery Broker):我们选择不启用 Chart 内嵌的数据库,而是连接外部服务。
externalDatabase: type: postgres host: prod-postgres.example.com port: 5432 database: airflow_db user: airflow_user passwordSecret: "airflow-external-db-secret" # 指向一个已创建的K8s Secret passwordSecretKey: "password" externalRedis: host: prod-redis.example.com port: 6379 passwordSecret: "airflow-external-redis-secret" passwordSecretKey: "password" # 如果Redis有多个数据库,指定一个,通常为0 databaseNumber: 03. Web Server 配置(启用Ingress):
web: replicaCount: 2 # 至少2个副本以实现高可用 service: type: ClusterIP ingress: enabled: true className: "nginx" # 根据你的Ingress Controller类型修改 hosts: - host: airflow.yourcompany.com paths: - path: / pathType: Prefix tls: [] # 资源请求与限制,根据实际负载调整 resources: requests: memory: "1Gi" cpu: "500m" limits: memory: "2Gi" cpu: "1000m"4. Scheduler 配置:
scheduler: replicaCount: 1 # 重要:Scheduler必须为单实例 # 启用健康检查和存活探针 livenessProbe: enabled: true readinessProbe: enabled: true # 资源通常需要比Webserver更多,因为它负责解析所有DAG resources: requests: memory: "2Gi" cpu: "1000m" limits: memory: "4Gi" cpu: "2000m"5. 定义 Kubernetes Executor 的任务 Pod 模板:这是最强大的部分,你可以在这里定义每个任务 Pod 的规格。
kube: worker: # 任务Pod使用的镜像,可以和主镜像不同 repository: your-registry.example.com/apache/airflow tag: 2.8.1-python3.10 # 为任务Pod设置资源限制,防止单个任务耗尽节点资源 resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" # 可以指定节点选择器或容忍度,将任务调度到特定节点 nodeSelector: {} tolerations: [] # 可以挂载额外的卷,例如用于访问共享代码或配置文件 extraVolumes: [] extraVolumeMounts: []6. 创建必要的 Kubernetes Secrets:在部署前,我们需要先创建包含数据库密码的 Secret。
kubectl create namespace airflow-namespace kubectl create secret generic airflow-external-db-secret \ --namespace airflow-namespace \ --from-literal=password='YourStrongDatabasePassword123!' kubectl create secret generic airflow-external-redis-secret \ --namespace airflow-namespace \ --from-literal=password='YourRedisPassword'3.2 执行部署与验证
配置完成后,使用 Helm 进行安装或升级:
# 首次安装 helm upgrade --install airflow airflow-helm/airflow \ --namespace airflow-namespace \ --version 8.10.0 \ -f my-production-values.yaml # 或者,如果你后续修改了 values.yaml 文件,使用以下命令升级 # helm upgrade airflow airflow-helm/airflow \ # --namespace airflow-namespace \ # -f my-production-values.yaml部署完成后,观察 Pod 状态:
kubectl get pods -n airflow-namespace -w你应该看到web、scheduler等 Pod 陆续变为Running状态。
通过 Ingress 地址访问 Airflow Web UI (https://airflow.yourcompany.com),默认用户名和密码可以在values.yaml中的airflow.users部分配置,或者通过airflow.fernetKey和airflow.extraEnv配置使用更安全的身份验证方式(如 OAuth)。
3.3 关键操作心得与避坑指南
Fernet Key 必须持久化且一致:
airflow.fernetKey用于加密数据库中的连接密码等敏感信息。一旦部署后,绝对不要随意更改它,否则所有已加密的信息将无法解密。最佳实践是在首次部署前生成一个强密钥,并将其保存在安全的密码管理器中,后续所有部署和升级都使用同一个密钥。你可以通过values.yaml中的airflow.fernetKey直接设置,或者通过airflow.extraEnv从 Secret 中注入AIRFLOW__CORE__FERNET_KEY。DAG 文件的存储与同步:Chart 本身不解决 DAG 从哪里来的问题。你需要自己管理 DAG 文件。常见方案有:
- Git Sync Sidecar:这是 Chart 原生支持且推荐的方式。通过在
dags.gitSync中配置 Git 仓库信息,Chart 会在每个需要 DAG 的组件(Web、Scheduler)Pod 中启动一个 sidecar 容器,定期拉取代码。这种方式简单直接,版本可控。 - 持久化卷挂载:将 DAG 目录挂载到一个共享的持久化卷(如 NFS、CephFS、云存储卷)上。所有组件都读写同一个目录。需要注意文件锁和并发读写问题。
- 镜像打包:将 DAG 直接打包到 Airflow 镜像里。这种方式部署简单,但更新 DAG 需要重新构建和部署镜像,不够灵活,适合 DAG 变动不频繁的场景。
- Git Sync Sidecar:这是 Chart 原生支持且推荐的方式。通过在
日志持久化是必须项:默认配置下,日志写在 Pod 的本地文件系统,Pod 重启就没了。生产环境必须配置远程日志存储。如上例所示,配置
AIRFLOW__LOGGING__REMOTE_LOGGING指向 S3、GCS 或 Azure Blob Storage。这样,在 Web UI 上点击查看任务日志时,Airflow 才会从远程存储拉取。谨慎使用
helm upgrade --force:在升级 Chart 版本或修改某些关键配置(如数据库连接)时,有时 Pod 会因配置不兼容而无法启动。--force参数会强制删除并重建 Pod,但可能导致短暂的不可用。生产环境升级前,务必在测试环境验证,并考虑在维护窗口进行操作。
4. 高级配置与运维实战
4.1 实现基于 Prometheus 的监控告警
要让 Airflow 的运维可视化,集成 Prometheus 是第一步。Airflow 的各个组件都暴露了/metrics端点。
首先,在values.yaml中启用指标:
metrics: enabled: true serviceMonitor: enabled: true # 根据你的 Prometheus Operator 配置调整 namespace: monitoring interval: 30s scrapeTimeout: 10s这会在 Service 上添加prometheus.io/scrape: "true"注解,并创建一个 ServiceMonitor CRD(如果启用了 Prometheus Operator)。
接下来,你需要关注一些核心指标:
scheduler_heartbeat:Scheduler 的心跳,用于判断其是否存活。executor_open_slots/executor_queued_tasks:Executor 的开放槽位和排队任务数,用于判断 Worker 是否饱和。dag_processing_total_run_seconds:DAG 文件解析耗时,如果持续过高,可能 DAG 文件太多或太复杂。scheduler_manager_heartbeat和dag_file_processor_manager_heartbeat:相关管理进程的健康状态。
你可以基于这些指标在 Grafana 中创建仪表盘,并设置告警规则,例如:Scheduler 心跳丢失超过 5 分钟,或排队任务数持续超过某个阈值时触发告警。
4.2 配置自动伸缩(HPA)
对于使用CeleryExecutor的场景,Worker 的数量可以根据任务队列长度动态调整。你需要先部署metrics-server。
然后,为 Worker Deployment 创建 HPA 策略。这通常不直接写在 Chart 的values.yaml里,而是作为额外的 K8s 资源部署。一个示例的 HPA 定义如下:
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: airflow-worker namespace: airflow-namespace spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: airflow-worker # 注意:Chart生成的Worker Deployment名称可能带有Release名,如 `my-release-airflow-worker` minReplicas: 2 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 - type: Pods pods: metric: name: celery_queue_length # 这是一个自定义指标,需要部署 Prometheus Adapter 来提供 target: type: AverageValue averageValue: 10 # 当平均队列长度超过10时扩容更实用的做法是基于 RabbitMQ 或 Redis 中的 Celery 队列长度来伸缩,这需要安装prometheus-adapter并将队列指标转换为 K8s 可识别的自定义指标。
4.3 数据库连接池与维护
Airflow 的 Web Server 和 Scheduler 会频繁访问元数据库。在高负载下,默认的连接设置可能成为瓶颈。你可以在airflow.config中调整数据库连接池设置:
airflow: config: AIRFLOW__DATABASE__SQL_ALCHEMY_POOL_SIZE: 10 AIRFLOW__DATABASE__SQL_ALCHEMY_MAX_OVERFLOW: 20 AIRFLOW__DATABASE__SQL_ALCHEMY_POOL_RECYCLE: 1800 # 单位秒,建议小于数据库的wait_timeoutPOOL_SIZE是保持打开的连接数,MAX_OVERFLOW是允许超过池大小的临时连接数。POOL_RECYCLE至关重要,它定期回收连接,避免数据库端因连接空闲时间过长而断开导致的“MySQL server has gone away”错误。这个值应设置得略小于你的数据库服务器的wait_timeout参数。
4.4 网络策略与安全加固
在安全要求高的集群中,你需要限制 Pod 间的网络访问。Chart 支持创建 NetworkPolicy。例如,只允许 Web Server 被 Ingress 访问,Scheduler 和 Worker 只能与 Redis、PostgreSQL 通信:
networkPolicy: enabled: true ingress: - from: - podSelector: matchLabels: app.kubernetes.io/name: airflow app.kubernetes.io/component: webserver ports: - port: 8080 # 允许来自Ingress Controller的流量(根据实际标签调整) - from: - namespaceSelector: matchLabels: name: ingress-nginx - podSelector: matchLabels: app.kubernetes.io/name: controller ports: - port: 8080同时,务必遵循 Pod 安全标准,在values.yaml中配置安全上下文,例如禁止以 root 用户运行:
securityContext: runAsUser: 50000 fsGroup: 500005. 常见问题排查与修复实录
即使配置再完善,在生产中也会遇到问题。下面是我和团队在运维中遇到的一些典型问题及解决方案。
5.1 Scheduler 不断重启或无法启动
- 症状:Scheduler Pod 处于
CrashLoopBackOff状态,查看日志显示数据库连接错误或导入模块错误。 - 排查:
kubectl logs -n airflow-namespace <scheduler-pod-name> --previous查看上一次崩溃的日志。- 检查数据库连接字符串是否正确,网络是否连通,密码 Secret 是否存在且键名正确。
- 检查
airflow.fernetKey是否与数据库中已有的密钥一致。 - 检查自定义的
airflow.config或airflow.extraEnv中是否有语法错误或拼写错误。
- 解决:修正数据库连接配置或 Fernet Key。确保所有配置项名称以
AIRFLOW__开头,并使用双下划线__分隔章节和键名(如AIRFLOW__CORE__SQL_ALCHEMY_CONN)。
5.2 Web UI 可以访问,但任务一直处于“排队中”状态
- 症状:使用
KubernetesExecutor时,任务状态卡在queued,没有进入running。 - 排查:
- 查看 Scheduler 日志,确认它是否成功将任务提交给了 K8s API。
kubectl get events -n airflow-namespace查看是否有关于任务 Pod 创建失败的事件,常见原因是资源配额不足、节点选择器不匹配或镜像拉取失败。- 检查
AIRFLOW__KUBERNETES__NAMESPACE配置是否正确。任务 Pod 会尝试创建在这个命名空间里。 - 检查 Service Account
airflow-worker(或你自定义的)是否具有在目标命名空间创建 Pod 的 RBAC 权限。Chart 默认会创建相应的 Role 和 RoleBinding,但如果你修改了命名空间或 Service Account,需要手动调整。
- 解决:根据事件日志修正资源配置、镜像地址或 RBAC 权限。确保 Scheduler 使用的 Service Account 有足够的权限。
5.3 任务日志在 Web UI 中显示“日志文件未找到”
- 症状:点击任务实例的“日志”按钮,页面提示无法加载日志。
- 排查:
- 确认已正确配置远程日志后端(如 S3、GCS)。检查
AIRFLOW__LOGGING__REMOTE_LOGGING和相关连接配置。 - 检查运行任务的 Worker 或 Pod 的环境变量中,远程日志配置是否生效。有时 Web Server 和 Worker 的配置不一致会导致此问题。
- 登录到远程存储,查看对应的日志路径下是否有文件生成。任务日志的路径通常包含
dag_id、task_id和execution_date。
- 确认已正确配置远程日志后端(如 S3、GCS)。检查
- 解决:统一 Web、Scheduler、Worker 的远程日志配置。确保用于访问对象存储的凭据(如 AWS IAM Role、Service Account Key)已正确配置并被 Pod 获取。
5.4 DAG 在 UI 中不显示或更新延迟
- 症状:Git 仓库中的 DAG 文件已更新,但 Airflow UI 中看不到新 DAG 或更改。
- 排查:
- 检查 Scheduler 和 Webserver 的 Git Sync Sidecar 容器日志,看是否有拉取错误。
- 检查 DAG 文件是否有 Python 语法错误。Scheduler 在解析失败的 DAG 时会静默跳过,并在日志中记录错误。查看 Scheduler 日志中是否有
Broken DAG字样。 - 检查
airflow.config中的AIRFLOW__SCHEDULER__DAG_DIR_LIST_INTERVAL和AIRFLOW__SCHEDULER__MIN_FILE_PROCESS_INTERVAL设置。前者控制检查 DAG 目录的频率,后者控制处理单个 DAG 文件的最小间隔。在开发环境可以调低(如 30 秒),生产环境可以调高以减轻负载。
- 解决:修复 DAG 语法错误。如果使用 Git Sync,确保仓库地址和凭据正确。可以尝试重启 Scheduler Pod 以强制重新解析所有 DAG。
5.5 数据库连接数过多
- 症状:数据库监控显示连接数激增,接近上限,可能导致新的 Airflow 组件无法启动。
- 排查:
- 检查
airflow.config中SQL_ALCHEMY_POOL_SIZE和MAX_OVERFLOW的设置是否过高。每个 Web 和 Scheduler Pod 都会创建独立的连接池。 - 检查是否有陈旧的连接。可能是
POOL_RECYCLE设置得大于数据库的wait_timeout,导致连接池中的连接已被数据库关闭,但 Airflow 仍试图使用。 - 考虑是否部署了过多的
replicaCount(特别是 Webserver)。
- 检查
- 解决:合理设置连接池参数。确保
POOL_RECYCLE(如 1800 秒)小于数据库的wait_timeout(如 28800 秒)。对于生产环境,定期重启 Pod 也是一种强制回收连接的有效手段(可通过 K8s 的滚动更新策略实现)。
经过这样一番从原理到实践,从部署到运维的深度拆解,你应该对如何使用airflow-helm/charts这个强大的工具在 Kubernetes 上驾驭 Apache Airflow 有了全面的认识。它的价值在于提供了一套经过千锤百炼的“最佳实践模板”,让你能避开无数前人踩过的坑,快速搭建一个稳固的基石。剩下的,就是根据你具体的业务逻辑去设计和编写 DAG 了。记住,好的运维是让系统安静稳定地运行,而airflow-helm/charts正是帮你实现这一目标的重要伙伴。
