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 控制器来提供外部访问和负载均衡。SchedulerAirflow 的大脑负责解析 DAG 文件、调度任务、触发执行。这是最关键的组件Chart 同样使用 Deployment 来部署。生产环境中强烈建议将其副本数设置为 1以避免多个 Scheduler 实例之间产生任务竞争导致重复执行。它的高可用需要通过 K8s 本身来保证即 Pod 故障后重启。Worker当使用CeleryExecutor时任务的实际执行者。Chart 会创建一个或多个 Worker Deployment。这是水平扩展的核心你可以根据任务队列的压力手动或通过 HPAHorizontal Pod Autoscaler动态调整 Worker 的数量。每个 Worker Pod 内部会运行多个 Celery 进程。TriggererAirflow 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 和 VariablesChart 支持通过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: postgresqlpsycopg2://airflow_user:${DATABASE_PASSWORD}prod-postgres.example.com:5432/airflow_db AIRFLOW__CELERY__RESULT_BACKEND: dbpostgresql://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. 外部数据库与RedisCelery 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 配置启用Ingressweb: 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: 1000m4. Scheduler 配置scheduler: replicaCount: 1 # 重要Scheduler必须为单实例 # 启用健康检查和存活探针 livenessProbe: enabled: true readinessProbe: enabled: true # 资源通常需要比Webserver更多因为它负责解析所有DAG resources: requests: memory: 2Gi cpu: 1000m limits: memory: 4Gi cpu: 2000m5. 定义 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-literalpasswordYourStrongDatabasePassword123! kubectl create secret generic airflow-external-redis-secret \ --namespace airflow-namespace \ --from-literalpasswordYourRedisPassword3.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、SchedulerPod 中启动一个 sidecar 容器定期拉取代码。这种方式简单直接版本可控。持久化卷挂载将 DAG 目录挂载到一个共享的持久化卷如 NFS、CephFS、云存储卷上。所有组件都读写同一个目录。需要注意文件锁和并发读写问题。镜像打包将 DAG 直接打包到 Airflow 镜像里。这种方式部署简单但更新 DAG 需要重新构建和部署镜像不够灵活适合 DAG 变动不频繁的场景。日志持久化是必须项默认配置下日志写在 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_heartbeatScheduler 的心跳用于判断其是否存活。executor_open_slots/executor_queued_tasksExecutor 的开放槽位和排队任务数用于判断 Worker 是否饱和。dag_processing_total_run_secondsDAG 文件解析耗时如果持续过高可能 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 Accountairflow-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。解决统一 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正是帮你实现这一目标的重要伙伴。