1. 项目概述为什么选 Docker 版 Airflow 而不是源码部署或 pip 安装Airflow docker版配置、部署——这八个字背后藏着过去三年我帮二十多个团队落地调度系统时踩出的一条最稳路径。不是因为 Docker 多酷而是因为Airflow 本身就是一个对环境极度敏感的“多面体”它既要跑 Python 任务又要连数据库PostgreSQL/MySQL要启 Web Server要起 Scheduler还要调度 Celery 或 Kubernetes Executor更麻烦的是它的插件生态比如 apache-airflow-providers-postgres、apache-airflow-providers-snowflake版本稍有不匹配轻则 DAG 不加载重则整个 Web UI 启动失败报ImportError: cannot import name X from Y。我亲眼见过某金融团队在 CentOS 7 上用 pip install airflow2.7.3 python 3.9 硬刚三天最后发现是系统级 OpenSSL 版本太老导致cryptography编译失败连第一个airflow db init都卡住。Docker 版不是“偷懒方案”而是把“环境一致性”这个隐形成本从人脑里搬进docker-compose.yml文件里。官方镜像apache/airflow:2.8.3已经预装了所有主流 provider、适配好 glibc 和 OpenSSL 版本、甚至连airflow db upgrade的初始化逻辑都封装进了 entrypoint。你不需要记住“Airflow 2.8 要求 psycopg2-binary2.9.7,3.0.0”也不用担心pip install apache-airflow-providers-amazon会悄悄升级你已有的boto3到不兼容版本——这些依赖锁都在镜像层里固化了。更重要的是Docker Compose 把五六个服务webserver、scheduler、worker、triggerer、redis、postgres的启动顺序、网络互通、健康检查全写成声明式配置而不是靠systemctl start之后手动tail -f /var/log/airflow/scheduler.log去猜哪个服务先挂了。所以当你看到“Airflow docker版配置、部署”这个标题它真正指向的不是一个技术动作而是一套可复现、可审计、可回滚的生产就绪型调度基础设施交付标准。适合三类人一是刚接手遗留 Airflow 集群、想快速重建环境的运维同学二是数据平台团队要为新业务线统一提供调度能力需要开箱即用模板的架构师三是算法/BI 工程师自己搭实验环境不想被环境问题绊住脚的实战派。接下来我会完全基于真实部署场景拆解从零到上线的每一步——不讲 Docker 基础原理只告诉你哪些配置必须改、哪些参数不能动、哪些日志要看、哪些坑我替你踩过了。2. 整体架构设计与方案选型逻辑为什么是 docker-compose 而非 Kubernetes2.1 为什么不用纯 Docker run——环境协同成本太高有人问“我直接docker run -d --name airflow-webserver ...启五个容器行不行” 行但极其脆弱。举个真实案例某电商团队初期用单命令启了 webserver、scheduler、postgres结果某天 scheduler 因内存溢出 OOM 被 kill而 webserver 完全感知不到继续往数据库写状态导致 DAG 运行记录错乱排查时发现dag_run表里有 200 多条running状态却找不到对应进程。根本原因是缺乏服务编排层——没有自动重启策略、没有健康检查、没有依赖启动顺序比如必须等 postgres ready 后 scheduler 才能连。Docker Compose 就是解决这个问题的最小完备单元它用一个 YAML 文件定义服务拓扑内置depends_onhealthcheck机制让 scheduler 容器启动前自动等待 postgres 返回SELECT 1成功这才是生产可用的起点。2.2 为什么不用 Kubernetes——复杂度与收益严重不匹配K8s 当然是终极方案但对大多数中小团队它带来的管理开销远超收益。我们做过对比测试在 4C8G 的云服务器上部署一套 3 节点 K8s 集群kubeadm光是证书轮换、etcd 备份、Ingress 配置、HPA 弹性策略调试就占用了 1.5 人日而同等资源下用 docker-compose 部署 Airflow从拉镜像到 DAG 正常跑通最快 35 分钟。更关键的是Airflow 的核心瓶颈从来不在“容器编排”而在数据库连接池、Celery Broker 吞吐、Worker 并发数这三个维度。K8s 的 Pod 自动扩缩容对 Airflow 几乎无效——因为 scheduler 是单点triggerer 无法水平扩展worker 扩容后若没配好 Celery 的broker_url和result_backend新 worker 根本拿不到任务。反观 docker-compose你只需改deploy.replicas和environment里的AIRFLOW__CELERY__WORKER_CONCURRENCY再加一条command: celery worker --concurrency 8就能精准控制资源水位。这不是妥协而是把有限精力聚焦在真正影响稳定性的参数上。2.3 为什么选官方镜像而非自建——版本陷阱比想象中深官方镜像apache/airflow:version的价值远不止“省事”。它解决了三个致命问题第一Python 环境纯净性。社区常见错误是pip install airflow时顺手装了pandas2.0.0结果 Airflow 2.7 的sqlalchemy依赖冲突scheduler 启动报AttributeError: module sqlalchemy has no attribute func。官方镜像用pyenv锁死 Python 版本所有 provider 用constraints-*.txt文件精确约束依赖范围比如apache-airflow-providers-postgres5.6.0明确要求psycopg2-binary2.9.7,3.0.0。第二二进制依赖预编译。cryptography、pyarrow这类包在 Alpine Linux 上编译极慢且易失败官方镜像直接用 Debian base预装好build-essential和libpq-dev避免部署时卡在gcc failed with exit status 1。第三安全基线合规。镜像通过 Snyk 扫描定期更新 CVE 补丁比如 2023 年log4j漏洞爆发后官方在 48 小时内发布了所有维护版本的修复镜像而自建镜像团队平均响应时间是 5.7 天。所以我的建议很直接除非你有强制的私有仓库策略或需要集成特定硬件驱动如 GPU 加速的 ML 任务否则永远优先用apache/airflow:2.8.3这类带具体 patch 版本的镜像而不是latest或2.8这种浮动标签——后者可能某天凌晨自动更新把你线上集群拖进兼容性地狱。3. 核心配置解析与实操要点从 docker-compose.yml 到 airflow.cfg 的逐层穿透3.1 docker-compose.yml不只是服务定义更是环境契约下面这段配置是我在线上环境稳定运行 14 个月的精简版删掉了所有 demo 用的 test-service只保留生产必需项version: 3.8 services: postgres: image: postgres:15.4 environment: POSTGRES_USER: airflow POSTGRES_PASSWORD: airflow POSTGRES_DB: airflow volumes: - postgres-db-volume:/var/lib/postgresql/data healthcheck: test: [CMD-SHELL, pg_isready -U airflow -d airflow] interval: 30s timeout: 10s retries: 5 redis: image: redis:7.2-alpine healthcheck: test: [CMD, redis-cli, ping] interval: 30s timeout: 10s retries: 5 webserver: image: apache/airflow:2.8.3 restart: always depends_on: postgres: condition: service_healthy redis: condition: service_healthy environment: # 必须显式设置否则默认 sqlite AIRFLOW__CORE__SQL_ALCHEMY_CONN: postgresqlpsycopg2://airflow:airflowpostgres/airflow AIRFLOW__CORE__FERNET_KEY: 46BKJoQYlPPOexq0OhDZnIlNepKFf87WFwLbfzqDDho AIRFLOW__CORE__EXECUTOR: CeleryExecutor AIRFLOW__CELERY__RESULT_BACKEND: dbpostgresql://airflow:airflowpostgres/airflow AIRFLOW__CELERY__BROKER_URL: redis://:redis:6379/1 AIRFLOW__CORE__LOAD_EXAMPLES: false AIRFLOW__CORE__DAGS_ARE_PAUSED_AT_CREATION: true # 关键关闭默认的用户创建由初始化脚本控制 AIRFLOW__WEBSERVER__AUTHENTICATE: true AIRFLOW__WEBSERVER__AUTH_BACKEND: airflow.api.auth.backend.basic_auth volumes: - ./dags:/opt/airflow/dags - ./logs:/opt/airflow/logs - ./plugins:/opt/airflow/plugins - ./config/airflow.cfg:/opt/airflow/airflow.cfg ports: - 8080:8080 command: - bash -c airflow db upgrade airflow users create --username admin --password admin --firstname Admin --lastname User --role Admin --email adminexample.com airflow webserver scheduler: image: apache/airflow:2.8.3 restart: always depends_on: webserver: condition: service_started postgres: condition: service_healthy redis: condition: service_healthy environment: AIRFLOW__CORE__SQL_ALCHEMY_CONN: postgresqlpsycopg2://airflow:airflowpostgres/airflow AIRFLOW__CORE__FERNET_KEY: 46BKJoQYlPPOexq0OhDZnIlNepKFf87WFwLbfzqDDho AIRFLOW__CORE__EXECUTOR: CeleryExecutor AIRFLOW__CELERY__RESULT_BACKEND: dbpostgresql://airflow:airflowpostgres/airflow AIRFLOW__CELERY__BROKER_URL: redis://:redis:6379/1 AIRFLOW__CORE__LOAD_EXAMPLES: false AIRFLOW__CORE__DAGS_ARE_PAUSED_AT_CREATION: true volumes: - ./dags:/opt/airflow/dags - ./logs:/opt/airflow/logs - ./plugins:/opt/airflow/plugins - ./config/airflow.cfg:/opt/airflow/airflow.cfg command: airflow scheduler worker: image: apache/airflow:2.8.3 restart: always depends_on: redis: condition: service_healthy postgres: condition: service_healthy environment: AIRFLOW__CORE__SQL_ALCHEMY_CONN: postgresqlpsycopg2://airflow:airflowpostgres/airflow AIRFLOW__CORE__FERNET_KEY: 46BKJoQYlPPOexq0OhDZnIlNepKFf87WFwLbfzqDDho AIRFLOW__CORE__EXECUTOR: CeleryExecutor AIRFLOW__CELERY__RESULT_BACKEND: dbpostgresql://airflow:airflowpostgres/airflow AIRFLOW__CELERY__BROKER_URL: redis://:redis:6379/1 AIRFLOW__CORE__LOAD_EXAMPLES: false AIRFLOW__CORE__DAGS_ARE_PAUSED_AT_CREATION: true volumes: - ./dags:/opt/airflow/dags - ./logs:/opt/airflow/logs - ./plugins:/opt/airflow/plugins - ./config/airflow.cfg:/opt/airflow/airflow.cfg command: celery worker --concurrency 4 triggerer: image: apache/airflow:2.8.3 restart: always depends_on: postgres: condition: service_healthy environment: AIRFLOW__CORE__SQL_ALCHEMY_CONN: postgresqlpsycopg2://airflow:airflowpostgres/airflow AIRFLOW__CORE__FERNET_KEY: 46BKJoQYlPPOexq0OhDZnIlNepKFf87WFwLbfzqDDho AIRFLOW__CORE__EXECUTOR: CeleryExecutor AIRFLOW__CORE__LOAD_EXAMPLES: false AIRFLOW__CORE__DAGS_ARE_PAUSED_AT_CREATION: true volumes: - ./dags:/opt/airflow/dags - ./logs:/opt/airflow/logs - ./config/airflow.cfg:/opt/airflow/airflow.cfg command: airflow triggerer volumes: postgres-db-volume:提示FERNET_KEY必须全局一致且保密这是 Airflow 加密连接密码的核心密钥。生成方法在 Python 中执行from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())然后把这个值硬编码到所有服务的环境变量里。千万别用默认值或空字符串否则所有加密的 connection 密码都会失效。这里有几个极易被忽略但致命的细节healthcheck的condition: service_healthy是刚需。很多团队只写depends_on: [postgres]结果 postgres 容器启动了但数据库还没初始化完pg_isready返回 falsescheduler 就去连库疯狂重试直到触发max_connections限制。加上健康检查后docker-compose 会严格等待pg_isready成功才启动下游服务。webserver的command里必须包含airflow db upgrade。这是 Airflow 2.0 的强制迁移步骤跳过会导致alembic_version表缺失Web UI 直接 500。注意db upgrade只需在 webserver 启动时执行一次scheduler/worker 不需要重复执行。AIRFLOW__CORE__LOAD_EXAMPLES: false必须设为 false。示例 DAG 会创建大量测试表和连接污染你的生产元数据且某些示例如tutorial_dag会调用time.sleep()在高并发下拖垮整个 scheduler。3.2 airflow.cfg覆盖默认行为的 7 个关键参数虽然 Docker 镜像提供了合理的默认值但生产环境必须手动覆盖以下参数。我把它们放在./config/airflow.cfg中并通过 volume 挂载到所有容器[core] # 必须关闭否则每次重启都重新扫描所有 DAG 文件小文件多时耗时惊人 dags_folder /opt/airflow/dags # 生产环境禁止自动加载全部用 git sync 或 CI/CD 推送 load_examples False # DAG 创建时默认暂停避免误触发 dags_are_paused_at_creation True # 日志级别调高减少 I/O 压力 logging_level INFO # 关键禁用 pickle防止反序列化漏洞 enable_xcom_pickling False [database] # 连接池大小必须大于 scheduler 并发数否则出现 connection pool exhausted sql_alchemy_pool_size 20 sql_alchemy_max_overflow 10 # 连接超时设为 30 秒避免长事务阻塞 sql_alchemy_pool_recycle 3600 [celery] # worker 并发数 CPU 核数 * 24C 机器设为 8 worker_concurrency 8 # 任务软超时超过 1 小时自动标记失败防止僵尸任务 task_soft_time_limit 3600 # 任务硬超时超时后强制 kill 进程 task_time_limit 7200 [webserver] # Web UI 最大并发按实际用户数设100 用户建议 20 web_server_worker_timeout 120 # 防止 XSS 攻击 cookie_secure False # 若用 HTTPS 则设为 True cookie_samesite Lax [logging] # 日志存储路径必须挂载到宿主机否则容器销毁日志全丢 base_log_folder /opt/airflow/logs # 日志保留天数避免磁盘打满 log_retention_days 30注意sql_alchemy_pool_size和sql_alchemy_max_overflow的计算有讲究。假设你有 3 个 scheduler 实例高可用部署每个 scheduler 默认开 4 个线程那么理论最大连接数是3 * 4 12。但 PostgreSQL 默认max_connections100我们预留 20% 余量所以设pool_size12, max_overflow3即可。但如果你还跑了 2 个 worker每个 worker 开 8 个 celery 进程那就要重新计算scheduler_connections worker_connections 12 (2*8) 28此时pool_size30, max_overflow10更稳妥。3.3 DAG 目录结构与权限为什么chown -R 50000:0 ./dags是必做动作Airflow 官方镜像使用 UID 50000 的非 root 用户运行进程出于安全考虑这意味着如果你直接mkdir ./dags宿主机上该目录属于root:root容器内 UID 50000 无权读取如果你用sudo chown -R $USER:$USER ./dags宿主机用户 UID 可能是 1000容器内 UID 50000 依然无权访问。正确做法是mkdir -p ./dags ./logs ./plugins sudo chown -R 50000:0 ./dags ./logs ./plugins sudo chmod -R 755 ./dags ./logs ./plugins这样容器内进程才能顺利读取 DAG 文件、写入日志、加载插件。我曾遇到一个诡异问题DAG 文件明明存在但 Web UI 里始终显示 “No modules found”排查半天发现是./dags目录权限为700而 Airflow 用户组 ID 是 0root 组755才能让组用户有读取权限。DAG 目录的标准结构建议如下./dags/ ├── __init__.py # 必须存在否则 Airflow 不识别为 Python 包 ├── common/ # 公共工具函数如数据库连接封装 │ ├── __init__.py │ └── db_utils.py ├── etl/ # ETL 类 DAG │ ├── __init__.py │ ├── daily_user_etl.py │ └── hourly_order_etl.py ├── ml/ # 机器学习训练 DAG │ ├── __init__.py │ └── train_model_v2.py └── utils/ # 自定义 Operator ├── __init__.py └── slack_alert_operator.py这种分层结构让团队协作更清晰也方便 CI/CD 做增量部署比如只推送etl/下的变更。4. 实操过程与核心环节实现从启动到第一个 DAG 运行成功的完整链路4.1 初始化部署5 分钟完成首次启动与验证部署不是一蹴而就而是分阶段验证。我推荐按以下顺序操作每步验证成功再进行下一步第一步准备基础环境# 创建项目目录 mkdir airflow-deploy cd airflow-deploy # 创建必要目录并授权 mkdir -p dags logs plugins config sudo chown -R 50000:0 dags logs plugins # 下载官方 airflow.cfg 模板从 https://raw.githubusercontent.com/apache/airflow/constraints-2.8.3/airflow/config_templates/default_airflow.cfg curl -o config/airflow.cfg https://raw.githubusercontent.com/apache/airflow/constraints-2.8.3/airflow/config_templates/default_airflow.cfg # 替换其中的关键参数见 3.2 节第二步启动基础服务postgres redis# 启动数据库和消息队列不带 webserver/scheduler docker-compose up -d postgres redis # 等待 30 秒检查健康状态 docker-compose ps # 应看到 postgres 和 redis 的 STATUS 列为 healthy第三步初始化 Airflow 元数据库# 临时启动一个 Airflow 容器执行 db init docker run --rm \ -e AIRFLOW__CORE__SQL_ALCHEMY_CONNpostgresqlpsycopg2://airflow:airflowhost.docker.internal:5432/airflow \ -e AIRFLOW__CORE__FERNET_KEY46BKJoQYlPPOexq0OhDZnIlNepKFf87WFwLbfzqDDho \ apache/airflow:2.8.3 \ airflow db upgrade注意这里用host.docker.internal是为了让容器能访问宿主机的 docker daemon从而连上刚才启动的 postgres。如果是在 Linux 上需替换为宿主机真实 IP如172.17.0.1因为 Linux 不支持host.docker.internal。第四步启动全栈服务# 启动所有服务 docker-compose up -d # 查看日志流重点关注 webserver 和 scheduler docker-compose logs -f webserver scheduler # 等待 2 分钟当看到 Webserver available at http://0.0.0.0:8080 和 Scheduler started 即成功第五步登录验证与首个 DAG 测试浏览器打开http://localhost:8080用admin/admin登录。进入 Admin → Connections确认postgres_default连接测试成功。然后在./dags/下创建一个最简 DAG# ./dags/hello_world.py from datetime import datetime, timedelta from airflow import DAG from airflow.operators.python import PythonOperator def print_hello(): print(Hello from Airflow!) default_args { owner: airflow, depends_on_past: False, start_date: datetime(2023, 1, 1), email_on_failure: False, retries: 1, retry_delay: timedelta(minutes1), } dag DAG( hello_world, default_argsdefault_args, descriptionA simple hello world DAG, schedule_intervaltimedelta(days1), catchupFalse, ) t1 PythonOperator( task_idprint_hello, python_callableprint_hello, dagdag, )保存后回到 Web UI找到hello_worldDAG点击右侧的 “Trigger DAG” 按钮。几秒后点击 “Graph View”应该能看到一个绿色节点表示任务成功执行。此时打开./logs/hello_world/print_hello/目录能看到2023-01-01子目录下的1.log文件里面包含Hello from Airflow!输出——至此端到端链路完全打通。4.2 生产级加固SSL、认证、监控三件套上线前必须补上这三块短板否则就是裸奔SSL 加密Nginx 反向代理Airflow Web UI 默认 HTTP必须加 SSL。我用 Nginx 做反向代理配置如下upstream airflow_web { server 127.0.0.1:8080; } server { listen 443 ssl; server_name airflow.example.com; ssl_certificate /etc/nginx/ssl/airflow.crt; ssl_certificate_key /etc/nginx/ssl/airflow.key; location / { proxy_pass http://airflow_web; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 关键传递认证头给 Airflow proxy_set_header Authorization $http_authorization; } }同时修改airflow.cfg[webserver] base_url https://airflow.example.com cookie_secure TrueRBAC 权限体系替代 Basic AuthBasic Auth 太简陋生产必须用 RBAC。在docker-compose.yml的 webserver environment 中添加AIRFLOW__WEBSERVER__AUTHENTICATE: true AIRFLOW__WEBSERVER__AUTH_BACKEND: airflow.api.auth.backend.jwt_auth # JWT 密钥用 openssl rand -hex 32 生成 AIRFLOW__API__JWT_SECRET_KEY: a1b2c3d4e5f6...然后用airflow users create创建不同角色用户# 创建只读用户 airflow users create --username reader --password reader --role Viewer --email readerexample.com # 创建数据工程师可编辑 DAG airflow users create --username de --password de --role DataProfiler --email deexample.comPrometheus 监控集成Airflow 内置/health和/metrics端点。在airflow.cfg中启用[metrics] statsd_on True statsd_host host.docker.internal statsd_port 8125 statsd_prefix airflow然后部署 Prometheus Grafana用官方提供的 Airflow Dashboard 模板关键指标包括airflow_scheduler_heartbeatscheduler 心跳是否正常airflow_dag_processing_import_errors_totalDAG 解析错误数airflow_task_instance_duration_seconds_bucket任务耗时分布airflow_celery_worker_tasks_received_totalworker 接收任务数实测心得airflow_dag_processing_import_errors_total 0是最危险的信号意味着某个 DAG 语法错误或 import 失败会阻塞整个 DAG 解析线程导致所有新 DAG 无法加载。必须设置告警阈值设为 1。4.3 DAG 开发规范让调度系统真正“可维护”很多团队部署成功后DAG 越写越多最后变成一团乱麻。我总结了三条铁律第一禁止在 DAG 文件中写业务逻辑错误示范# ❌ bad: 业务代码混在 DAG 定义里 def process_data(): import pandas as pd # 每次调度都 import浪费资源 df pd.read_sql(SELECT * FROM orders, conn) # 连接未复用 df.to_csv(/tmp/output.csv)正确做法把业务逻辑封装成独立 Python 脚本或模块DAG 中只调用# ✅ good: DAG 只负责调度编排 from airflow.operators.python import PythonOperator from my_project.etl import load_orders_to_warehouse # 预先安装的包 t1 PythonOperator( task_idload_orders, python_callableload_orders_to_warehouse, # 纯函数调用 op_kwargs{date: {{ ds }}}, # 参数化传入 dagdag, )第二所有外部连接必须走 Airflow Connection禁止在代码里硬编码数据库密码# ❌ bad: 密码明文暴露 conn psycopg2.connect(hostdb.example.com userairflow password123456) # ✅ good: 从 Connection 获取 from airflow.hooks.postgres_hook import PostgresHook hook PostgresHook(postgres_conn_idprod_db) conn hook.get_conn()Connection 在 Web UI 的 Admin → Connections 中统一管理支持加密存储且能按环境dev/staging/prod切换。第三强制设置catchupFalse和max_active_runs1catchupTrue是定时任务的“地雷”。比如一个每天跑的 DAG如果因故障停了 3 天catchupTrue会一次性补跑 3 次瞬间压垮数据库。max_active_runs1防止同一 DAG 的多个实例并发执行避免数据覆盖。这两个参数应作为 DAG 模板的默认值default_args { catchup: False, max_active_runs: 1, # ... 其他参数 }5. 常见问题与排查技巧实录那些让我熬夜到凌晨三点的 Bug5.1 DAG 不显示/显示为灰色90% 是路径或权限问题现象DAG 文件已放入./dags/但 Web UI 列表为空或显示为灰色no status。排查路径查日志docker-compose logs webserver | grep DAG重点看DAG import error或Failed to import。查容器内路径docker exec -it webserver_container_id ls -l /opt/airflow/dags/确认文件存在且权限为-rw-r--r--644。查 Python 语法在宿主机上python -m py_compile ./dags/hello_world.py如果报错说明语法错误。查依赖缺失如果 DAG 里import boto3但镜像没装apache-airflow-providers-amazon会报ModuleNotFoundError。解决方案在docker-compose.yml的 webserver service 中添加build步骤或用pip install命令注入webserver: image: apache/airflow:2.8.3 # ... 其他配置 command: bash -c pip install apache-airflow-providers-amazon airflow db upgrade airflow users create ... airflow webserver5.2 Task 卡在queued状态Celery 配置的三大死穴现象DAG 触发后Task 一直显示queued从不变成running。根本原因只有三个Redis 连接失败检查AIRFLOW__CELERY__BROKER_URL是否正确redis服务是否 healthy。用docker exec -it redis_container_id redis-cli ping测试。Worker 未启动或崩溃docker-compose ps worker看状态docker-compose logs worker看是否有ConnectionRefusedError。常见原因是BROKER_URL里密码错误或 Redis 的requirepass未配置。并发数超限celery worker --concurrency 4但实际有 10 个任务排队前 4 个会running其余queued。解决方案增加 worker 实例数docker-compose up -d --scale worker2或调高单 worker 并发--concurrency 8。5.3 Scheduler 高 CPU 占用不是性能问题是配置缺陷现象docker stats显示 scheduler 容器 CPU 持续 90%但任务执行缓慢。真相这是parsing_processes参数过高导致。Airflow scheduler 默认开 2 个进程扫描 DAG 目录但如果 DAG 文件多100 个、单个 DAG 复杂含大量SubDagOperator扫描会非常耗 CPU。解决方案在airflow.cfg中调低[core] # 默认是 2对于 50 个 DAG 的环境设为 1 即可 parsing_processes 1 # 同时延长扫描间隔减少频率 min_file_process_interval 300 # 从默认 30 秒改为 5 分钟实测效果CPU 从 90% 降到 15%DAG 加载延迟从 2 分钟降到 20 秒。5.4 数据库连接池耗尽sqlalchemy的隐藏陷阱现象Web UI 打不开日志报sqlalchemy.exc.TimeoutError: QueuePool limit of size 10 overflow 20 reached。原因sql_alchemy_pool_size设得太小而并发请求scheduler 线程 webserver 请求 CLI 命令超过了池大小。计算公式所需最小连接数 scheduler_threads * scheduler_instances webserver_workers * webserver_instances CLI_concurrent_users * 2例如1 个 scheduler4 线程 1 个 webserver20 workers 5 个 CLI 用户 4 20 10 34。所以pool_size40, max_overflow10是安全值。注意PostgreSQL 的max_connections必须大于这个总和否则数据库层会拒绝连接。show max_connections;查看当前值ALTER SYSTEM SET max_connections 200;修改需重启。5.5 日志丢失挂载路径与权限的双重陷阱现象DAG 执行后Web UI 的 Log Viewer 显示*** Log file does not exist: ...。
阅读完成 · 觉得有帮助?