MAATRIX / Блог / Пять задач по расписанию: cron справится дешевле, чем Apache Airflow

Пять задач по расписанию: cron справится дешевле, чем Apache Airflow

MAATRIX

Кто-то в команде увидел красивые DAG-графы в интерфейсе Airflow и предложил «перевести туда все наши скрипты по расписанию». Через месяц выясняется, что ради пяти независимых задач — выгрузить отчёт, почистить старые файлы, забрать курс валют, отправить дайджест, прогнать бэкап — теперь крутится веб-сервер, планировщик, база метаданных в Postgres и воркер, и половину времени администратора съедает не сама задача, а поддержка платформы, которая её запускает. Разберём честно: для чего Airflow создавался, когда он реально экономит время, а когда пять строк в crontab дешевле и надёжнее всей этой конструкции.

Что такое Apache Airflow и какую проблему он решает

Airflow — это платформа оркестрации рабочих процессов (workflow orchestration), написанная на Python. Задача описывается не списком команд, а DAG — Directed Acyclic Graph, направленным ациклическим графом задач с зависимостями между ними. Смысл не в «запустить скрипт по времени» (это и cron умеет), а в управлении графом: «шаг B стартует только после успешного завершения шага A», «если шаг C упал — повторить три раза с задержкой, а если не помогло — остановить весь пайплайн», «шаги D и E выполняются параллельно, но оба должны завершиться до шага F».

Типичный сценарий, для которого Airflow придумывался, — конвейер данных (data pipeline) уровня компании: забрать данные из десятка источников, провалидировать, преобразовать, загрузить в хранилище, пересчитать витрины для аналитиков — с учётом того, что часть шагов может упасть по сетевой ошибке, часть должна ждать завершения предыдущих, а на графе из полусотни задач нужно быстро видеть, где застряло выполнение сегодня ночью.

Отсюда и набор возможностей, которых у обычного планировщика нет:

  • Зависимости между задачами. Граф: задача знает, от чего она зависит, и не запускается, пока зависимости не выполнены успешно.
  • Ретраи с политикой. Количество попыток, задержка между ними, получатели уведомления при финальном провале — настраивается на уровне отдельной задачи, а не пишется вручную в каждом скрипте.
  • Визуализация графа выполнения. Веб-интерфейс показывает статус каждой задачи за каждый запуск: зелёный — успех, красный — провал, жёлтый — выполняется. Для графа из 40 задач это быстрее, чем читать логи построчно.
  • Backfill. Если пайплайн правили и нужно пересчитать данные за прошлые две недели с новой логикой — Airflow умеет проиграть DAG за прошедшие даты, не переписывая скрипт под ручной цикл.
  • Sensor-задачи — тип задачи, который не делает работу, а ждёт условия (появления файла, ответа API) и только потом пропускает выполнение дальше по графу.

Ничего из этого не бесплатно с точки зрения инфраструктуры — и это ключевая часть разговора.

Из чего состоит Airflow «под капотом»

Когда Airflow предлагают поставить «просто чтобы было», часто недооценивают, что это не один процесс, а система из нескольких компонентов, работающих одновременно:

КомпонентРольЧто будет, если упадёт
WebserverВеб-интерфейс: графы DAG, логи, ручной запуск, статусыUI недоступен, выполнение задач может продолжаться
SchedulerЧитает DAG-файлы, решает, какие задачи запускать, ставит в очередьНовые задачи не запускаются вообще — критичный компонент
Metadata DBPostgreSQL (реже MySQL) — состояние всех DAG, задач, запусковБез неё Airflow не работает вообще, ни один компонент
ExecutorГде физически исполняется задача: локально (LocalExecutor), в Celery-воркерах, в Kubernetes-подахПри CeleryExecutor нужен ещё и брокер сообщений (Redis/RabbitMQ)
Worker(s)Процессы, выполняющие код задач (при Celery/Kubernetes executor)Задачи ставятся в очередь, но не выполняются

Даже в минимальной конфигурации (LocalExecutor, без отдельных воркеров) это минимум три процесса — webserver, scheduler, база данных, — которые нужно поднимать, обновлять синхронно по версиям и держать под наблюдением. Официальный docker-compose из документации проекта прямо предупреждает, что он не рассчитан на продакшн «как есть» и требует заметного объёма памяти уже для демо-запуска — стоит знать это заранее, а не выяснять после того, как сервер ушёл в своп.

Для сравнения: crontab — текстовый файл, который читает один системный демон, уже установленный в системе. Ни веб-сервера, ни базы метаданных, ни очереди сообщений. Задача либо выполнилась, либо нет — узнаёте вы об этом из кода возврата и того, что сами настроили для логирования (подробнее про сам механизм — в статье как установить и настроить cron-задачи на VPS).

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Когда Airflow реально оправдан

Есть конкретный набор признаков, при совпадении нескольких из которых оркестратор данных перестаёт быть роскошью и начинает экономить время:

Десятки задач со сложными зависимостями друг от друга. Граф из 20–50+ шагов, где половина ждёт результата другой половины, причём зависимости меняются по мере роста системы, — держать это в bash-скрипте с ручными проверками кода возврата быстро становится источником ошибок. Граф в Airflow делает зависимости явными и проверяемыми.

Нужны предсказуемые ретраи с разной политикой на разных шагах. Не «упало — перезапустили руками через час», а «эта задача повторяется 3 раза с задержкой в 5 минут, а критичная — сразу эскалируется». В cron такое пишется вручную в каждом скрипте, и вы неизбежно придёте к своей самодельной версии того же механизма.

Нужна визуальная картина конвейера для команды. Если над пайплайнами работает несколько человек и им важно видеть на одном экране, какой шаг сегодня не отработал и почему, — UI Airflow с историей запусков экономит время на коммуникации, а не только на технике.

Нужен backfill — пересчёт истории при изменении логики. Поправили трансформацию данных, нужно пересчитать витрины за последние три месяца с новой логикой для каждого дня отдельно. Ручной цикл for date in ...; do python transform.py --date $date; done работает, но не отслеживает частичные сбои и не даёт картины, какие даты уже пересчитаны.

Задачи гетерогенны и физически распределены — часть шагов это SQL-запросы к хранилищу, часть вызовы внешних API, часть тяжёлые Python-джобы на отдельных воркерах с большим объёмом памяти. Executor Airflow с несколькими очередями решает распределение нагрузки; в cron это либо несколько серверов с ручной синхронизацией, либо всё на одной машине без изоляции.

Если у вас реальный ETL/ELT-конвейер такого масштаба — расходы на поддержку Airflow (сервер под него, время на обновления, освоение DAG-синтаксиса командой) окупаются экономией времени на отладке и предсказуемостью работы. Это случай, где оркестратор не «модно», а действительно снимает боль, которая иначе росла бы линейно с числом задач.

Когда это оверинжиниринг: пять задач по расписанию

Возьмём реалистичный сценарий: у вас пять задач по расписанию, и они не зависят друг от друга — ночной бэкап базы, ежедневная выгрузка отчёта в бухгалтерию, синхронизация курса валют раз в час, еженедельная чистка временных файлов, ежедневная рассылка дайджеста. У каждой свой ритм, и провал одной не должен останавливать остальные.

Что вы получаете, разворачивая Airflow под такой набор:

  • Инфраструктура ради инфраструктуры. Веб-сервер, планировщик, СУБД для метаданных (а часто и брокер сообщений, если сразу заложились «на масштабирование» через CeleryExecutor) — минимум два-три процесса, которые сами по себе ничего не делают для бизнеса, а только управляют тем, что и так работало бы через строку в crontab.
  • Постоянная поддержка платформы, а не задач. Обновления между минорными версиями иногда меняют поведение провайдеров и требуют миграции метаданных БД. Для пяти независимых задач это значит, что большую часть времени вы тратите на поддержание оркестратора, а не самих задач.
  • Порог входа для команды. DAG пишется в Python-файле со своим синтаксисом (операторы, schedule, XCom для передачи данных между задачами). Если задачи писал администратор, использующий Python эпизодически, время на освоение модели может превысить время, которое вы тратите на все пять задач за год.
  • Точка отказа усложняется. В cron, если что-то сломалось, вы разбираетесь с одной задачей и её логом. В Airflow добавляется вопрос: жив ли scheduler, жива ли metadata DB, не разошлись ли версии между webserver и воркерами. Ресурсы сервера при этом расходуются на саму платформу постоянно, а не только когда задача реально выполняется.

Здесь работает та же логика, что и в других решениях «взять инструмент посерьёзнее про запас»: похожая ситуация разобрана в статье «Антипаттерн: очередь сообщений там, где хватило бы cron» — механизм тот же, просто с другим инструментом. Сложность, которую вы не используете, не бесплатна: она требует поддержки постоянно, а пользу приносит только когда вырастает объём задач и их взаимосвязей.

Что реально теряет cron — и как это закрыть без Airflow

Честный разбор не обходится без признания слабых мест. У голого cron действительно нет из коробки того, что есть в Airflow.

Нет визуализации и истории запусков. По умолчанию cron не хранит историю успехов и провалов — только то, что попало в лог, если вы его настроили. Закрывается сервисом вроде healthchecks.io: каждая задача в конце скрипта дёргает свой URL, и если пинг не пришёл вовремя — приходит уведомление (подробная настройка — в статье мониторинг cron-задач через healthchecks.io). Не такой богатый UI, как граф DAG, но для пяти задач вопрос «отработало или нет» закрывает полностью.

Нет автоматических ретраев. В cron это добавляется руками — обёрткой скрипта в цикл с ограниченным числом попыток и задержкой:

#!/bin/bash
# retry-wrapper.sh — до 3 попыток с паузой 60 секунд
MAX_ATTEMPTS=3; DELAY=60; attempt=1
until "$@"; do
  if [ $attempt -ge $MAX_ATTEMPTS ]; then
    echo "Провал после $MAX_ATTEMPTS попыток: $*" >&2; exit 1
  fi
  attempt=$((attempt + 1)); sleep $DELAY
done

Вызов в crontab: 0 3 * * * /opt/scripts/retry-wrapper.sh /opt/scripts/backup.sh >> /var/log/backup.log 2>&1. Не так гибко, как политика ретраев на уровне отдельной задачи в Airflow, но для пяти скриптов без сложных зависимостей — достаточно.

Нет зависимостей между задачами. Если вторая задача действительно должна ждать первую, простой вариант — не два отдельных crontab-задания, а один скрипт, последовательно вызывающий оба шага с проверкой кода возврата между ними. Если зависимостей больше двух-трёх и они меняются — это сигнал присмотреться к Airflow или к чему-то среднему (см. следующий раздел).

Логи разбросаны по разным файлам. Решается единым каталогом логов с ротацией через logrotate — все cron-задачи пишут туда с префиксом имени задачи и timestamp. Не так удобно, как единый UI, но грепается за секунды.

Нет предупреждения о просроченном запуске. Например, при переводе часов или зависшем сервере — но и это закрывается тем же healthchecks.io: если пинг не пришёл в ожидаемое окно, вы узнаёте сразу, а не когда кто-то заметит отсутствие отчёта.

Установка Airflow: минимальный рабочий пример

Если по итогам разбора вы попадаете в категорию «оркестратор оправдан» — вот минимальный путь через официальный docker-compose проекта, с LocalExecutor (без отдельных Celery-воркеров и брокера — расширять до CeleryExecutor стоит, когда воркеров реально понадобится несколько). Актуальный docker-compose.yaml нужно брать со страницы официальной установки в документации Apache Airflow — он меняется от релиза к релизу, публиковать здесь фиксированную копию смысла нет, она устареет быстрее статьи. Последовательность разворачивания одинакова из версии в версию:

mkdir -p ./dags ./logs ./plugins
echo "AIRFLOW_UID=50000" > .env
curl -LfO 'https://airflow.apache.org/docs/apache-airflow/stable/docker-compose.yaml'

# Инициализация базы метаданных и создание первого пользователя
docker compose up airflow-init

# Запуск всех сервисов
docker compose up -d

После запуска веб-интерфейс поднимается на порту 8080 (публиковать наружу без реверс-прокси с HTTPS и ограничения доступа по IP или Basic Auth не стоит — это панель управления выполнением ваших пайплайнов). Простейший DAG с одной задачей:

from airflow import DAG
from airflow.operators.bash import BashOperator
from datetime import datetime, timedelta

with DAG(
    dag_id="daily_report",
    start_date=datetime(2026, 1, 1),
    schedule="0 6 * * *",
    catchup=False,
    default_args={"retries": 3, "retry_delay": timedelta(minutes=5)},
) as dag:
    generate_report = BashOperator(
        task_id="generate_report",
        bash_command="/opt/scripts/generate_report.sh",
    )

Файл кладётся в каталог dags/, scheduler подхватывает его автоматически в течение минуты-другой. Сравните объём этой обвязки с эквивалентной строкой в crontab 0 6 * * * /opt/scripts/generate_report.sh, которая делает то же самое без ретраев из коробки.

Промежуточный вариант: что делать между «cron» и «Airflow»

Реальность часто не бинарна: не пять задач и не пятьдесят, а, скажем, дюжина, часть связана попарно, и хочется визуализации и ретраев, но полноценный оркестратор явно избыточен. Есть промежуточные варианты:

  • Windmill — легче Airflow по модели ресурсов, ближе к «превратить скрипт в задачу с расписанием, ретраями и веб-UI», без полноценной модели DAG-графа данных.
  • n8n — изначально инструмент no-code автоматизации, но умеет и расписания, и цепочки шагов с условной логикой; порог входа ниже, но модель мышления другая — визуальный конструктор, а не Python-код.
  • systemd timers — если нужна не визуализация, а то, чего не умеет cron по умолчанию (запуск сразу после старта системы, если окно было пропущено, изоляция ресурсов через cgroups) — это всё ещё не Airflow, а часть системы, уже установленной на сервере.

Прежде чем брать любой из этих вариантов «на будущее», стоит трезво посчитать, сколько сейчас реально независимых задач и сколько из них через полгода останутся нужными — практический разбор такой ревизии есть в статье ревизия cron-задач: сколько из них ещё нужны. Часто после неё оказывается, что часть задач можно удалить, а оставшихся действительно пять, а не двадцать, — и вопрос выбора оркестратора снимается сам собой.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Airflow можно поставить не в Docker, а напрямую через pip?

Да, через pip install apache-airflow, но зависимости у проекта специфичные (constraints-файлы под конкретную версию Python), и Docker Compose или Helm-чарт для Kubernetes — заметно более предсказуемый путь для продакшна.

Сколько ресурсов сервера реально нужно под Airflow?

Точная цифра зависит от числа и веса задач и от executor'а, но даже для демо-запуска официальная документация прямо предупреждает о заметном потреблении памяти самой платформой — до запуска хоть одной вашей задачи. Планируйте отдельный сервер, а не «подселение» к существующим сервисам.

Можно ли начать с cron, а потом перейти на Airflow, если задач станет больше?

Да, это разумный путь: cron-скрипт и Python-функция, вызываемая из DAG, часто отличаются минимально — логика остаётся той же, меняется обвязка вокруг неё (расписание, ретраи, зависимости).

Что если я боюсь, что через год задач станет много, и лучше сразу заложиться на Airflow?

Заложиться «на будущее» — тот самый оверинжиниринг: вы платите инфраструктурой и временем на поддержку с первого дня ради роста, который может не случиться в предсказанном масштабе. Дешевле мигрировать, когда задач станет двадцать пять с реальными зависимостями, чем нести стоимость платформы все месяцы, пока их всё ещё пять.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →