<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Apache Airflow - DataTalks.RU. Data Engineering / DWH / Data Pipeline</title>
	<atom:link href="https://datatalks.ru/tag/apache-airflow/feed/" rel="self" type="application/rss+xml" />
	<link>https://datatalks.ru/tag/apache-airflow/</link>
	<description>RoadMap для инженера данных. Дорожная карта по инструментам Data Engineer</description>
	<lastBuildDate>Thu, 12 Feb 2026 20:32:20 +0000</lastBuildDate>
	<language>ru-RU</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://datatalks.ru/wp-content/uploads/2024/12/cropped-logo_datatalks-32x32.png</url>
	<title>Apache Airflow - DataTalks.RU. Data Engineering / DWH / Data Pipeline</title>
	<link>https://datatalks.ru/tag/apache-airflow/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Best Practices &#8212; Airflow 3 Документация</title>
		<link>https://datatalks.ru/best-practices-airflow-3-documentation/</link>
					<comments>https://datatalks.ru/best-practices-airflow-3-documentation/#respond</comments>
		
		<dc:creator><![CDATA[Data Engineer (Admin)]]></dc:creator>
		<pubDate>Sat, 17 Jan 2026 15:48:03 +0000</pubDate>
				<category><![CDATA[Apache Airflow Best Practices]]></category>
		<category><![CDATA[Apache Airflow]]></category>
		<category><![CDATA[Apache Airflow 3]]></category>
		<guid isPermaLink="false">https://datatalks.ru/?p=2751</guid>

					<description><![CDATA[<p>Перевод документации Apache Airflow 3 &#8212; Best Practices Лучшие практики по работе с Apache Airflow 3 Создание нового Dag — это процесс из трёх шагов: написание Python-кода для создания объекта Dag, проверка того, что код соответствует вашим ожиданиям, настройка зависимостей окружения для запуска вашего Dag В этом руководстве представлены лучшие практики для этих трёх шагов. [&#8230;]</p>
<p>Сообщение <a href="https://datatalks.ru/best-practices-airflow-3-documentation/">Best Practices &#8212; Airflow 3 Документация</a> появились сначала на <a href="https://datatalks.ru">DataTalks.RU. Data Engineering / DWH / Data Pipeline</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>Перевод <a href="https://airflow.apache.org/docs/apache-airflow/stable/best-practices.html" target="_blank" rel="noopener">документации Apache Airflow 3 &#8212; Best Practices</a></p>
<h1>Лучшие практики по работе с Apache Airflow 3</h1>
<p>Создание нового <strong>Dag</strong> — это процесс из трёх шагов:</p>
<ul>
<li>написание Python-кода для создания объекта Dag,</li>
<li>проверка того, что код соответствует вашим ожиданиям,</li>
<li>настройка зависимостей окружения для запуска вашего Dag</li>
</ul>
<p>В этом руководстве представлены лучшие практики для этих трёх шагов.</p>
<h2>Написание Dag</h2>
<p>Создание нового Dag в Airflow довольно простое. Однако существует множество вещей, о которых необходимо позаботиться, чтобы запуск <strong>Dag</strong> или его сбой не приводили к неожиданным результатам.</p>
<h3><strong>Создание пользовательского Operator/Hook</strong></h3>
<p>Пожалуйста, следуйте нашему <a href="https://airflow.apache.org/docs/apache-airflow/stable/howto/custom-operator.html#custom-operator" target="_blank" rel="noopener">руководству по пользовательским (custom) <strong>Operator</strong>’ам</a>.</p>
<h3><strong>Создание задачи</strong></h3>
<p>Вы должны рассматривать задачи в <strong>Airflow</strong> как эквивалент транзакций в базе данных. Это означает, что ваши задачи никогда не должны производить неполные результаты. Например, нельзя оставлять неполные данные в <strong>HDFS</strong> или <strong>S3</strong> по завершении задачи.</p>
<p><strong>Airflow</strong> может повторно запускать задачу в случае её сбоя. Следовательно, задачи должны выдавать одинаковый результат при каждом повторном запуске. Некоторые способы избежать получения различного результата:</p>
<ul>
<li>Не используйте <code>INSERT</code> при повторном запуске задачи — оператор <code>INSERT</code> может привести к появлению дублирующихся строк в базе данных. Замените его на <code>UPSERT</code>.</li>
<li>Читайте и записывайте данные в конкретный партицию. Никогда не читайте самые последние доступные данные в задаче. Кто-то может обновить входные данные между повторными запусками, что приведёт к разным результатам. Лучший подход — читать входные данные из конкретного партициона. В качестве партициона можно использовать <code>data_interval_start</code>. Этот же метод партиционирования следует применять и при записи данных в S3/HDFS.</li>
<li>Функция Python datetime <code>now()</code> возвращает текущий объект <code>datetime</code>. Эту функцию никогда не следует использовать внутри задачи, особенно для выполнения критических вычислений, так как это приводит к разным результатам при каждом запуске. Допустимо использовать её, например, для генерации временного лога.</li>
</ul>
<p><strong>Совет</strong></p>
<p>Следует определять повторяющиеся параметры, такие как <code>connection_id</code> или пути <strong>S3</strong>, в <code>default_args</code>, а не объявлять их для каждой задачи. <code>default_args</code> помогают избежать ошибок, таких как опечатки. Кроме того, большинство типов соединений имеют уникальные имена параметров в задачах, поэтому вы можете объявить соединение только один раз в <code>default_args</code> (например, <code>gcp_conn_id</code>), и оно будет автоматически использоваться всеми операторами, которые работают с данным типом соединения.</p>
<h3><strong>Удаление задачи</strong></h3>
<p>Будьте осторожны при удалении задачи из Dag. После удаления вы не сможете увидеть эту задачу в <strong>Graph View</strong>, <strong>Grid View</strong> и других представлениях, что усложнит проверку логов данной задачи через <strong>Webserver</strong>. Если такое поведение нежелательно, пожалуйста, создайте новый <strong>Dag</strong>.</p>
<h3><strong>Коммуникация</strong></h3>
<p><strong>Airflow</strong> выполняет задачи Dag на разных серверах в случае использования <strong><a href="https://airflow.apache.org/docs/apache-airflow-providers-cncf-kubernetes/stable/kubernetes_executor.html" target="_blank" rel="noopener">Kubernetes Executor</a></strong> или <strong><a href="https://airflow.apache.org/docs/apache-airflow-providers-celery/stable/celery_executor.html" target="_blank" rel="noopener">Celery Executor</a></strong>. Поэтому не следует хранить какие-либо файлы или конфигурации в локальной файловой системе, так как следующая задача с большой вероятностью будет выполняться на другом сервере без доступа к ним — например, задача, которая загружает файл с данными, который затем обрабатывается следующей задачей. В случае использования <code>Local Executor</code> хранение файлов на диске также может усложнить повторные запуски, например если вашей задаче требуется конфигурационный файл, который удаляется другой задачей в Dag.</p>
<p>По возможности используйте <code>XCom</code> для передачи небольших сообщений между задачами, а для передачи больших объёмов данных используйте удалённое хранилище, такое как <strong>S3</strong> или <strong>HDFS</strong>. Например, если у вас есть задача, которая сохраняет обработанные данные в <strong>S3</strong>, эта задача может положить путь к выходным данным в <strong>S3</strong> в <strong>XCom</strong>, а <strong>downstream</strong>-задачи смогут получить этот путь из <strong>XCom</strong> и использовать его для чтения данных.</p>
<p>Задачи также не должны хранить внутри себя какие-либо параметры аутентификации, такие как пароли или токены. По возможности используйте <a href="https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/connections.html" target="_blank" rel="noopener"><strong>Connections</strong></a> для безопасного хранения данных в <strong>backend</strong>’е <strong>Airflow</strong> и получайте их с помощью уникального <strong>connection id</strong>.</p>
<h3><strong>Код верхнего уровня Python</strong></h3>
<p>Следует избегать написания кода верхнего уровня, который не требуется для создания <strong>Operator</strong>’ов и построения связей <strong>Dag</strong> между ними. Это связано с архитектурным решением планировщика <strong>Airflow</strong> и влиянием скорости парсинга кода верхнего уровня на производительность и масштабируемость Airflow.</p>
<p><strong>Планировщик Airflow</strong> выполняет код вне методов execute операторов с минимальным интервалом <a href="https://airflow.apache.org/docs/apache-airflow/stable/configurations-ref.html#config-dag-processor-min-file-process-interval" target="_blank" rel="noopener"><code>min_file_process_interval</code></a> секунд. Это делается для того, чтобы обеспечить динамическое планирование Dag’ов — когда расписание и зависимости могут со временем изменяться и влиять на следующий запуск Dag. <strong>Планировщик Airflow</strong> постоянно старается убедиться, что то, что описано в Dag’ах, корректно отражено в запланированных задачах.</p>
<p>В частности, не следует выполнять доступ к базам данных, тяжёлые вычисления и сетевые операции.</p>
<p>Одним из важных факторов, влияющих на время загрузки Dag, который часто упускают из виду Python-разработчики, является то, что импорты на верхнем уровне могут занимать неожиданно много времени и создавать значительные накладные расходы. Этого легко избежать, переместив такие импорты в локальные импорты внутри <strong>Python-callable</strong>, например.</p>
<p>Рассмотрим два примера ниже. В первом примере <strong>Dag</strong> будет парситься на дополнительные 1000 секунд дольше, чем функционально эквивалентный <strong>Dag</strong> во втором примере, где <code>expensive_api_call</code> выполняется в контексте своей задачи.</p>
<p>Неизбежание кода верхнего уровня Dag:</p><pre class="urvanov-syntax-highlighter-plain-tag">import pendulum

from airflow.sdk import DAG
from airflow.sdk import task


def expensive_api_call():
    print("Hello from Airflow!")
    sleep(1000)


my_expensive_response = expensive_api_call()

with DAG(
    dag_id="example_python_operator",
    schedule=None,
    start_date=pendulum.datetime(2021, 1, 1, tz="UTC"),
    catchup=False,
    tags=["example"],
) as dag:

    @task()
    def print_expensive_api_call():
        print(my_expensive_response)</pre><p>Избегание кода верхнего уровня <strong>Dag</strong>:</p><pre class="urvanov-syntax-highlighter-plain-tag">import pendulum

from airflow.sdk import DAG
from airflow.sdk import task


def expensive_api_call():
    sleep(1000)
    return "Hello from Airflow!"


with DAG(
    dag_id="example_python_operator",
    schedule=None,
    start_date=pendulum.datetime(2021, 1, 1, tz="UTC"),
    catchup=False,
    tags=["example"],
) as dag:

    @task()
    def print_expensive_api_call():
        my_expensive_response = expensive_api_call()
        print(my_expensive_response)</pre><p>В первом примере <code>expensive_api_call</code> выполняется каждый раз при парсинге файла <strong>Dag</strong>, что приводит к неоптимальной производительности при обработке <strong>Dag</strong>-файла. Во втором примере <code>expensive_api_call</code> вызывается только во время выполнения задачи и, таким образом, Dag может быть распарсен без потери производительности. Чтобы проверить это самостоятельно, реализуйте первый Dag и посмотрите, как строка <strong>«Hello from Airflow!»</strong> выводится в логах планировщика.</p>
<p>Обратите внимание, что операторы <code>import</code> также считаются кодом верхнего уровня. Поэтому, если у вас есть <code>import</code>, который выполняется долго, или импортируемый модуль сам выполняет код на верхнем уровне, это также может негативно сказаться на производительности планировщика. Следующий пример показывает, как работать с дорогостоящими импортами.</p><pre class="urvanov-syntax-highlighter-plain-tag"># It's ok to import modules that are not expensive to load at top-level of a Dag file
import random
import pendulum

# Expensive imports should be avoided as top level imports, because Dag files are parsed frequently, resulting in top-level code being executed.
#
# import pandas
# import torch
# import tensorflow
#

...


@task()
def do_stuff_with_pandas_and_torch():
    import pandas
    import torch

    # do some operations using pandas and torch


@task()
def do_stuff_with_tensorflow():
    import tensorflow

    # do some operations using tensorflow</pre><p></p>
<h3><strong>Как проверить, является ли мой код «кодом верхнего уровня»</strong></h3>
<p>Чтобы понять, является ли ваш код <strong>«кодом верхнего уровня»</strong> или нет, необходимо разбираться во многих тонкостях того, как работает <strong>парсинг</strong> Python. В общем случае, когда <strong>Python</strong> парсит файл, он выполняет весь код, который видит, за исключением (как правило) внутреннего кода методов, который он не выполняет.</p>
<p>Существует ряд неочевидных специальных случаев — например, к коду верхнего уровня также относится любой код, используемый для определения значений по умолчанию у методов.</p>
<p>Однако есть простой способ проверить, является ли ваш код <strong>«кодом верхнего уровня»</strong> или нет. Достаточно распарсить ваш код и посмотреть, выполняется ли данный фрагмент кода.</p>
<p>Представьте следующий код:</p><pre class="urvanov-syntax-highlighter-plain-tag">from airflow.sdk import DAG
from airflow.providers.standard.operators.python import PythonOperator
import pendulum


def get_task_id():
    return "print_array_task"  # &lt;- is that code going to be executed?


def get_array():
    return [1, 2, 3]  # &lt;- is that code going to be executed?


with DAG(
    dag_id="example_python_operator",
    schedule=None,
    start_date=pendulum.datetime(2021, 1, 1, tz="UTC"),
    catchup=False,
    tags=["example"],
) as dag:
    operator = PythonOperator(
        task_id=get_task_id(),
        python_callable=get_array,
        dag=dag,
    )</pre><p>Чтобы это проверить, вы можете добавить несколько операторов <code>print</code> в код, который хотите проверить, а затем выполнить команду <code>python &lt;my_dag_file&gt;.py</code>.</p><pre class="urvanov-syntax-highlighter-plain-tag">from airflow.sdk import DAG
from airflow.providers.standard.operators.python import PythonOperator
import pendulum


def get_task_id():
    print("Executing 1")
    return "print_array_task"  # &lt;- is that code going to be executed? YES


def get_array():
    print("Executing 2")
    return [1, 2, 3]  # &lt;- is that code going to be executed? NO


with DAG(
    dag_id="example_python_operator",
    schedule=None,
    start_date=pendulum.datetime(2021, 1, 1, tz="UTC"),
    catchup=False,
    tags=["example"],
) as dag:
    operator = PythonOperator(
        task_id=get_task_id(),
        python_callable=get_array,
        dag=dag,
    )</pre><p>При выполнении этого кода вы увидите:</p><pre class="urvanov-syntax-highlighter-plain-tag">[Breeze:3.10.19] root@cf85ab34571e:/opt/airflow# python /files/test_python.py
Executing 1</pre><p>Это означает, что <code>get_array</code> не выполняется как код верхнего уровня, а <code>get_task_id</code> — выполняется.</p>
<h3><strong>Качество кода и линтинг</strong></h3>
<p>Поддержание высокого качества кода имеет ключевое значение для надёжности и сопровождаемости ваших <strong>workflow</strong> в <strong>Airflow</strong>. Использование инструментов <strong>линтинга</strong> помогает выявлять потенциальные проблемы и обеспечивать соблюдение стандартов кодирования. Одним из таких инструментов является <strong>ruff</strong> — быстрый линтер для Python, который теперь включает специальные правила для Airflow.</p>
<p><strong>ruff</strong> помогает выявлять устаревшие возможности и паттерны, которые могут повлиять на миграцию на <strong>Airflow 3.0</strong>. Например, он включает правила с префиксом AIR, предназначенные для обнаружения потенциальных проблем.</p>
<p><em>Полный список этих правил описан в <a href="https://docs.astral.sh/ruff/rules/#airflow-air" target="_blank" rel="noopener">разделе Airflow (AIR)</a>.</em></p>
<h3><strong>Установка и использование ruff</strong></h3>
<blockquote><p><strong>ruff</strong> — это очень быстрый линтер и автоформаттер для Python, написанный на Rust (в десятки раз быстрее <code>flake8</code>, <code>isort</code>, <code>pylint</code>).</p></blockquote>
<hr />
<p>Установка: установите <code>ruff</code> с помощью <code>pip</code>:</p><pre class="urvanov-syntax-highlighter-plain-tag">pip install "ruff&gt;=0.14.10"</pre><p><strong>Запуск ruff:</strong> выполните <code>ruff</code> для проверки ваших Dag’ов на наличие потенциальных проблем:</p><pre class="urvanov-syntax-highlighter-plain-tag">ruff check dags/ --select AIR3</pre><p>Эта команда проанализирует ваши <strong>Dag</strong>’и, расположенные в директории <code>dags/</code>, и сообщит о проблемах, связанных с указанными правилами.</p>
<p><strong>Пример</strong></p>
<p>Рассмотрим <strong>legacy</strong> <strong>Dag</strong>, определённый следующим образом:</p><pre class="urvanov-syntax-highlighter-plain-tag">from airflow import dag
from airflow.datasets import Dataset
from airflow.sensors.filesystem import FileSensor


@dag()
def legacy_dag():
    FileSensor(task_id="wait_for_file", filepath="/tmp/test_file")</pre><p>Запуск <code>ruff</code> приведёт к следующему выводу:</p><pre class="urvanov-syntax-highlighter-plain-tag">dags/legacy_dag.py:7:2: AIR301 Dag should have an explicit schedule argument
dags/legacy_dag.py:12:6: AIR302 schedule_interval is removed in Airflow 3.0
dags/legacy_dag.py:17:15: AIR302 airflow.datasets.Dataset is removed in Airflow 3.0
dags/legacy_dag.py:19:5: AIR303 airflow.sensors.filesystem.FileSensor is moved into ``standard`` provider in Airflow 3.0</pre><p>Интегрируя <code>ruff</code> в ваш процесс разработки, вы можете заблаговременно устранять устаревшие элементы и поддерживать высокое качество кода, что облегчает переход между версиями <strong>Airflow</strong>.</p>
<h3><strong>Динамическая генерация Dag</strong></h3>
<p>Иногда написание <strong>Dag</strong>’ов вручную нецелесообразно. Возможно, у вас есть большое количество <strong>Dag</strong>’ов, которые делают одно и то же, отличаясь лишь параметрами. Или вам нужен набор Dag’ов для загрузки таблиц, но вы не хотите вручную обновлять Dag’и каждый раз при изменении этих таблиц. В этих и других случаях может быть полезно динамически генерировать <strong>Dag</strong>’и.</p>
<p>Избегание избыточной обработки в коде верхнего уровня, описанное в предыдущей главе, особенно важно в случае динамической конфигурации Dag’ов, которая, по сути, может быть реализована одним из следующих способов:</p>
<ul>
<li>через переменные окружения (не путать с <strong>Airflow Variables</strong>)</li>
<li>через внешне предоставляемый, сгенерированный <strong>Python</strong>-код, содержащий метаданные в папке <strong>Dag</strong>’ов</li>
<li>через внешний, сгенерированный файл конфигурационных метаданных в папке <strong>Dag</strong>’ов</li>
</ul>
<p>Некоторые случаи динамической генерации Dag’ов описаны в разделе <a href="https://airflow.apache.org/docs/apache-airflow/stable/howto/dynamic-dag-generation.html" target="_blank" rel="noopener"><strong>Dynamic Dag Generation</strong></a>.</p>
<h3><strong>Переменные Airflow</strong></h3>
<p>Использование переменных <strong>Airflow</strong> приводит к сетевым вызовам и обращениям к базе данных, поэтому их применение в коде Python верхнего уровня для DAG-ов следует по возможности избегать, как упоминалось в предыдущей главе Python-код верхнего уровня. Если переменные Airflow всё же необходимо использовать в коде DAG верхнего уровня, их влияние на парсинг DAG можно снизить, включив экспериментальный кэш, настроенный с разумным значением <code>ttl</code>.</p>
<p>Вы можете свободно использовать переменные Airflow внутри методов <code>execute()</code> операторов, а также передавать переменные Airflow в существующие операторы через <strong>Jinja-шаблоны</strong>, что откладывает чтение значения до момента выполнения задачи. Синтаксис шаблона для этого следующий:</p><pre class="urvanov-syntax-highlighter-plain-tag">{{ var.value.&lt;variable_name&gt; }}</pre><p>или, если требуется десериализовать JSON-объект из переменной:</p><pre class="urvanov-syntax-highlighter-plain-tag">{{ var.json.&lt;variable_name&gt; }}</pre><p>В коде верхнего уровня переменные, использующие <strong>Jinja-шаблоны</strong>, не выполняют запрос до момента запуска задачи, тогда как <code>Variable.get()</code> выполняет запрос каждый раз, когда файл DAG парсится планировщиком, если кэширование не включено. Использование <code>Variable.get()</code> без включённого кэширования приводит к неоптимальной производительности при обработке файлов DAG.</p>
<p>В некоторых случаях это может привести к тому, что файл <strong>DAG</strong> не успеет полностью распарситься и произойдёт тайм-аут.</p>
<p><strong>Плохой пример:</strong></p><pre class="urvanov-syntax-highlighter-plain-tag">from airflow.sdk import Variable

foo_var = Variable.get("foo")  # AVOID THAT
bash_use_variable_bad_1 = BashOperator(
    task_id="bash_use_variable_bad_1", bash_command="echo variable foo=${foo_env}", env={"foo_env": foo_var}
)

bash_use_variable_bad_2 = BashOperator(
    task_id="bash_use_variable_bad_2",
    bash_command=f"echo variable foo=${Variable.get('foo')}",  # AVOID THAT
)

bash_use_variable_bad_3 = BashOperator(
    task_id="bash_use_variable_bad_3",
    bash_command="echo variable foo=${foo_env}",
    env={"foo_env": Variable.get("foo")},  # AVOID THAT
)</pre><p><strong>Хороший пример:</strong></p><pre class="urvanov-syntax-highlighter-plain-tag">bash_use_variable_good = BashOperator(
    task_id="bash_use_variable_good",
    bash_command="echo variable foo=${foo_env}",
    env={"foo_env": "{{ var.value.get('foo') }}"},
)

@task
def my_task():
    var = Variable.get("foo")  # This is ok since my_task is called only during task run, not during Dag scan.
    print(var)</pre><p>В целях безопасности рекомендуется использовать <strong>Secrets Backend</strong> для любых переменных, содержащих чувствительные данные.</p>
<h3><strong>Расписания (Timetables)</strong></h3>
<p>Избегайте использования <strong>переменных/подключений Airflow</strong> или обращения к базе данных <strong>Airflow</strong> на верхнем уровне кода расписаний. Доступ к базе данных должен быть отложен до момента выполнения DAG. Это означает, что не следует получать переменные/подключения в качестве аргументов при инициализации класса расписания, а также использовать <strong>Variable/Connection</strong> на верхнем уровне вашего пользовательского модуля расписания.</p>
<p><strong>Плохой пример:</strong></p><pre class="urvanov-syntax-highlighter-plain-tag">from airflow.sdk import Variable
from airflow.timetables.interval import CronDataIntervalTimetable


class CustomTimetable(CronDataIntervalTimetable):
    def __init__(self, *args, something=Variable.get("something"), **kwargs):
        self._something = something
        super().__init__(*args, **kwargs)</pre><p><strong>Хороший пример:</strong></p><pre class="urvanov-syntax-highlighter-plain-tag">from airflow.sdk import Variable
from airflow.timetables.interval import CronDataIntervalTimetable


class CustomTimetable(CronDataIntervalTimetable):
    def __init__(self, *args, something="something", **kwargs):
        self._something = Variable.get(something)
        super().__init__(*args, **kwargs)</pre><p></p>
<h3><strong>Запуск DAG-ов после изменений</strong></h3>
<p>Избегайте запуска <strong>DAG</strong>-ов сразу после их изменения или изменения любых сопутствующих файлов в папке DAG-ов.</p>
<p>Необходимо дать системе достаточно времени для обработки изменённых файлов. Этот процесс включает несколько этапов. Сначала файлы должны быть доставлены планировщику — обычно через распределённую файловую систему или <strong>Git-Sync</strong>, затем планировщик должен распарсить Python-файлы и сохранить их в базе данных. В зависимости от вашей конфигурации, скорости распределённой файловой системы, количества файлов, количества <strong>DAG</strong>-ов, числа изменений в файлах, размеров файлов, количества планировщиков, скорости <strong>CPU</strong>, этот процесс может занимать от нескольких секунд до нескольких минут, а в крайних случаях — многие минуты. Вам следует дождаться появления <strong>DAG</strong>-а в <strong>UI</strong>, прежде чем пытаться его запустить.</p>
<p>Если вы наблюдаете большие задержки между обновлением <strong>DAG</strong>-а и моментом, когда он становится доступен для запуска, вы можете обратить внимание на следующие параметры конфигурации и настроить их в соответствии с вашими потребностями (подробности по каждому параметру см. по ссылкам):</p>
<ul>
<li><code>scheduler_idle_sleep_time</code> &#8212; Управляет временем ожидания планировщика между циклами, но если в цикле ничего не нужно делать, то есть если что-то запланировано, то следующая итерация цикла начнется немедленно.</li>
<li><code>min_file_process_interval</code> &#8212; Количество секунд, по истечении которых происходит разбор DAG-файла. Разбор DAG-файла происходит каждые несколько секунд. Обновления DAG-файлов отражаются после этого интервала. Низкое значение этого параметра приведет к увеличению загрузки ЦП.</li>
<li><code>refresh_interval</code> &#8212; Как часто (в секундах) следует обновлять или искать новые файлы в пакете DAG.</li>
<li><code>parsing_processes</code> &#8212; Процессор DAG может запускать несколько процессов параллельно для анализа DAG. Это определяет, сколько процессов будет запущено.</li>
<li><code>file_parsing_sort_mode</code> &#8212; Один из вариантов <strong>modified_time</strong>, <strong>random_seeded_by_host</strong> и <strong>alphabetical</strong>. Процессор DAG перечислит и отсортирует файлы DAG, чтобы определить порядок их анализа.
<ul>
<li><code>modified_time</code> &#8212; Сортировка файлов по времени изменения. Это полезно в больших масштабах для предварительной обработки недавно измененных DAG-графов.</li>
<li><code>random_seeded_by_host</code> &#8212; Произвольная сортировка файлов несколькими процессорами DAG, но в одном и том же порядке на одном и том же хосте, что позволяет каждому процессору обрабатывать файлы в разном порядке.</li>
<li><code>alphabetical</code> &#8212; Сортировка по имени файла</li>
</ul>
</li>
</ul>
<h3><strong>Пример паттерна watcher с правилами триггеров</strong></h3>
<p>Паттерн <strong>watcher</strong> — это способ организации DAG-а с задачей, которая «наблюдает» за состояниями других задач. Его основное назначение — пометить запуск DAG-а как <strong>failed</strong>, если любая другая задача завершилась с ошибкой. Необходимость в этом возникла в системных тестах <strong>Airflow</strong>, которые представляют собой <strong>DAG</strong>-и с разными задачами (аналогично тесту, состоящему из шагов).</p>
<p>Обычно, когда любая задача завершается с ошибкой, все остальные задачи не выполняются, и весь запуск <strong>DAG</strong>-а также получает статус <strong>failed</strong>. Однако при использовании правил триггеров можно нарушить стандартный поток выполнения задач, и весь DAG может получить статус, отличный от ожидаемого. Например, можно иметь задачу очистки ресурсов (<strong>teardown task</strong>) с правилом триггера <code>TriggerRule.ALL_DONE</code>, которая будет выполняться независимо от состояния других задач (например, для освобождения ресурсов). В такой ситуации <strong>DAG</strong> всегда выполнит эту задачу, и запуск <strong>DAG</strong>-а получит статус именно этой задачи, в результате чего можно потерять информацию о задачах, завершившихся с ошибкой. Если требуется гарантировать, что <strong>DAG</strong> с задачей очистки завершится с ошибкой при падении любой задачи, необходимо использовать паттерн <strong>watcher</strong>.</p>
<p>Задача <strong>watcher</strong> — это задача, которая всегда завершается с ошибкой при выполнении, но она должна запускаться только в том случае, если любая другая задача завершилась с ошибкой. Для неё необходимо установить правило триггера <code>TriggerRule.ONE_FAILED</code>, а также сделать её <strong>downstream-задачей</strong> для всех остальных задач в <strong>DAG</strong>-е. Благодаря этому, если все остальные задачи завершатся <strong>успешно</strong>, watcher будет пропущена, а если произойдёт ошибка, задача <strong>watcher</strong> выполнится и завершится с ошибкой, что приведёт к статусу <strong>failed</strong> у всего запуска <strong>DAG</strong>-а.</p>
<p><strong>Примечание</strong></p>
<p>Следует учитывать, что правила триггеров опираются только на непосредственные upstream-задачи (родительские). Например, <code>TriggerRule.ONE_FAILED</code> будет игнорировать любые задачи со статусом <strong>failed</strong> (или <strong>upstream_failed</strong>), которые не являются прямыми родителями параметризуемой задачи.</p>
<p>Проще понять концепцию на примере. Предположим, у нас есть следующий DAG:</p><pre class="urvanov-syntax-highlighter-plain-tag">from datetime import datetime

from airflow.sdk import DAG
from airflow.sdk import task
from airflow.exceptions import AirflowException
from airflow.providers.standard.operators.bash import BashOperator
from airflow.utils.trigger_rule import TriggerRule


@task(trigger_rule=TriggerRule.ONE_FAILED, retries=0)
def watcher():
    raise AirflowException("Failing task because one or more upstream tasks failed.")


with DAG(
    dag_id="watcher_example",
    schedule="@once",
    start_date=datetime(2021, 1, 1),
    catchup=False,
) as dag:
    failing_task = BashOperator(task_id="failing_task", bash_command="exit 1", retries=0)
    passing_task = BashOperator(task_id="passing_task", bash_command="echo passing_task")
    teardown = BashOperator(
        task_id="teardown",
        bash_command="echo teardown",
        trigger_rule=TriggerRule.ALL_DONE,
    )

    failing_task &gt;&gt; passing_task &gt;&gt; teardown
    list(dag.tasks) &gt;&gt; watcher()</pre><p>Визуальное представление этого <strong>DAG</strong>-а после выполнения выглядит следующим образом:</p>
<p><a href="https://datatalks.ru/wp-content/uploads/2026/01/watcher_airflow_dag.png"><img fetchpriority="high" decoding="async" class="aligncenter size-full wp-image-2767" src="https://datatalks.ru/wp-content/uploads/2026/01/watcher_airflow_dag.png" alt="" width="893" height="119" srcset="https://datatalks.ru/wp-content/uploads/2026/01/watcher_airflow_dag.png 893w, https://datatalks.ru/wp-content/uploads/2026/01/watcher_airflow_dag-300x40.png 300w, https://datatalks.ru/wp-content/uploads/2026/01/watcher_airflow_dag-768x102.png 768w, https://datatalks.ru/wp-content/uploads/2026/01/watcher_airflow_dag-450x60.png 450w, https://datatalks.ru/wp-content/uploads/2026/01/watcher_airflow_dag-780x104.png 780w" sizes="(max-width: 893px) 100vw, 893px" /></a></p>
<p>В нём есть несколько задач, выполняющих разные роли:</p>
<ul>
<li><code>failing_task</code> — всегда завершается с ошибкой;</li>
<li><code>passing_task</code> — всегда завершается успешно (если выполняется);</li>
<li><code>teardown</code> — всегда запускается (независимо от состояний других задач) и должна всегда завершаться успешно;</li>
<li><code>watcher</code> — является <strong>downstream-задачей</strong> для всех остальных задач, то есть запускается, когда любая задача завершается с ошибкой, и тем самым переводит весь запуск <strong>DAG</strong>-а в состояние <strong>failed</strong>, так как является листовой задачей.</li>
</ul>
<p>Важно отметить, что без задачи <strong>watcher</strong> весь запуск <strong>DAG</strong>-а получит состояние <strong>success</strong>, поскольку единственная задача, завершающаяся с ошибкой, не является листовой, а задача <strong>teardown</strong> завершится успешно. Если мы хотим, чтобы <strong>watcher</strong> отслеживала состояние всех задач, необходимо сделать её зависимой от каждой из них по отдельности. Благодаря этому мы можем перевести запуск <strong>DAG</strong>-а в состояние <strong>failed</strong>, если любая из задач завершится с ошибкой. Обратите внимание, что для задачи watcher установлено правило триггера <strong>&#171;one_failed&#187;</strong>.</p>
<p>С другой стороны, без задачи <strong>teardown</strong> задача <strong>watcher</strong> не понадобилась бы, поскольку <strong>failing_task</strong> передала бы свой статус <strong>failed</strong> <strong>downstream-задаче</strong> <strong>passing_task</strong>, и весь запуск <strong>DAG</strong>-а также получил бы статус <strong>failed</strong>.</p>
<h3><strong>Использование исключения AirflowClusterPolicySkipDag в кластерных политиках для пропуска определённых DAG-ов</strong></h3>
<p><em>Добавлено в версии 2.7.</em></p>
<p><strong>DAG</strong>-и <strong>Airflow</strong> обычно разворачиваются и обновляются из конкретной ветки Git-репозитория с помощью <code>git-sync</code>. Однако, когда по операционным причинам требуется запускать несколько кластеров <strong>Airflow</strong>, поддержка нескольких <strong>Git-веток</strong> становится крайне неудобной. Особенно это усложняется, когда необходимо периодически синхронизировать две отдельные ветки (например, <strong>prod</strong> и <strong>beta</strong>) с использованием корректной стратегии ветвления.</p>
<ul>
<li><code>cherry-pick</code> слишком трудоёмок для сопровождения <strong>Git</strong>-репозитория;</li>
<li><code>hard-reset</code> не является рекомендуемым подходом в <strong>GitOps</strong>.</li>
</ul>
<p>Вместо этого можно рассмотреть вариант подключения нескольких кластеров Airflow к одной и той же ветке <strong>Git</strong> (например, main) и управления ими с помощью разных переменных окружения и различных конфигураций подключений с одинаковым connection_id. При необходимости также можно выбрасывать исключение <code>AirflowClusterPolicySkipDag</code> в кластерной политике, чтобы загружать определённые <strong>DAG</strong>-и в <strong>DagBag</strong> только в конкретном развертывании Airflow.</p><pre class="urvanov-syntax-highlighter-plain-tag">def dag_policy(dag: DAG):
    """Пропуск DAG-а с тегом `only_for_beta`."""

    if "only_for_beta" in dag.tags:
        raise AirflowClusterPolicySkipDag(
            f"Dag {dag.dag_id} is not loaded on the production cluster, due to `only_for_beta` tag."
        )</pre><p>Приведённый выше пример показывает фрагмент кода <strong>dag_policy</strong>, который пропускает DAG в зависимости от тегов, указанных у него.</p>
<h2><strong>Снижение сложности DAG-ов</strong></h2>
<p>Хотя Airflow хорошо справляется с обработкой большого количества DAG-ов с множеством задач и зависимостей между ними, при наличии большого числа сложных DAG-ов их сложность может негативно сказаться на производительности планирования. Одним из способов поддерживать высокую производительность и эффективное использование экземпляра Airflow является стремление к упрощению и оптимизации DAG-ов везде, где это возможно. Следует помнить, что процесс парсинга и создания DAG-а — это всего лишь выполнение Python-кода, и именно от вас зависит, насколько производительным он будет. Не существует «волшебных рецептов» для того, чтобы сделать DAG «менее сложным» — поскольку это Python-код, именно автор DAG-а контролирует сложность своего кода.</p>
<p>Не существует метрик «сложности DAG-а», и в частности нет метрик, которые могли бы однозначно сказать, является ли DAG «достаточно простым». Однако, как и в случае с любым Python-кодом, можно определить, что код DAG-а стал «проще» или «быстрее», если он оптимизирован. Если вы хотите оптимизировать свои DAG-и, можно предпринять следующие действия:</p>
<ul>
<li><strong>Сделайте загрузку DAG-а быстрее:</strong><br />
Это единственная рекомендация по улучшению, которая может быть реализована разными способами, но именно она оказывает наибольшее влияние на производительность планировщика. Если у вас есть возможность ускорить загрузку <strong>DAG</strong>-а — делайте это, если ваша цель — повышение производительности. Обратитесь к разделу <strong>Python</strong>-код верхнего уровня для получения советов, а также к <strong>Dag Loader Test</strong>, чтобы оценить время загрузки <strong>DAG</strong>-а.</li>
<li><strong>Генерируйте более простую структуру DAG-а:</strong><br />
Каждая зависимость между задачами добавляет дополнительную нагрузку на планирование и выполнение. <strong>DAG</strong> с простой линейной структурой <code>A → B → C</code> будет испытывать меньшие задержки при планировании задач, чем <strong>DAG</strong> с глубоко вложенной древовидной структурой, например с экспоненциально растущим числом зависимых задач. Если вы можете сделать свои DAG-и более линейными — так, чтобы в каждый момент времени было как можно меньше потенциальных задач-кандидатов на запуск, — это, как правило, улучшит общую производительность планирования.</li>
<li><strong>Уменьшите количество DAG-ов в одном файле:</strong><br />
Хотя <strong>Airflow 2</strong> оптимизирован для сценария, при котором в одном файле описано несколько DAG-ов, в системе есть компоненты, из-за которых такой подход иногда менее производителен или приводит к большим задержкам по сравнению с разбиением DAG-ов по нескольким файлам. Уже сам факт того, что один файл может быть обработан только одним <code>FileProcessor</code>, делает этот подход менее масштабируемым. Если у вас много DAG-ов, генерируемых из одного файла, рассмотрите возможность их разделения, особенно если вы замечаете, что изменения в файлах DAG-ов долго отражаются в <strong>UI Airflow</strong>.</li>
<li><strong>Пишите эффективный Python-код:</strong><br />
Необходимо соблюдать баланс между меньшим количеством DAG-ов в файле (как указано выше) и общим объёмом кода. Файлы <strong>Python</strong>, описывающие DAG-и, должны следовать лучшим практикам программирования и не должны рассматриваться как конфигурационные файлы. Если ваши DAG-и используют схожий код, не следует копировать его снова и снова в большое количество почти идентичных исходных файлов, так как это приведёт к ненужным повторным импортам одних и тех же ресурсов. Вместо этого следует стремиться к минимизации повторяющегося кода во всех DAG-ах, чтобы приложение работало эффективно и было проще в отладке.<br />
<em>См. раздел Dynamic Dag Generation о том, как создавать несколько DAG-ов с похожей логикой.</em></li>
</ul>
<h2>Тестирование DAG-а</h2>
<p>Пользователям Airflow следует относиться к DAG-ам как к коду промышленного уровня, и у DAG-ов должны быть различные связанные тесты, чтобы гарантировать получение ожидаемых результатов. Для DAG-а можно написать широкий спектр тестов. Рассмотрим некоторые из них.</p>
<h3><strong>Тест загрузки DAG-а (Dag Loader Test)</strong></h3>
<p>Этот тест должен гарантировать, что ваш DAG не содержит кода, который вызывает ошибку во время загрузки. Для запуска этого теста пользователю не требуется писать дополнительный код.</p><pre class="urvanov-syntax-highlighter-plain-tag">python your-dag-file.py</pre><p>Выполнение приведённой выше команды без ошибок гарантирует, что в DAG-е нет неустановленных зависимостей, синтаксических ошибок и т. д. Убедитесь, что вы загружаете DAG в окружении, соответствующем окружению планировщика — с теми же зависимостями, переменными окружения и общим кодом, на который ссылается DAG.</p>
<p>Это также отличный способ проверить, загружается ли DAG быстрее после оптимизации, если вы хотите попробовать оптимизировать время загрузки DAG-а. Просто запустите DAG и измерьте время его выполнения, но, опять же, необходимо убедиться, что DAG выполняется с теми же зависимостями, переменными окружения и общим кодом.</p>
<p>Существует множество способов измерить время выполнения, один из них в <strong>Linux</strong> — использование встроенной команды <code>time</code>. Обязательно запускайте её несколько раз подряд, чтобы учесть эффекты кэширования. Сравнивайте результаты до и после оптимизации (в одинаковых условиях — на той же машине, в том же окружении и т. д.), чтобы оценить влияние оптимизации.</p><pre class="urvanov-syntax-highlighter-plain-tag">time python airflow/example_dags/example_python_operator.py</pre><p><strong>Результат:</strong></p><pre class="urvanov-syntax-highlighter-plain-tag">real    0m0.699s
user    0m0.590s
sys     0m0.108s</pre><p>Важной метрикой является <strong>«real time»</strong>, которая показывает, сколько времени заняла обработка DAG-а. Обратите внимание, что при таком способе загрузки файла каждый раз запускается новый интерпретатор, поэтому присутствует начальное время инициализации, которого нет при парсинге DAG-а самим <strong>Airflow</strong>. Оценить время инициализации можно, выполнив:</p><pre class="urvanov-syntax-highlighter-plain-tag">time python -c ''</pre><p><strong>Результат:</strong></p><pre class="urvanov-syntax-highlighter-plain-tag">real    0m0.073s
user    0m0.037s
sys     0m0.039s</pre><p>В данном случае начальное время запуска интерпретатора составляет примерно ~0,07 с, что составляет около 10% времени, необходимого для парсинга <code>example_python_operator.py</code> выше, поэтому фактическое время парсинга для примера <strong>DAG</strong>-а составляет примерно ~0,62 с.</p>
<p><em>Подробности о том, как тестировать отдельные операторы, см. в разделе Testing a Dag.</em></p>
<h3>Юнит-тесты</h3>
<p><strong>Юнит-тесты</strong> гарантируют отсутствие некорректного кода в вашем DAG-е. Вы можете писать юнит-тесты как для отдельных задач, так и для самого DAG-а.</p>
<p>Юнит-тест загрузки DAG-а:</p><pre class="urvanov-syntax-highlighter-plain-tag">import pytest

from airflow.models import DagBag

@pytest.fixture()
def dagbag():
    return DagBag()

def test_dag_loaded(dagbag):
    dag = dagbag.get_dag(dag_id="hello_world")
    assert dagbag.import_errors == {}
    assert dag is not None
    assert len(dag.tasks) == 1</pre><p><strong>Юнит-тест структуры DAG-а:</strong></p>
<p>Это пример теста, предназначенного для проверки структуры DAG-а, сгенерированного кодом, путём сравнения с объектом типа <code>dict</code>.</p><pre class="urvanov-syntax-highlighter-plain-tag">def assert_dag_dict_equal(source, dag):
    assert dag.task_dict.keys() == source.keys()
    for task_id, downstream_list in source.items():
        assert dag.has_task(task_id)
        task = dag.get_task(task_id)
        assert task.downstream_task_ids == set(downstream_list)

def test_dag():
    assert_dag_dict_equal(
        {
            "DummyInstruction_0": ["DummyInstruction_1"],
            "DummyInstruction_1": ["DummyInstruction_2"],
            "DummyInstruction_2": ["DummyInstruction_3"],
            "DummyInstruction_3": [],
        },
        dag,
    )</pre><p><strong>Юнит-тест</strong> для пользовательского оператора:</p><pre class="urvanov-syntax-highlighter-plain-tag">import pendulum

from airflow.sdk import DAG, TaskInstanceState

def test_my_custom_operator_execute_no_trigger(dag):
    TEST_TASK_ID = "my_custom_operator_task"
    with DAG(
        dag_id="my_custom_operator_dag",
        schedule="@daily",
        start_date=pendulum.datetime(2021, 9, 13, tz="UTC"),
    ) as dag:
        MyCustomOperator(
            task_id=TEST_TASK_ID,
            prefix="s3://bucket/some/prefix",
        )

    dagrun = dag.test()
    ti = dagrun.get_task_instance(task_id=TEST_TASK_ID)
    assert ti.state == TaskInstanceState.SUCCESS
    # Assert something related to tasks results: ti.xcom_pull()</pre><p></p>
<h3><strong>Самопроверки (Self-Checks)</strong></h3>
<p>Вы также можете реализовать проверки непосредственно в DAG-е, чтобы убедиться, что задачи производят ожидаемые результаты. Например, если у вас есть задача, которая выгружает данные в <strong>S3</strong>, вы можете реализовать проверку в следующей задаче. Такая проверка, к примеру, может удостовериться, что партиция создана в <strong>S3</strong>, и выполнить простые проверки, чтобы определить корректность данных.</p>
<p>Аналогично, если у вас есть задача, которая запускает микросервис в <strong>Kubernetes</strong> или <strong>Mesos</strong>, следует проверить, был ли сервис успешно запущен, используя <code>airflow.providers.http.sensors.http.HttpSensor</code>.</p><pre class="urvanov-syntax-highlighter-plain-tag">task = PushToS3(...)
check = S3KeySensor(
    task_id="check_parquet_exists",
    bucket_key="s3://bucket/key/foo.parquet",
    poke_interval=0,
    timeout=0,
)
task &gt;&gt; check</pre><p></p>
<h3><strong>Staging-окружение</strong></h3>
<p>По возможности поддерживайте <strong>staging-окружение</strong> для тестирования полного выполнения DAG-а перед деплоем в <strong>production</strong>. Убедитесь, что ваш DAG параметризован и позволяет изменять переменные, например путь вывода при работе с <strong>S3</strong> или базу данных, используемую для чтения конфигурации. Не хардкодьте значения внутри DAG-а и не изменяйте их вручную в зависимости от окружения.</p>
<p>Для параметризации <strong>DAG</strong>-а вы можете использовать переменные окружения.</p><pre class="urvanov-syntax-highlighter-plain-tag">import os

dest = os.environ.get("MY_DAG_DEST_PATH", "s3://default-target/path/")</pre><p></p>
<h2>Мокирование переменных и подключений</h2>
<p>При написании тестов для кода, использующего <strong>переменные</strong> или <strong>подключения</strong>, необходимо убедиться, что они существуют во время выполнения тестов. Очевидное решение — сохранить эти объекты в базе данных, чтобы их можно было прочитать во время выполнения кода. Однако чтение и запись объектов в базу данных сопровождаются дополнительными временными затратами. Чтобы ускорить выполнение тестов, имеет смысл имитировать наличие этих объектов без сохранения их в базе данных. Для этого можно создать переменные окружения, замокировав <code>os.environ</code> с помощью <code>unittest.mock.patch.dict()</code>.</p>
<p>Для переменных используйте <code>AIRFLOW_VAR_{KEY}</code>.</p><pre class="urvanov-syntax-highlighter-plain-tag">with mock.patch.dict("os.environ", AIRFLOW_VAR_KEY="env-value"):
    assert "env-value" == Variable.get("key")</pre><p>Для подключений используйте <code>AIRFLOW_CONN_{CONN_ID}</code>.</p><pre class="urvanov-syntax-highlighter-plain-tag">conn = Connection(
    conn_type="gcpssh",
    login="cat",
    host="conn-host",
)
conn_uri = conn.get_uri()
with mock.patch.dict("os.environ", AIRFLOW_CONN_MY_CONN=conn_uri):
    assert "cat" == Connection.get_connection_from_secrets("my_conn").login</pre><p></p>
<h2>Обслуживание metadata DB</h2>
<p>Со временем база метаданных будет увеличивать занимаемое дисковое пространство по мере накопления запусков <strong>DAG</strong>-ов и задач, а также логов событий.</p>
<p>Для очистки старых данных можно использовать <strong>Airflow CLI</strong> с командой <code>airflow db clean</code>.</p>
<p>Подробности см. в разделе использования <strong>db clean</strong>.</p>
<h2>Обновления и откаты версий</h2>
<h3><strong>Резервное копирование базы данных</strong></h3>
<p>Всегда разумно делать резервную копию базы метаданных перед выполнением любых операций, изменяющих базу данных.</p>
<h3><strong>Отключение планировщика</strong></h3>
<p>Во время проведения такого обслуживания можно рассмотреть отключение <strong>кластера Airflow</strong>.</p>
<p>Один из способов — установить параметр <code>[scheduler] &gt; use_job_schedule</code> в значение <code>False</code> и дождаться завершения всех выполняющихся <strong>DAG</strong>-ов; после этого новые запуски DAG-ов не будут создаваться, если только они не будут запущены извне.</p>
<p>Лучший способ (хотя и более ручной) — использовать команду <code>dags pause</code>. Вам потребуется заранее зафиксировать список DAG-ов, которые не находятся в состоянии паузы, чтобы затем знать, какие из них нужно вернуть в активное состояние после завершения обслуживания. Сначала выполните <code>airflow dags list</code> и сохраните список не приостановленных <strong>DAG</strong>-ов. Затем используйте этот же список для выполнения <code>dags pause</code> для каждого DAG-а перед обслуживанием и <code>dags unpause</code> после его завершения. Преимущество такого подхода в том, что после обновления можно попробовать снять с паузы только один или два <strong>DAG</strong>-а (например, специальные тестовые <strong>DAG</strong>-и), чтобы убедиться, что всё работает корректно, прежде чем включать все <strong>DAG</strong>-и обратно.</p>
<h3><strong>Добавление DAG-ов для интеграционного тестирования</strong></h3>
<p>Полезно добавить несколько <strong>DAG</strong>-ов для <strong>«интеграционного тестирования»</strong>, которые используют все основные сервисы вашей экосистемы (например, <strong>S3</strong>, <strong>Snowflake</strong>, <strong>Vault</strong>), но с тестовыми ресурсами или «<strong>dev»-аккаунтами</strong>. Эти тестовые <strong>DAG</strong>-и можно запускать первыми после обновления, поскольку в случае их сбоя это не приведёт к негативным последствиям, и вы сможете откатиться к резервной копии. Если же они выполняются успешно, это подтвердит, что кластер способен выполнять задачи с использованием необходимых библиотек и сервисов.</p>
<p>Например, если вы используете внешний <strong>secrets backend</strong>, убедитесь, что у вас есть задача, которая извлекает подключение. Если вы используете <code>KubernetesPodOperator</code>, добавьте задачу, выполняющую <code>sleep 30; echo "hello"</code>. Если требуется запись в <strong>S3</strong> — реализуйте это в тестовой задаче. А если нужен доступ к базе данных, добавьте задачу, выполняющую <code>select 1</code> на сервере.</p>
<h3><strong>Очистка данных перед обновлением (Prune data)</strong></h3>
<p>Некоторые миграции базы данных могут занимать значительное время. Если база метаданных имеет очень большой размер, перед выполнением обновления стоит рассмотреть возможность очистки части старых данных с помощью команды <code>db clean</code>. Используйте с осторожностью.</p>
<h2>Работа с конфликтующими и сложными Python-зависимостями</h2>
<p><strong>Airflow</strong> имеет множество Python-зависимостей, и иногда зависимости <strong>Airflow</strong> конфликтуют с зависимостями, которые ожидает код ваших задач. Поскольку по умолчанию окружение <strong>Airflow</strong> представляет собой единый набор <strong>Python-зависимостей</strong> и одно <strong>Python-окружение</strong>, нередко возникают ситуации, когда разные задачи требуют различных зависимостей, которые при этом конфликтуют между собой.</p>
<p>Если вы используете предопределённые <strong>Operator</strong>’ы <strong>Airflow</strong> для взаимодействия с внешними сервисами, выбор обычно невелик, однако такие операторы, как правило, имеют зависимости, не конфликтующие с базовыми зависимостями <strong>Airflow</strong>. Airflow использует механизм <strong>constraints</strong>, что означает наличие «зафиксированного» набора зависимостей, с которым сообщество гарантирует корректную установку <strong>Airflow</strong> (включая все <strong>community-провайдеры</strong>) без возникновения конфликтов. При этом вы можете обновлять провайдеры независимо, и их <strong>constraints</strong> вас не ограничивают, поэтому вероятность конфликтов зависимостей ниже (хотя такие зависимости всё равно необходимо тестировать). Таким образом, при использовании предопределённых операторов вероятность столкнуться с конфликтующими зависимостями минимальна или отсутствует вовсе.</p>
<p>Однако при более «современном» подходе к использованию <strong>Airflow</strong> — когда вы применяете <strong>TaskFlow API</strong> и большинство операторов реализуете с помощью собственного Python-кода, либо когда вы пишете собственные <strong>Custom Operator</strong>’ы — вы можете столкнуться с ситуацией, когда зависимости, требуемые вашим кастомным кодом, конфликтуют с зависимостями Airflow, или даже когда зависимости нескольких ваших <strong>Custom Operator</strong>’ов конфликтуют между собой.</p>
<p>Существует несколько стратегий, которые можно использовать для смягчения этой проблемы. И хотя работа с конфликтами зависимостей в кастомных операторах может быть сложной, она значительно упрощается при использовании <code>airflow.providers.standard.operators.python.PythonVirtualenvOperator</code> или <code>airflow.providers.standard.operators.python.ExternalPythonOperator</code> — как при прямом использовании классического подхода с <strong>Operator</strong>’ами, так и при использовании задач, декорированных <code>@task.virtualenv</code> или <code>@task.external_python</code>, если вы применяете <strong>TaskFlow</strong>.</p>
<p>Начнём со стратегий, которые проще всего реализовать (хотя они имеют определённые ограничения и накладные расходы), и постепенно перейдём к стратегиям, требующим изменений в развертывании Airflow.</p>
<h3><strong>Использование PythonVirtualenvOperator</strong></h3>
<p>Это самая простая в использовании и одновременно наиболее ограниченная стратегия. <code>PythonVirtualenvOperator</code> позволяет динамически создавать <code>virtualenv</code>, в котором будет выполняться ваш <strong>Python-callable</strong>. В современном подходе <strong>TaskFlow</strong>, описанном в разделе Pythonic Dags with the <strong>TaskFlow API</strong>, это также можно сделать, задекорировав callable декоратором <code>@task.virtualenv</code> (рекомендуемый способ использования оператора). Каждая задача <code>airflow.providers.standard.operators.python.PythonVirtualenvOperator</code> может иметь собственный независимый <strong>Python</strong> <code>virtualenv</code> (динамически создаваемый при каждом запуске задачи) и задавать детальный набор зависимостей, которые необходимо установить для выполнения этой задачи.</p>
<p><strong>Оператор берёт на себя:</strong></p>
<ul>
<li>создание <code>virtualenv</code> на основе вашего окружения,</li>
<li>сериализацию вашего Python-callable и передачу его на выполнение Python-интерпретатору внутри <code>virtualenv</code>,</li>
<li>выполнение <code>callable</code>, получение результата и передачу его через <code>XCom</code>, если это указано.</li>
</ul>
<p><strong>Преимущества оператора:</strong></p>
<ul>
<li>Нет необходимости заранее подготавливать <code>virtualenv</code>. Он динамически создаётся перед запуском задачи и удаляется после её завершения, поэтому для использования нескольких виртуальных окружений не требуется ничего особенного (кроме наличия пакета <code>virtualenv</code> в зависимостях <strong>Airflow</strong>).</li>
<li>Вы можете запускать задачи с разными наборами зависимостей на одних и тех же воркерах — таким образом, ресурсы памяти переиспользуются (хотя см. ниже про накладные расходы на <strong>CPU</strong> при создании <code>virtualenv</code>).</li>
<li>В крупных инсталляциях авторам Dag’ов не нужно просить кого-то создавать <code>virtualenv</code> за них. Как автор <strong>Dag’а</strong>, вам достаточно иметь установленную зависимость <code>virtualenv</code>, и вы можете задавать и изменять окружения по своему усмотрению.</li>
<li>Не требуется изменений в требованиях к деплою — независимо от того, используете ли вы локальный <code>virtualenv</code>, <strong>Docker</strong> или <strong>Kubernetes</strong>, задачи будут работать без добавления чего-либо в окружение развертывания.</li>
<li>Автору Dag’ов не нужно изучать контейнеры или <strong>Kubernetes</strong>. Для такого подхода к написанию <strong>Dag’ов</strong> достаточно знания <strong>Python-зависимостей</strong>.</li>
</ul>
<p>У данного оператора есть определённые ограничения и накладные расходы:</p>
<ul>
<li>Ваш <strong>Python-callable</strong> должен быть сериализуемым. Существует множество Python-объектов, которые не сериализуются стандартной библиотекой <code>pickle</code>. Часть этих ограничений можно обойти с помощью библиотеки <code>dill</code>, однако и она не решает всех проблем сериализации.</li>
<li>Все зависимости, отсутствующие в окружении <strong>Airflow</strong>, должны импортироваться локально внутри используемого <code>callable</code>, а код верхнего уровня <strong>Dag</strong> не должен импортировать или использовать эти библиотеки.</li>
<li><code>Virtualenv</code> запускаются в рамках одной и той же операционной системы, поэтому они не могут иметь конфликтующие системные зависимости (устанавливаемые через <code>apt</code> или <code>yum</code>). Независимо могут устанавливаться только <strong>Python-зависимости</strong>.</li>
<li>Оператор добавляет накладные расходы на <strong>CPU</strong>, <strong>сеть</strong> и общее время выполнения каждой задачи — Airflow вынужден пересоздавать <code>virtualenv</code> с нуля для каждого запуска задачи.</li>
<li>Воркеры должны иметь доступ к <strong>PyPI</strong> или приватным репозиториям для установки зависимостей.</li>
<li>Динамическое создание <code>virtualenv</code> подвержено временным сбоям (например, если репозиторий недоступен или возникают сетевые проблемы при подключении к нему).</li>
<li>Легко попасть в ситуацию «слишком» динамичного окружения — устанавливаемые зависимости могут обновляться, а их транзитивные зависимости могут получать независимые обновления, в результате чего задача может перестать работать из-за выхода новой версии зависимости или вы можете стать жертвой атаки на цепочку поставок, когда новая версия зависимости оказывается вредоносной.</li>
<li>Задачи изолированы друг от друга только за счёт выполнения в разных окружениях. Это означает, что выполняющиеся задачи всё ещё могут влиять друг на друга — например, последующие задачи, выполняемые на том же воркере, могут быть затронуты предыдущими задачами, которые создавали или изменяли файлы и т. п.</li>
</ul>
<p>Подробные примеры использования <code>airflow.providers.standard.operators.python.PythonVirtualenvOperator</code> приведены в соответствующем разделе руководства по <strong>TaskFlow API</strong>.</p>
<h3><strong>Использование ExternalPythonOperator</strong></h3>
<p><em>Добавлено в версии 2.4.</em></p>
<p>Более сложным в использовании, но при этом значительно менее накладным с точки зрения ресурсов, безопасности и стабильности вариантом является использование <code>airflow.providers.standard.operators.python.ExternalPythonOperator</code>. В современном подходе TaskFlow, описанном в разделе <strong>Pythonic Dags with the TaskFlow API</strong>, этого также можно добиться, задекорировав ваш callable декоратором @task.external_python (рекомендуемый способ использования оператора). Однако для этого требуется заранее подготовленное, неизменяемое Python-окружение. В отличие от <code>airflow.providers.standard.operators.python.PythonVirtualenvOperator</code>, вы не можете добавлять новые зависимости в такое предсуществующее окружение. Все необходимые зависимости должны быть добавлены заранее и быть доступны на всех воркерах, если Airflow работает в распределённом окружении.</p>
<p>Таким образом, вы избегаете накладных расходов и проблем, связанных с пересозданием <code>virtualenv</code>, однако такие окружения необходимо подготовить и задеплоить вместе с установкой Airflow. Обычно в этот процесс вовлечены специалисты, отвечающие за установку Airflow, и в крупных инсталляциях это, как правило, другие люди, нежели авторы Dag’ов (DevOps/System Admins).</p>
<p>Такие <code>virtualenv</code> могут быть подготовлены разными способами: при использовании <code>LocalExecutor</code> их достаточно установить на машине, где запускается планировщик; при использовании распределённой установки <code>Celery</code> должна существовать пайплайн, который устанавливает эти <code>virtualenv</code> на нескольких машинах; наконец, если вы используете <strong>Docker-образы</strong> (например, в <strong>Kubernetes</strong>), создание <code>virtualenv</code> должно быть добавлено в пайплайн сборки вашего кастомного образа.</p>
<p><strong>Преимущества оператора:</strong></p>
<ul>
<li>Отсутствие накладных расходов при запуске задачи. <code>Virtualenv</code> уже готов в момент начала выполнения задачи.</li>
<li>Вы можете запускать задачи с разными наборами зависимостей на одних и тех же воркерах — таким образом, все ресурсы переиспользуются.</li>
<li>Воркерам не требуется доступ к <strong>PyPI</strong> или приватным репозиториям. Меньше вероятность временных сбоев, связанных с сетью.</li>
<li>Зависимости могут быть заранее проверены администраторами и командой безопасности, и никакой новый, неожиданный код не будет динамически добавляться. Это полезно как с точки зрения безопасности, так и стабильности.</li>
<li>Минимальное влияние на деплой — вам не нужно переходить на Docker-контейнеры или <strong>Kubernetes</strong>, чтобы эффективно использовать оператор.</li>
<li>Автору <strong>Dag</strong>’ов не нужно изучать контейнеры или <strong>Kubernetes</strong>. Для написания Dag’ов таким способом достаточно знания <strong>Python</strong> и работы с <strong>requirements</strong>.</li>
</ul>
<p><strong>Недостатки:</strong></p>
<ul>
<li>Окружения должны быть подготовлены заранее. Обычно это означает, что вы не можете менять их «на лету»: добавление новых зависимостей или изменение существующих требует как минимум повторного деплоя <strong>Airflow</strong>, а время итераций при разработке новых версий может увеличиться.</li>
<li>Ваш <strong>Python-callable</strong> должен быть сериализуемым. Существует множество <strong>Python-объектов</strong>, которые не сериализуются стандартной библиотекой <code>pickle</code>. Часть этих ограничений можно смягчить с помощью библиотеки <code>dill</code>, однако она также не решает всех проблем сериализации.</li>
<li>Все зависимости, отсутствующие в окружении Airflow, должны импортироваться локально внутри используемого callable, а код верхнего уровня Dag не должен импортировать или использовать эти библиотеки.</li>
<li>Virtualenv запускаются в рамках одной и той же операционной системы, поэтому они не могут иметь конфликтующие системные зависимости (устанавливаемые через apt или yum). Независимо могут устанавливаться только Python-зависимости.</li>
<li>Задачи изолированы друг от друга только за счёт выполнения в разных окружениях. Это означает, что выполняющиеся задачи всё ещё могут влиять друг на друга — например, последующие задачи, выполняемые на том же воркере, могут быть затронуты предыдущими задачами, которые создавали или изменяли файлы и т. п.</li>
</ul>
<p><code>PythonVirtualenvOperator</code> и <code>ExternalPythonOperator</code> можно рассматривать как взаимодополняющие инструменты, которые упрощают переход от этапа разработки к продакшену. Как автор Dag’ов, вы обычно будете итерироваться с зависимостями и разрабатывать Dag, используя <code>PythonVirtualenvOperator</code> (декорируя задачи <code>@task.virtualenv</code>), а после завершения итераций и внесения изменений, для продакшена, скорее всего, переключитесь на <code>ExternalPythonOperator</code> (и <code>@task.external_python</code>) после того, как команды <strong>DevOps/System Admin</strong> развернут новые зависимости в предсуществующих <code>virtualenv</code> в продакшене. Преимущество такого подхода в том, что вы в любой момент можете вернуть декоратор обратно и продолжить «динамическую» разработку с <code>PythonVirtualenvOperator</code>.</p>
<p>Подробные примеры использования <code>airflow.providers.standard.operators.python.ExternalPythonOperator</code> приведены в разделе TaskFlow External Python example.</p>
<h3><strong>Использование DockerOperator или KubernetesPodOperator</strong></h3>
<p>Ещё одной стратегией является использование <code>airflow.providers.docker.operators.docker.DockerOperator</code> и <code>airflow.providers.cncf.kubernetes.operators.pod.KubernetesPodOperator</code>. Для этого требуется, чтобы <strong>Airflow</strong> имел доступ к <strong>Docker Engine</strong> или кластеру <strong>Kubernetes</strong>.</p>
<p>Аналогично Python-операторам, декораторы <strong>TaskFlow</strong> удобны в случае, если вы хотите использовать эти операторы для выполнения вашего <strong>Python-callable</strong>.</p>
<p>Однако этот подход значительно сложнее — вам необходимо понимать, как работают<strong> Docker-контейнеры</strong> и <strong>Kubernetes Pod</strong>’ы, если вы хотите его использовать. Зато задачи полностью изолированы друг от друга, и вы даже не ограничены выполнением только Python-кода. Вы можете писать задачи на любом языке программирования. Кроме того, ваши зависимости полностью независимы от зависимостей <strong>Airflow</strong> (включая системные зависимости), поэтому если вашей задаче требуется принципиально иное окружение, это подходящий вариант.</p>
<p><em>Добавлено в версии 2.2:</em><br />
Начиная с версии <strong>Airflow 2.2</strong>, вы можете использовать декоратор <code>@task.docker</code> для запуска функций с помощью <strong>DockerOperator</strong>.</p>
<p><em>Добавлено в версии 2.4:</em><br />
Начиная с версии <strong>Airflow 2.2</strong>, вы можете использовать декоратор <code>@task.kubernetes</code> для запуска функций с помощью <code>KubernetesPodOperator</code>.</p>
<p>Преимущества использования этих операторов:</p>
<ul>
<li>Вы можете запускать задачи с разными наборами как <strong>Python-</strong>, так и системных зависимостей, а также задачи, написанные на совершенно другом языке программирования или даже под другую архитектуру процессора (<code>x86</code> vs. <code>arm</code>).</li>
<li>Окружение, в котором выполняются задачи, использует оптимизации и неизменяемость контейнеров. Похожие наборы зависимостей эффективно переиспользуют закешированные слои образов, поэтому окружение хорошо оптимизировано для случаев, когда у вас есть несколько похожих, но разных окружений.</li>
<li>Зависимости могут быть заранее проверены администраторами и командой безопасности, и никакой новый, неожиданный код не будет динамически добавляться. Это полезно как с точки зрения безопасности, так и стабильности.</li>
<li>Полная изоляция между задачами. Они не могут влиять друг на друга иначе, чем через стандартные механизмы <strong>Airflow XCom</strong>.</li>
</ul>
<p><strong>Недостатки</strong>:</p>
<ul>
<li>Существует накладной расход на запуск задач. Обычно он меньше, чем при динамическом создании <code>virtualenv</code>, но всё равно заметен (особенно для <code>KubernetesPodOperator</code>).</li>
<li>В случае использования декораторов <strong>TaskFlow</strong> весь вызываемый метод должен быть сериализован и передан в <strong>Docker-контейнер</strong> или <strong>Kubernetes Pod</strong>, при этом существуют системные ограничения на размер метода. Сериализация, передача и последующая десериализация на удалённой стороне также добавляют накладные расходы.</li>
<li>Присутствуют накладные расходы по ресурсам, связанные с необходимостью нескольких процессов. При использовании этих операторов для выполнения задач требуется как минимум два процесса: один процесс (в <strong>Docker-контейнере</strong> или <strong>Kubernetes Pod</strong>), выполняющий задачу, и процесс-наблюдатель в воркере <strong>Airflow</strong>, который отправляет задание в <strong>Docker/Kubernetes</strong> и отслеживает его выполнение.</li>
<li><strong>Контейнерные образы</strong> должны быть подготовлены заранее. Обычно это означает, что вы не можете изменять их «на лету». Добавление системных зависимостей, изменение или обновление <strong>Python-зависимостей</strong> требует пересборки и публикации образа (как правило, в приватном реестре). Время итераций при работе с новыми зависимостями обычно больше и требует от разработчика сборки и использования собственных образов во время разработки. Наличие корректного пайплайна деплоя здесь критически важно для надёжного сопровождения системы.</li>
<li>Если вы хотите запускать <strong>Python-callable</strong> через <strong>декораторы</strong>, он должен быть сериализуемым. Также в этом случае все зависимости, отсутствующие в окружении <strong>Airflow</strong>, должны импортироваться локально внутри используемого <strong>callable</strong>, а код верхнего уровня <strong>Dag</strong> не должен импортировать или использовать эти библиотеки.</li>
<li>Вам необходимо глубже понимать, как работают <strong>Docker-контейнеры</strong> или <strong>Kubernetes</strong>. Абстракции, предоставляемые этими технологиями, являются «протекающими», поэтому для написания Dag’ов с использованием этих операторов нужно разбираться в ресурсах, сетях, контейнерах и других аспектах.</li>
</ul>
<p>Подробные примеры использования <code>airflow.providers.docker.operators.docker.DockerOperator</code> приведены в разделе <strong>TaskFlow Docker example</strong>, а <code>airflow.providers.cncf.kubernetes.operators.pod.KubernetesPodOperator</code> — в разделе <strong>TaskFlow Kubernetes example</strong>.</p>
<h3><strong>Использование нескольких Docker-образов и очередей Celery</strong></h3>
<p>Существует возможность (хотя она требует глубокого понимания деплоя <strong>Airflow</strong>) запускать задачи Airflow с использованием нескольких независимых <strong>Docker-образов</strong>. Это можно реализовать путём назначения разных задач разным <strong>очередям (Queues)</strong> и настройки <strong>Celery-воркеров</strong> на использование разных образов для разных очередей. Однако такой подход (по крайней мере на данный момент) требует большого объёма ручной конфигурации деплоя и глубоких знаний того, как работают <strong>Airflow, Celery</strong> и <strong>Kubernetes</strong>. Кроме того, он вносит существенные накладные расходы при выполнении задач — снижается возможность переиспользования ресурсов, а также становится значительно сложнее точно настраивать стоимость потребляемых ресурсов без негативного влияния на производительность и стабильность.</p>
<p>Одним из возможных способов сделать этот подход более полезным является реализация <strong>AIP-46 (Runtime isolation for Airflow tasks and Dag parsing)</strong> и завершение <strong>AIP-43 (Dag Processor Separation)</strong>. До реализации этих инициатив преимуществ у данного подхода крайне мало, и он не рекомендуется к использованию.</p>
<p>Однако после реализации этих <strong>AIP</strong> откроется возможность более <strong>мультиарендного (multi-tenant) подхода</strong>, при котором несколько команд смогут иметь полностью изолированные наборы зависимостей, используемые на протяжении всего <strong>жизненного цикла Dag</strong> — от парсинга до выполнения.</p>
<h1>Создание пользовательского оператора (custom Operator)</h1>
<p>Airflow позволяет создавать новые операторы в соответствии с требованиями вас или вашей команды. Такая расширяемость — одна из ключевых возможностей, делающих Apache Airflow мощным инструментом.</p>
<p>Вы можете создать любой оператор, унаследовавшись от публичного базового класса SDK — BaseOperator.</p>
<p><strong>В производном классе необходимо переопределить два метода:</strong></p>
<ul>
<li><strong>Конструктор (__init__)</strong> — определить параметры, необходимые для оператора. Нужно указывать только аргументы, специфичные для вашего оператора. <code>default_args</code> можно задать в файле <code>Dag</code>.</li>
<li><strong>Execute</strong> — код, который будет выполнен при вызове оператора раннером. Метод принимает контекст <code>Airflow</code> в качестве параметра, который можно использовать для чтения конфигурационных значений.</li>
</ul>
<p><strong>Примечание</strong></p>
<p>При реализации пользовательских операторов не выполняйте ресурсоёмкие операции в методе <code>init</code>. Операторы создаются один раз за цикл планировщика для каждой задачи, которая их использует, и выполнение, например, запросов к базе данных может существенно замедлить планирование и привести к неэффективному использованию ресурсов.</p>
<p>Реализуем пример <code>HelloOperator</code> в новом файле <code>hello_operator.py</code>:</p><pre class="urvanov-syntax-highlighter-plain-tag">from airflow.sdk import BaseOperator


class HelloOperator(BaseOperator):
    def __init__(self, name: str, **kwargs) -&gt; None:
        super().__init__(**kwargs)
        self.name = name

    def execute(self, context):
        message = f"Hello {self.name}"
        print(message)
        return message</pre><p><strong>Примечание</strong></p>
<p>Чтобы импорты работали корректно, файл должен находиться в директории, присутствующей в переменной окружения <code>PYTHONPATH</code>. <strong>Airflow</strong> по умолчанию добавляет директории <code>dags/</code>, <code>plugins/</code> и <code>config/</code> из домашнего каталога <strong>Airflow</strong> в <code>PYTHONPATH</code>. В нашем примере файл размещён в директории <code>custom_operator/</code>.</p>
<p>Теперь вы можете использовать созданный пользовательский оператор следующим образом:</p><pre class="urvanov-syntax-highlighter-plain-tag">from custom_operator.hello_operator import HelloOperator

with dag:
    hello_task = HelloOperator(task_id="sample-task", name="foo_bar")</pre><p>Вы также можете продолжать использовать папку <code>plugins</code> для хранения пользовательских операторов. Если файл <code>hello_operator.py</code> находится в директории <strong>plugins</strong>, оператор можно импортировать следующим образом:</p><pre class="urvanov-syntax-highlighter-plain-tag">from hello_operator import HelloOperator</pre><p>Если оператор взаимодействует с внешним сервисом (API, база данных и т. п.), рекомендуется реализовать слой взаимодействия через <strong>Hooks</strong>. Это позволит повторно использовать реализованную логику в других операторах. Такой подход обеспечивает лучшее разделение ответственности и более эффективное использование интеграции по сравнению с созданием <code>CustomServiceBaseOperator</code> для каждого внешнего сервиса.</p>
<p>Ещё один аспект — временное состояние. Если операция требует хранения состояния в памяти (например, job id, который должен использоваться в методе on_kill для отмены запроса), это состояние должно храниться в операторе, а не в hook. Таким образом, hook сервиса остаётся полностью stateless, а вся логика операции сосредоточена в одном месте — в операторе.</p>
<h2>Hooks</h2>
<p><strong>Hooks</strong> выступают интерфейсом для взаимодействия с внешними общими ресурсами в Dag. Например, нескольким задачам в <strong>Dag</strong> может потребоваться доступ к базе данных <strong>MySQL</strong>. Вместо создания отдельного подключения для каждой задачи можно получить подключение через <strong>hook</strong> и использовать его повторно.</p>
<p><strong>Hook</strong> также помогает избежать хранения параметров аутентификации подключения непосредственно в <strong>Dag</strong>.</p>
<p>Расширим предыдущий пример и получим имя из MySQL:</p><pre class="urvanov-syntax-highlighter-plain-tag">class HelloDBOperator(BaseOperator):
    def __init__(self, name: str, mysql_conn_id: str, database: str, **kwargs) -&gt; None:
        super().__init__(**kwargs)
        self.name = name
        self.mysql_conn_id = mysql_conn_id
        self.database = database

    def execute(self, context):
        hook = MySqlHook(mysql_conn_id=self.mysql_conn_id, schema=self.database)
        sql = "select name from user"
        result = hook.get_first(sql)
        message = f"Hello {result['name']}"
        print(message)
        return message</pre><p>Когда оператор выполняет запрос через объект <strong>hook</strong>, создаётся новое подключение, если оно ещё не существует. <strong>Hook</strong> получает параметры аутентификации (например, имя пользователя и пароль) из <strong>backend</strong> Airflow и передаёт их в <code>airflow.hooks.base.BaseHook.get_connection()</code>.</p>
<p>Создавать <strong>hook</strong> следует только в методе <strong>execute</strong> или в методах, вызываемых из <strong>execute</strong>. Конструктор вызывается каждый раз при парсинге <strong>Dag</strong> (а это происходит часто), и создание <strong>hook</strong> в нём приведёт к множеству ненужных подключений к базе данных. Метод <strong>execute</strong> вызывается только во время запуска <strong>Dag</strong>.</p>
<h3>Пользовательский интерфейс</h3>
<p>Airflow позволяет разработчику управлять отображением оператора в интерфейсе Dag.</p>
<ul>
<li>Переопределите <code>ui_color</code>, чтобы изменить цвет фона оператора в <strong>UI</strong>.</li>
<li>Переопределите <code>ui_fgcolor</code>, чтобы изменить цвет текста.</li>
</ul>
<p>Переопределите <code>custom_operator_name</code>, чтобы изменить отображаемое имя (отличное от имени класса).</p><pre class="urvanov-syntax-highlighter-plain-tag">class HelloOperator(BaseOperator):
    ui_color = "#ff0000"
    ui_fgcolor = "#000000"
    custom_operator_name = "Howdy"
    # ...</pre><p></p>
<h3>Шаблонизация (Templating)</h3>
<p>Вы можете использовать шаблоны <strong>Jinja</strong> для параметризации оператора. <strong>Airflow</strong> применяет шаблонизацию к полям, указанным в <code>template_fields</code>, во время рендеринга оператора.</p><pre class="urvanov-syntax-highlighter-plain-tag">class HelloOperator(BaseOperator):
    template_fields: Sequence[str] = ("name",)

    def __init__(self, name: str, world: str, **kwargs) -&gt; None:
        super().__init__(**kwargs)
        self.name = name
        self.world = world

    def execute(self, context):
        message = f"Hello {self.world} it's {self.name}!"
        print(message)
        return message</pre><p>Использование шаблона:</p><pre class="urvanov-syntax-highlighter-plain-tag">with dag:
    hello_task = HelloOperator(
        task_id="task_id_1",
        name="{{ task_instance.task_id }}",
        world="Earth",
    )</pre><p>В этом примере <code>Jinja</code> найдёт параметр <code>name</code> и заменит <code>{{ task_instance.task_id }}</code> на <code>task_id_1</code>.</p>
<p>Параметр также может содержать имя файла, например <strong>bash-скрипта</strong> или <strong>SQL-файла</strong>. В этом случае нужно указать расширение файла в <strong>template_ext</strong>. Если поле из <strong>template_fields</strong> содержит строку, заканчивающуюся расширением из <strong>template_ext</strong>, <strong>Jinja</strong> прочитает содержимое файла и заменит шаблоны на реальные значения.</p>
<p><strong>Обратите внимание:</strong> <code>Jinja</code> подставляет значения в атрибуты оператора, а не в аргументы функции.</p><pre class="urvanov-syntax-highlighter-plain-tag">class HelloOperator(BaseOperator):
    template_fields: Sequence[str] = ("guest_name",)
    template_ext = ".sql"

    def __init__(self, name: str, **kwargs) -&gt; None:
        super().__init__(**kwargs)
        self.guest_name = name</pre><p>В этом примере <code>template_fields</code> должен быть <code>['guest_name']</code>, а не <code>['name']</code>.</p>
<p>Дополнительно вы можете указать <code>template_fields_renderers</code> — словарь, определяющий, в каком формате значение шаблонного поля будет отображаться в веб-интерфейсе. Например:</p><pre class="urvanov-syntax-highlighter-plain-tag">class MyRequestOperator(BaseOperator):
    template_fields: Sequence[str] = ("request_body",)
    template_fields_renderers = {"request_body": "json"}

    def __init__(self, request_body: str, **kwargs) -&gt; None:
        super().__init__(**kwargs)
        self.request_body = request_body</pre><p>В ситуации, когда <code>template_field</code> сам по себе является словарём, также можно указать путь к ключу через точку, чтобы извлекать и корректно отображать отдельные элементы. Например:</p><pre class="urvanov-syntax-highlighter-plain-tag">class MyConfigOperator(BaseOperator):
    template_fields: Sequence[str] = ("configuration",)
    template_fields_renderers = {
        "configuration": "json",
        "configuration.query.sql": "sql",
    }

    def __init__(self, configuration: dict, **kwargs) -&gt; None:
        super().__init__(**kwargs)
        self.configuration = configuration</pre><p>Использование этого шаблона:</p><pre class="urvanov-syntax-highlighter-plain-tag">with dag:
    config_task = MyConfigOperator(
        task_id="task_id_1",
        configuration={"query": {"job_id": "123", "sql": "select * from my_table"}},
    )</pre><p>В результате в <strong>UI</strong> поле <code>configuration</code> будет отображаться в формате <strong>JSON</strong>, а значение, находящееся по пути <code>configuration.query.sql</code>, будет подсвечено с использованием <strong>SQL-лексера</strong>.</p>
<p><a href="https://datatalks.ru/wp-content/uploads/2026/01/template_field_renderer_path.png"><img decoding="async" class="aligncenter size-full wp-image-2868" src="https://datatalks.ru/wp-content/uploads/2026/01/template_field_renderer_path.png" alt="" width="1274" height="574" srcset="https://datatalks.ru/wp-content/uploads/2026/01/template_field_renderer_path.png 1274w, https://datatalks.ru/wp-content/uploads/2026/01/template_field_renderer_path-300x135.png 300w, https://datatalks.ru/wp-content/uploads/2026/01/template_field_renderer_path-1024x461.png 1024w, https://datatalks.ru/wp-content/uploads/2026/01/template_field_renderer_path-768x346.png 768w, https://datatalks.ru/wp-content/uploads/2026/01/template_field_renderer_path-450x203.png 450w, https://datatalks.ru/wp-content/uploads/2026/01/template_field_renderer_path-780x351.png 780w" sizes="(max-width: 1274px) 100vw, 1274px" /></a></p>
<p>В настоящее время доступны следующие <strong>лексеры</strong>:</p>
<ul>
<li>bash</li>
<li>bash_command</li>
<li>doc</li>
<li>doc_json</li>
<li>doc_md</li>
<li>doc_rst</li>
<li>doc_yaml</li>
<li>doc_md</li>
<li>hql</li>
<li>html</li>
<li>jinja</li>
<li>json</li>
<li>md</li>
<li>mysql</li>
<li>postgresql</li>
<li>powershell</li>
<li>py</li>
<li>python_callable</li>
<li>rst</li>
<li>sql</li>
<li>tsql</li>
<li>yaml</li>
</ul>
<p>Если вы укажете несуществующий лексер, значение шаблонного поля будет отображено как красиво отформатированный (<strong>pretty-printed</strong>) объект.</p>
<h2>Ограничения</h2>
<p>Чтобы предотвратить неправильное использование, при определении и назначении шаблонизируемых полей в конструкторе оператора (если он определён, иначе — см. ниже) необходимо соблюдать следующие ограничения:</p>
<p><strong>1. Параметры конструктора, соответствующие шаблонным полям, должны называться точно так же, как и сами поля.</strong></p>
<p>Следующий пример некорректен, так как имя параметра конструктора не совпадает с именем шаблонного поля:</p><pre class="urvanov-syntax-highlighter-plain-tag">class HelloOperator(BaseOperator):
    template_fields = "foo"

    def __init__(self, foo_id) -&gt; None:  # должно быть def __init__(self, foo) -&gt; None
        self.foo = foo_id  # должно быть self.foo = foo</pre><p><strong>2. Атрибуты экземпляра, соответствующие шаблонным полям, должны быть явно присвоены из соответствующих параметров конструктора — либо напрямую, либо через вызов конструктора родительского класса (где эти поля определены как template_fields) с явной передачей параметров.</strong></p>
<p>Следующий пример некорректен, так как атрибут <code>self.foo</code> вообще не присваивается, несмотря на то, что он объявлен как шаблонное поле:</p><pre class="urvanov-syntax-highlighter-plain-tag">class HelloOperator(BaseOperator):
    template_fields = ("foo", "bar")

    def __init__(self, foo, bar) -&gt; None:
        self.bar = bar</pre><p>Следующий пример также некорректен, так как <code>self.foo</code> в <code>MyHelloOperator</code> инициализируется неявно через <code>kwargs</code>, переданные в конструктор родителя:</p><pre class="urvanov-syntax-highlighter-plain-tag">class HelloOperator(BaseOperator):
    template_fields = "foo"

    def __init__(self, foo) -&gt; None:
        self.foo = foo


class MyHelloOperator(HelloOperator):
    template_fields = ("foo", "bar")

    def __init__(self, bar, **kwargs) -&gt; None:  # должно быть def __init__(self, foo, bar, **kwargs)
        super().__init__(**kwargs)  # должно быть super().__init__(foo=foo, **kwargs)
        self.bar = bar</pre><p><strong>3. Нельзя применять преобразования к параметру при его присваивании в конструкторе.</strong></p>
<p>Любые действия над значением должны выполняться в методе <code>execute()</code>.</p>
<p>Следующий пример некорректен:</p><pre class="urvanov-syntax-highlighter-plain-tag">class HelloOperator(BaseOperator):
    template_fields = "foo"

    def __init__(self, foo) -&gt; None:
        self.foo = foo.lower()  # должно быть только self.foo = foo</pre><p>Если оператор наследуется от базового оператора и не определяет собственный конструктор, указанные ограничения не применяются. Однако шаблонные поля должны быть корректно определены в родительском классе с соблюдением этих правил.</p>
<p>Следующий пример корректен:</p><pre class="urvanov-syntax-highlighter-plain-tag">class HelloOperator(BaseOperator):
    template_fields = "foo"

    def __init__(self, foo) -&gt; None:
        self.foo = foo


class MyHelloOperator(HelloOperator):
    template_fields = "foo"</pre><p>Эти ограничения проверяются <code>pre-commit</code> <strong>hook’ом</strong> с именем <code>validate-operators-init</code>.</p>
<p>Добавление шаблонных полей через наследование</p>
<p>Распространённый сценарий создания пользовательского оператора — расширение уже существующих <code>template_fields</code>. Может возникнуть ситуация, когда нужный вам оператор не объявляет определённые параметры как шаблонные, но вы хотите передавать их динамически через <strong>Jinja-выражения</strong>. Это легко реализуется через простое наследование.</p>
<p>Предположим, у вас есть ранее определённый <code>HelloOperator</code>:</p><pre class="urvanov-syntax-highlighter-plain-tag">class HelloOperator(BaseOperator):
    template_fields: Sequence[str] = ("name",)

    def __init__(self, name: str, world: str, **kwargs) -&gt; None:
        super().__init__(**kwargs)
        self.name = name
        self.world = world

    def execute(self, context):
        message = f"Hello {self.world} it's {self.name}!"
        print(message)
        return message</pre><p>Допустим, вы хотите динамически параметризовать аргумент <strong>world</strong>.</p>
<p>Поскольку <code>template_fields</code> гарантированно является <code>Sequence[str]</code> (списком или кортежем строк), можно легко создать подкласс и расширить список шаблонных полей:</p><pre class="urvanov-syntax-highlighter-plain-tag">class MyHelloOperator(HelloOperator):
    template_fields: Sequence[str] = (*HelloOperator.template_fields, "world")</pre><p>Теперь можно использовать <code>MyHelloOperator</code> следующим образом:</p><pre class="urvanov-syntax-highlighter-plain-tag">with dag:
    hello_task = MyHelloOperator(
        task_id="task_id_1",
        name="{{ task_instance.task_id }}",
        world="{{ var.value.my_world }}",
    )</pre><p>В этом примере аргумент <strong>world</strong> будет динамически установлен в значение переменной <strong>Airflow</strong> с именем <strong>my_world</strong> через <strong>Jinja-выражение</strong>.</p>
<h2>Определение дополнительной ссылки (Extra Link) для оператора</h2>
<p>Для своего оператора вы можете определить <strong>дополнительную ссылку (extra link)</strong>, которая будет перенаправлять пользователей во внешние системы. Например, можно добавить ссылку, ведущую на документацию или руководство по использованию оператора.</p>
<h2>Sensors</h2>
<p><strong>Airflow</strong> предоставляет специальный тип оператора — <strong>Sensor</strong>, предназначенный для регулярной проверки (<strong>polling</strong>) некоторого состояния (например, наличия файла) до тех пор, пока не будет выполнено условие успешного завершения.</p>
<p>Вы можете создать собственный сенсор, унаследовавшись от <code>airflow.sensors.base.BaseSensorOperator</code> и реализовав метод <code>poke</code>, который будет опрашивать внешнее состояние и проверять критерий успешности.</p>
<h3>Режим reschedule</h3>
<p>У сенсоров есть мощная возможность — режим <code>reschedule</code>, который позволяет задаче сенсора быть перепланированной, вместо того чтобы занимать слот воркера между проверками.</p>
<p>Это полезно, если:</p>
<ul>
<li>вы можете позволить себе более длинный интервал опроса,</li>
<li>ожидается длительное ожидание выполнения условия.</li>
</ul>
<h3>Ограничение режима reschedule</h3>
<p>Режим <code>reschedule</code> имеет важное ограничение: сенсор не может сохранять внутреннее состояние между перепланированными запусками.</p>
<p>Если ваш сенсор хранит внутреннее состояние, его следует декорировать с помощью <code>airflow.sensors.base.poke_mode_only()</code>. Это даст пользователям понять, что сенсор не подходит для использования в режиме <code>reschedule</code>.</p>
<h3>Пример сенсора с внутренним состоянием</h3>
<p>Примером сенсора, который хранит внутреннее состояние и не может использоваться в режиме reschedule, является:</p>
<p><code>airflow.providers.google.cloud.sensors.gcs.GCSUploadSessionCompleteSensor</code></p>
<p>Этот сенсор:</p>
<ul>
<li>опрашивает количество объектов по заданному префиксу (это количество является его внутренним состоянием),</li>
<li>считается успешно завершённым, если в течение определённого времени количество объектов не меняется.</li>
</ul>
<p>Сообщение <a href="https://datatalks.ru/best-practices-airflow-3-documentation/">Best Practices &#8212; Airflow 3 Документация</a> появились сначала на <a href="https://datatalks.ru">DataTalks.RU. Data Engineering / DWH / Data Pipeline</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://datatalks.ru/best-practices-airflow-3-documentation/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Airflow Best Practices Перевод 3 главы &#171;Компоненты Airflow&#187;</title>
		<link>https://datatalks.ru/airflow-best-practices-chapter-3-components-of-airflow/</link>
					<comments>https://datatalks.ru/airflow-best-practices-chapter-3-components-of-airflow/#respond</comments>
		
		<dc:creator><![CDATA[Data Engineer (Admin)]]></dc:creator>
		<pubDate>Sun, 20 Jul 2025 08:42:50 +0000</pubDate>
				<category><![CDATA[Apache Airflow Best Practices]]></category>
		<category><![CDATA[Apache Airflow]]></category>
		<category><![CDATA[CeleryExecutor]]></category>
		<category><![CDATA[CeleryKubernetesExecutor]]></category>
		<category><![CDATA[Components of Airflow]]></category>
		<category><![CDATA[Dask Executor]]></category>
		<category><![CDATA[Kubernetes Executor]]></category>
		<category><![CDATA[LocalExecutor]]></category>
		<category><![CDATA[LocalKubernetes Executor]]></category>
		<category><![CDATA[SequentialExecutor]]></category>
		<category><![CDATA[Компоненты Airflow]]></category>
		<guid isPermaLink="false">https://datatalks.ru/?p=1871</guid>

					<description><![CDATA[<p>Перевод книги &#171;Apache Airflow Best Practices, by Dylan Intorf, Dylan Storey, Kendrick van Doorn&#187; Packt Publishing подготовлен автором сайта Глава 3 – Компоненты Airflow Apache Airflow — это распределённая система с несколькими компонентами. Хотя распределённые системы по своей сути являются сложными, сами компоненты относительно просты. Важно понимать конкретную роль каждого компонента и ту роль, которую [&#8230;]</p>
<p>Сообщение <a href="https://datatalks.ru/airflow-best-practices-chapter-3-components-of-airflow/">Airflow Best Practices Перевод 3 главы &#171;Компоненты Airflow&#187;</a> появились сначала на <a href="https://datatalks.ru">DataTalks.RU. Data Engineering / DWH / Data Pipeline</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><em>Перевод книги &#171;Apache Airflow Best Practices, by Dylan Intorf, Dylan Storey, Kendrick van Doorn&#187; Packt Publishing подготовлен автором сайта</em></p>
<h1>Глава 3 – Компоненты Airflow</h1>
<p><strong>Apache Airflow</strong> — это распределённая система с несколькими компонентами. Хотя распределённые системы по своей сути являются сложными, сами компоненты относительно просты. Важно понимать конкретную роль каждого компонента и ту роль, которую они играют в работе приложения Airflow. Понимание их настройки, работы и обслуживания поможет вам уверенно масштабироваться и стать экспертами в эксплуатации сред Airflow в продуктиве. В этой главе вы узнаете об ответственности и возможностях каждого компонента в рамках общего приложения, о том, как выбирать определённые конфигурации для конкретных компонентов и как определить, какие возможности вам понадобятся для достижения ваших бизнес-целей.</p>
<p>Важно сосредоточиться на понимании таких базовых компонентов, поскольку часто можно найти возможности для оптимизации и скрытого ресурса, когда общее количество задач и заданий, которые координирует Airflow, увеличивается.</p>
<p><strong>В этой главе мы рассмотрим следующие основные темы:</strong></p>
<ul>
<li>Общая архитектура и ключевые компоненты</li>
<li>Какой Executor подходит для различных сценариев</li>
<li>Подробный взгляд на оптимизацию планировщика (Scheduler)</li>
</ul>
<h1>Технические требования</h1>
<p>Как и в предыдущих главах, мы предполагаем, что у вас уже настроена среда Apache Airflow на вашем локальном компьютере, и вы знаете, как получить к ней доступ — будь то через пользовательский интерфейс или через интерфейс командной строки. Если вы не выполнили эти шаги, рекомендуем обратиться к предыдущим главам или перейти к руководству по быстрому старту (Quick Start), поддерживаемому сообществом с открытым исходным кодом, для получения самой актуальной информации.</p>
<p>Общее понимание таких понятий, как архитектура распределённого программного обеспечения, Kubernetes и проектирование систем, необходимо для получения максимальной пользы от этой информации.</p>
<h1>Общая архитектура</h1>
<p>Apache Airflow в своей основе представляет собой набор компонентов, работающих вместе, позволяющих вам строить и запускать рабочие процессы или ориентированные ациклические графы (DAG). Эти рабочие процессы выполняются поверх нескольких микросервисов, которые координируют выполнение задач рабочими по заданному расписанию.</p>
<p>Архитектура Apache Airflow включает в себя несколько ключевых компонентов, которые работают совместно для эффективной оркестрации пайплайнов данных. Основные компоненты включают:</p>
<ul>
<li><strong>Metadata Database (База метаданных):</strong> Хранит метаданные, связанные с запусками DAG, статусами экземпляров задач и другими ключевыми метаданными. Позволяет вашей инстанции Airflow отслеживать состояния задач, версии DAG и обеспечивает устойчивость данных.</li>
<li><strong>Scheduler (Планировщик):</strong> Отвечает за запуск экземпляров задач на основе заданного времени или внешнего триггера. Постоянно проверяет DAG’и, чтобы определить, можно ли их запустить.</li>
<li><strong>Triggerer:</strong> Отвечает за хранение и выполнение асинхронных функций, создаваемых из классов Trigger.</li>
<li><strong>Executor (Исполнитель):</strong> Определяет, как задачи будут выполняться в Airflow.</li>
<li><strong>Workers (Рабочие процессы):</strong> Подхватывают задачи, назначенные к выполнению, и непосредственно отвечают за &#171;выполнение работы&#187;.</li>
<li><strong>DAG Directory (Каталог DAG):</strong> Место, где Airflow ищет Python-файлы с определениями DAG. Используется многими компонентами через файловую систему.</li>
<li><strong>Web Server (Веб-сервер):</strong> Обеспечивает веб-интерфейс для Apache Airflow.</li>
<li><strong>User Interface (Пользовательский интерфейс):</strong> Через веб-сервер UI позволяет пользователям отслеживать DAG, просматривать успешные и неудачные выполнения задач, просматривать логи и управлять средой Airflow.</li>
</ul>
<p>Каждый из этих компонентов выполняет уникальную роль в экосистеме Airflow, совместно обеспечивая гладкую оркестрацию и управление сложными рабочими процессами данных. Понимание этих основных элементов даёт прочную основу для работы с архитектурой Apache Airflow.</p>
<p><strong>Рисунок 3.1: Общая архитектура компонентов Apache Airflow</strong></p>
<p><a href="https://datatalks.ru/wp-content/uploads/2025/07/image_3_1_components_airflow.jpeg"><img decoding="async" class="aligncenter size-full wp-image-1917" src="https://datatalks.ru/wp-content/uploads/2025/07/image_3_1_components_airflow.jpeg" alt="" width="1164" height="939" srcset="https://datatalks.ru/wp-content/uploads/2025/07/image_3_1_components_airflow.jpeg 1164w, https://datatalks.ru/wp-content/uploads/2025/07/image_3_1_components_airflow-300x242.jpeg 300w, https://datatalks.ru/wp-content/uploads/2025/07/image_3_1_components_airflow-1024x826.jpeg 1024w, https://datatalks.ru/wp-content/uploads/2025/07/image_3_1_components_airflow-768x620.jpeg 768w, https://datatalks.ru/wp-content/uploads/2025/07/image_3_1_components_airflow-450x363.jpeg 450w, https://datatalks.ru/wp-content/uploads/2025/07/image_3_1_components_airflow-780x629.jpeg 780w" sizes="(max-width: 1164px) 100vw, 1164px" /></a></p>
<p><strong>Веб-сервер, планировщик (scheduler)</strong> и <strong>исполнитель (executor)</strong> являются процессами Airflow.</p>
<p><strong>База метаданных (Metadata Database) или просто база данных</strong> — это отдельный сервис, который должен быть предоставлен Airflow для хранения метаданных от веб-сервера и планировщика. Каталог DAG или папка dags должна быть доступна для планировщика и часто включается в ту же рабочую директорию.</p>
<p>Веб-сервер визуально отображает информацию о текущем состоянии DAG’ов и пайплайнов, а также предоставляет пользователю возможность просматривать ключевую информацию в различных представлениях и вручную запускать DAG’и. Планировщик служит для парсинга файлов DAG из каталога DAG и определения задач для выполнения, после чего помещает их в очередь.</p>
<p>Для выполнения этих задач из очереди доступно несколько вариантов на выбор, в зависимости от бизнес-целей и требований. Apache Airflow может быть установлен и запущен по-разному: на локальной машине, на одном сервере или в распределённой сети из нескольких машин. Каждый из этих подходов предоставляет разные преимущества, уровень сложности и требует разного исполнителя (executor).</p>
<h2>Исполнители (Executors)</h2>
<p>Исполнители определяют, как экземпляры задач будут выполняться в среде Airflow. Они являются подключаемыми (pluggable), что позволяет командам менять исполнителей в зависимости от своих бизнес-целей и потребностей. Каждая среда Airflow может быть настроена только с одним исполнителем одновременно, и он может быть изменён в конфигурационном файле.</p>
<p>Вы можете увидеть исполнителей с названиями, такими как SequentialExecutor, в официальной документации и конфигурационных файлах. В этой главе мы будем разделять слова для удобства чтения и ссылаться на них по типу, например, Sequential вместо SequentialExecutor.</p>
<p>На момент написания доступно несколько типов исполнителей, и сообщество продолжает расширять доступные опции. Таблица 4.1 содержит список исполнителей, которые доступны в настоящее время, а также тех, которые были устаревшими. В следующей таблице мы рассмотрим дополнительные детали по наиболее распространённым исполнителям и лучшие варианты использования.</p>
<p><strong>Таблица 3.1: Описание некоторых распространённых исполнителей, доступных в настоящее время для выполнения задач, их сложность и варианты использования в эксплуатации.</strong></p>
<table>
<thead>
<tr>
<th><strong>Executor</strong></th>
<th><strong>Удалённое выполнение</strong></th>
<th><strong>Параллелизм</strong></th>
<th><strong>Сложность установки и поддержки</strong></th>
<th><strong>Сценарии использования</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>SequentialExecutor</td>
<td>Нет</td>
<td>Нет</td>
<td>Очень простая</td>
<td>Демонстрация/тестирование</td>
</tr>
<tr>
<td>LocalExecutor</td>
<td>Нет</td>
<td>Да</td>
<td>Простой</td>
<td>Одна машина или среда разработки</td>
</tr>
<tr>
<td>CeleryExecutor</td>
<td>Да</td>
<td>Да</td>
<td>Средняя</td>
<td>Масштабирование на несколько машин и воркеров</td>
</tr>
<tr>
<td>CeleryKubernetesExecutor</td>
<td>Да</td>
<td>Да</td>
<td>Сложная</td>
<td>Как CeleryExecutor, но справляется с высокими нагрузками в пиковое время и обеспечивает изоляцию выполнения, как у KubernetesExecutor</td>
</tr>
<tr>
<td>Dask Executor</td>
<td>Да</td>
<td>Да</td>
<td>Средняя</td>
<td>Параллельные вычисления в распределённой архитектуре</td>
</tr>
<tr>
<td>Kubernetes Executor</td>
<td>Да</td>
<td>Да</td>
<td>Сложная</td>
<td>Масштабирование и запуск каждой задачи в отдельном pod&#8217;е Kubernetes-кластера</td>
</tr>
<tr>
<td>LocalKubernetes Executor</td>
<td>Да</td>
<td>Да</td>
<td>Сложная</td>
<td>Преимущества KubernetesExecutor с возможностью выполнения задач через LocalExecutor внутри сервера планировщика</td>
</tr>
</tbody>
</table>
<p>Давайте рассмотрим примеры каждого типа и посмотрим на наилучшие применения каждого из них.</p>
<h2>Локальные исполнители (Sequential и Local)</h2>
<p>Если вы следовали инструкциям по быстрому старту для установки Apache Airflow и не вносили никаких изменений, по умолчанию используется исполнитель Sequential. Это единственный исполнитель, который может использоваться с SQLite, так как SQLite не поддерживает множественные подключения.</p>
<p>Sequential Executor работает, как следует из его названия: задачи выполняются последовательно, в логическом порядке. Локальные установки Apache Airflow часто имеют одного рабочего, поскольку Sequential Executor ограничивает выполнение одной задачи за раз.</p>
<p><strong>Рисунок 3.2: Набор из трёх задач из примера базового DAG</strong></p>
<p><a href="https://datatalks.ru/wp-content/uploads/2025/07/image_3_2_dag_example.jpeg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-1918" src="https://datatalks.ru/wp-content/uploads/2025/07/image_3_2_dag_example.jpeg" alt="" width="1008" height="188" srcset="https://datatalks.ru/wp-content/uploads/2025/07/image_3_2_dag_example.jpeg 1008w, https://datatalks.ru/wp-content/uploads/2025/07/image_3_2_dag_example-300x56.jpeg 300w, https://datatalks.ru/wp-content/uploads/2025/07/image_3_2_dag_example-768x143.jpeg 768w, https://datatalks.ru/wp-content/uploads/2025/07/image_3_2_dag_example-450x84.jpeg 450w, https://datatalks.ru/wp-content/uploads/2025/07/image_3_2_dag_example-780x145.jpeg 780w" sizes="(max-width: 1008px) 100vw, 1008px" /></a></p>
<p>Обратимся к базовому примеру DAG, рассмотренному в предыдущей главе — Sequential Executor является идеальным вариантом использования. DAG не является сложным, и каждая задача должна быть выполнена по порядку перед переходом к следующей. Sequential Executor — отличный инструмент для запуска примерных DAG’ов и рабочих нагрузок на локальной машине. По мере увеличения количества необходимых рабочих узлов производительность локальной машины будет снижаться из-за использования ресурсов.</p>
<p>Следующим шагом после Sequential Executor является Local Executor, который запускает экземпляры задач параллельно на той же машине, где работает планировщик. Он использует модуль multiprocessing Python для создания нескольких процессов, позволяя выполнять задачи параллельно. Чтобы изменить Sequential Executor на Local Executor, необходимо обновить конфигурационный файл (airflow.cfg), установив поле executor в значение LocalExecutor.</p><pre class="urvanov-syntax-highlighter-plain-tag"># в конфигурационном файле
executor = LocalExecutor</pre><p>Если вы хотите проверить, какой исполнитель используется в просматриваемой среде Airflow, вы можете выполнить следующую команду в интерфейсе командной строки:</p><pre class="urvanov-syntax-highlighter-plain-tag">$ airflow config get-value core executor</pre><p>Основные варианты использования Local Executor:</p>
<ul>
<li><strong>Среда разработки:</strong> благодаря своей простоте и отсутствию внешних зависимостей, Local Executor часто используется в средах разработки. Разработчики могут уверенно запускать DAG’и и задачи без необходимости в более сложных исполнителях.</li>
<li><strong>Малые и средние рабочие нагрузки:</strong> для рабочих сред с ограниченными требованиями к параллельности (в среднем менее пяти одновременных DAG’ов/задач) или слабыми SLA, Local Executor может быть достаточным решением для команды.</li>
</ul>
<p>Local Executor предлагает множество преимуществ по сравнению с Sequential Executor и удалёнными исполнителями:</p>
<ul>
<li><strong>Параллельность:</strong> этот исполнитель позволяет запускать несколько экземпляров задач одновременно. Уровень параллельности определяет, сколько задач может быть выполнено одновременно в данный момент времени. Этот инструмент крайне полезен при работе с длительными задачами. Параллельность — это параметр конфигурации, который можно настроить до максимального значения, поддерживаемого машиной, на которой работает Local Executor.</li>
<li><strong>Простота:</strong> Local Executor прост в использовании и не требует настройки дополнительных компонентов инфраструктуры, таких как брокеры сообщений для Celery Executor или кластер Kubernetes для Kubernetes Executor. Мы рассмотрим оба этих исполнителя позже в этой главе. Это делает его проще в настройке, особенно для новых пользователей или небольших установок.</li>
<li><strong>Локальная разработка:</strong> он предоставляет более реалистичную среду для тестирования по сравнению с Sequential Executor, который выполняет задачи по одной. Разработчики могут тестировать параллельное выполнение задач без необходимости обслуживания и настройки более сложных исполнителей.</li>
<li><strong>Использование ресурсов:</strong> так как он запускает задачи на той же машине, что и планировщик, он подходит для сценариев, где вы хотите максимально использовать ресурсы машины без распределения задач между различными узлами или контейнерами.</li>
<li><strong>Низкие издержки:</strong> отсутствие внешних систем для отправки задач на выполнение снижает сетевую задержку, и нет дополнительных систем, которые нужно мониторить или обслуживать.</li>
<li><strong>Переход к продакшену:</strong> для небольших и средних развёртываний Airflow переход от среды разработки с использованием Local Executor к производственной среде с тем же исполнителем происходит легко.</li>
</ul>
<p>Однако важно отметить ограничения Local Executor. Как следует из названия, задачи выполняются &#171;локально&#187;, поэтому, если рабочие процессы требуют значительной параллельности или есть необходимость распределить нагрузку выполнения между несколькими машинами для масштабируемости или отказоустойчивости, то могут подойти другие исполнители.</p>
<h2>Параллелизм</h2>
<p>Давайте подробнее рассмотрим тему параллелизма и то, насколько он эффективен для выполнения экземпляров задач. Local Executor предоставляет возможность выполнять несколько экземпляров задач одновременно. По сравнению с Sequential Executor, который ограничивается одной задачей за раз, параллелизм может значительно ускорить процесс. Например, на следующем изображении мы можем визуализировать, как Sequential Executor может выполнять три отдельные задачи. Каждая задача обозначена разным цветом и не зависит от других для выполнения.</p>
<p><strong>Рисунок 3.3: Визуализация одного рабочего, выполняющего три задачи с течением времени</strong></p>
<p><a href="https://datatalks.ru/wp-content/uploads/2025/07/image_3_3_airflow_single_worker_executing.jpeg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-1919" src="https://datatalks.ru/wp-content/uploads/2025/07/image_3_3_airflow_single_worker_executing.jpeg" alt="" width="731" height="470" srcset="https://datatalks.ru/wp-content/uploads/2025/07/image_3_3_airflow_single_worker_executing.jpeg 731w, https://datatalks.ru/wp-content/uploads/2025/07/image_3_3_airflow_single_worker_executing-300x193.jpeg 300w, https://datatalks.ru/wp-content/uploads/2025/07/image_3_3_airflow_single_worker_executing-450x289.jpeg 450w" sizes="(max-width: 731px) 100vw, 731px" /></a></p>
<p>Sequential Executor возьмёт первую задачу, зелёную, на выполнение, требующее трёх циклов для завершения. Хотя вторая задача, синяя, не зависит от зелёной для начала выполнения, она должна ждать завершения зелёной задачи, поскольку имеется только один доступный рабочий. Третья задача, фиолетовая, проходит тот же процесс ожидания завершения первой и второй задач перед началом выполнения. Этот процесс может быть приемлем для некоторых бизнес-кейсов и представляет собой быстрый способ начать работу.</p>
<p>Local Executor предлагает возможность реализации многопроцессности или параллелизма для выполнения задач одновременно. В следующем примере по-прежнему имеются три задачи, и каждая из них не зависит от других. Executor настроен с двумя рабочими для выполнения экземпляров задач.</p>
<p><strong>Рисунок 3.4: Пример Local Executor с параллелизмом</strong></p>
<p><a href="https://datatalks.ru/wp-content/uploads/2025/07/image_3_4_local_executor.jpeg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-1920" src="https://datatalks.ru/wp-content/uploads/2025/07/image_3_4_local_executor.jpeg" alt="" width="789" height="828" srcset="https://datatalks.ru/wp-content/uploads/2025/07/image_3_4_local_executor.jpeg 789w, https://datatalks.ru/wp-content/uploads/2025/07/image_3_4_local_executor-286x300.jpeg 286w, https://datatalks.ru/wp-content/uploads/2025/07/image_3_4_local_executor-768x806.jpeg 768w, https://datatalks.ru/wp-content/uploads/2025/07/image_3_4_local_executor-450x472.jpeg 450w, https://datatalks.ru/wp-content/uploads/2025/07/image_3_4_local_executor-780x819.jpeg 780w" sizes="(max-width: 789px) 100vw, 789px" /></a></p>
<p>В этом примере мы видим, что задача один, зелёная, и задача два, синяя, выполняются одновременно, поскольку каждый рабочий обрабатывает их независимо. После завершения задачи один, задача три, фиолетовая, может быть запущена Рабочим №1. Если вас интересуют дополнительные оптимизации, мы рассмотрим пулы рабочих и очереди в следующих главах, которые могут быть крайне полезны с точки зрения затрат и времени.</p>
<p>Уровень параллелизма, по сути, определяет, сколько экземпляров задач может быть выполнено одновременно в любой момент времени. Этот инструмент крайне полезен при работе с длительными задачами. Параллелизм — это параметр конфигурации, который можно настроить до наивысшего предела, поддерживаемого машиной, на которой работает Local Executor.</p>
<h2>Celery Executor (Удалённый Executor)</h2>
<p><strong>Celery Executor</strong> — один из удалённых исполнителей, доступных в Apache Airflow. Он использует Celery — распределённую систему очередей задач, которая позволяет выполнять задачи параллельно на нескольких машинах-воркерах. В этой конфигурации Celery использует брокер, такой как RabbitMQ или Redis, для обработки коммуникации между основной инстанцией Airflow и машинами-воркерами.</p>
<p>Основные случаи использования Celery Executor включают:</p>
<ul>
<li><strong>Масштабируемость:</strong> Подходит для крупных развёртываний Airflow, когда задачи нужно распределять между несколькими машинами из-за большого объёма экземпляров задач или ресурсоёмких задач.</li>
<li><strong>Распределённое выполнение:</strong> Когда вы хотите выполнять задачи на разных машинах с разными конфигурациями или возможностями.</li>
<li><strong>Высокая доступность:</strong> При наличии нескольких узлов-воркеров, если один воркер выходит из строя, другие всё ещё могут обрабатывать задачи, обеспечивая устойчивость системы к сбоям.</li>
<li><strong>Разделение ресурсов:</strong> Если определённые задачи требуют специфических системных ресурсов или конфигураций, их можно направлять на специально настроенные воркеры.</li>
</ul>
<p>Celery Executor предлагает множество преимуществ по сравнению с локальными и удалёнными исполнителями:</p>
<ul>
<li><strong>Горизонтальная масштабируемость:</strong> По мере роста нагрузки можно просто добавить больше узлов-воркеров для обработки увеличенного объёма задач без изменения существующей инфраструктуры.</li>
<li><strong>Гибкость:</strong> Можно настроить разные узлы-воркеры для разных типов задач на основе требований к ресурсам, обеспечивая оптимальное использование ресурсов.</li>
<li><strong>Разделение компонентов:</strong> Разделённая архитектура означает, что веб-сервер и планировщик Airflow отделены от машин-воркеров, выполняющих задачи. Это разделение гарантирует, что ресурсоёмкие задачи не замедлят работу планировщика или веб-интерфейса.</li>
<li><strong>Параллелизм:</strong> За счёт распределения задач между несколькими машинами можно достичь высокой степени параллелизма, позволяя множеству задач выполняться одновременно, тем самым сокращая общее время выполнения для больших рабочих процессов.</li>
</ul>
<p>По мере изучения разных исполнителей, сложность настройки и управления возрастает по сравнению с базовым Sequential Executor. Для Celery Executor следует учитывать следующее:</p>
<ul>
<li><strong>Сложность настройки:</strong> По сравнению с LocalExecutor или SequentialExecutor, настройка CeleryExecutor требует дополнительных компонентов, таких как брокер сообщений (RabbitMQ или Redis) и backend для хранения результатов, что делает начальную настройку более сложной.</li>
<li><strong>Операционные издержки:</strong> Мониторинг и обслуживание нескольких компонентов (Airflow, Celery, воркеры) могут создать операционные сложности.</li>
<li><strong>Задержки:</strong> Могут возникать небольшие задержки из-за передачи сообщений между основной инстанцией Airflow, брокером и узлами-воркерами.</li>
<li><strong>Ограничения брокера:</strong> Выбор брокера сообщений связан с его собственными ограничениями, особенностями и сложностями обслуживания.</li>
<li><strong>Стоимость:</strong> Запуск нескольких узлов-воркеров может увеличить затраты на инфраструктуру, особенно если они недозагружены.</li>
<li><strong>Синхронизация версий:</strong> Обеспечение того, чтобы все узлы-воркеры работали на одной версии Airflow и имели все необходимые зависимости, может быть сложной задачей в распределённой системе.</li>
</ul>
<p>Хотя Celery Executor предоставляет возможности масштабируемости и распределённого выполнения, необходимые для крупномасштабных развёртываний Airflow, он также влечёт за собой увеличение сложности и операционных трудностей. Важно сопоставлять преимущества и ограничения, исходя из конкретных требований ваших рабочих процессов.</p>
<h2>Kubernetes Executor (Удалённый Executor)</h2>
<p><strong>Kubernetes Executor</strong> — это удалённый и динамический исполнитель для Apache Airflow, который запускает экземпляры задач в отдельных pod&#8217;ах Kubernetes. Этот исполнитель был введён для использования возможностей Kubernetes, позволяя по запросу создавать pod&#8217;ы для выполнения задач и удовлетворять растущие потребности корпоративных команд с высоко сложными и крупными рабочими нагрузками.</p>
<p>Основные случаи использования Kubernetes Executor включают:</p>
<ul>
<li><strong>Запуск задач, требующих доступа к большому количеству ресурсов:</strong> KubernetesExecutor может использоваться для запуска задач, которым необходим доступ к большим объёмам ресурсов, таким как CPU, память и GPU. Это возможно благодаря тому, что Kubernetes может динамически выделять ресурсы pod&#8217;ам по мере необходимости.</li>
<li><strong>Запуск задач, которые должны выполняться в определённой среде:</strong> KubernetesExecutor может использоваться для запуска задач, которые должны выполняться в определённой среде, например, с определённой версией Python или набором библиотек. Это возможно, потому что Kubernetes может запускать pod&#8217;ы с разными контейнерами, каждый из которых может содержать свою собственную среду.</li>
<li><strong>Запуск задач, которым требуется отказоустойчивость:</strong> KubernetesExecutor может использоваться для запуска задач, которым нужна отказоустойчивость. Это возможно, потому что Kubernetes может перезапускать завершившиеся сбоем pod&#8217;ы и переназначать задачи другим pod&#8217;ам.</li>
<li><strong>Масштабируемость:</strong> Этот исполнитель подходит для сред, в которых нагрузка по задачам значительно варьируется. Kubernetes может быстро масштабироваться вверх или вниз в зависимости от спроса.</li>
<li><strong>Распределённое выполнение:</strong> Полезен, когда задачи нужно распределить по кластеру Kubernetes — как в облачной среде, так и на собственных серверах.</li>
<li><strong>Эфемерные среды:</strong> Для задач, которым требуется чистая среда при каждом запуске, создание нового pod&#8217;а обеспечивает временное окружение.</li>
</ul>
<p>С введением Kubernetes Executor были отмечены многочисленные преимущества по сравнению с локальными и другими удалёнными исполнителями:</p>
<ul>
<li><strong>Динамическая масштабируемость:</strong> В отличие от статических конфигураций, где ресурсы выделяются заранее, с Kubernetes Executor ресурсы выделяются только тогда, когда задачи нуждаются в выполнении, что оптимизирует использование ресурсов.</li>
<li><strong>Интеграция с облаком:</strong> Многие облачные провайдеры предлагают управляемые сервисы Kubernetes (например, GKE, EKS, AKS). Kubernetes Executor интегрируется с этими сервисами и может быть развёрнут с помощью таких инструментов, как Helm, предоставляя облачную масштабируемость и управление.</li>
<li><strong>Автоматическое восстановление и избыточность:</strong> Kubernetes изначально предоставляет такие функции, как автоматические перезапуски, замена вышедших из строя pod&#8217;ов и распределение pod&#8217;ов по узлам, что повышает надёжность выполнения задач.</li>
<li><strong>Настраиваемость:</strong> Каждый pod может быть настроен с использованием примитивов Kubernetes. Это позволяет задавать специфические конфигурации, секреты, тома и другие необходимые параметры для задач в индивидуальном порядке.</li>
</ul>
<p>С дополнительными возможностями и контролем над экземплярами задач и воркерами, которые выполняют задачи, растёт и сложность обслуживания. Для Kubernetes Executor важно учитывать следующее:</p>
<ul>
<li><strong>Сложность настройки:</strong> Развёртывание и управление кластером Kubernetes, особенно если он ещё не используется, может быть сложным и требует экспертных знаний в Kubernetes, облачной архитектуре и распределённых сетях.</li>
<li><strong>Накладные расходы:</strong> Для очень лёгких или быстрых задач накладные расходы на запуск нового pod&#8217;а могут быть значительными по сравнению с фактическим временем выполнения задачи.</li>
<li><strong>Стоимость:</strong> Хотя динамическое масштабирование может быть экономически эффективным, всё же существует базовая стоимость поддержки кластера Kubernetes. Кроме того, частое создание и удаление pod&#8217;ов может привести как к увеличению расходов, так и к экономии в некоторых случаях.</li>
<li><strong>Постоянные данные:</strong> Pod&#8217;ы являются эфемерными, и хранение постоянных данных может быть проблематичным. Хотя существуют способы решения этой проблемы с использованием постоянных томов, это добавляет дополнительную сложность.</li>
<li><strong>Кривая обучения:</strong> Для команд, незнакомых с Kubernetes, может быть крутая кривая обучения как в понимании концепций Kubernetes, так и в отладке проблем, специфичных для платформы.</li>
<li><strong>Сетевые задержки:</strong> Запуск pod&#8217;ов может вызывать сетевые задержки, особенно если образы нужно загружать из реестра или если в pod&#8217;е есть начальные задачи настройки.</li>
<li><strong>Задержка запуска:</strong> Образы необходимо загрузить из реестра (если они не закэшированы), а контейнеры проходят процесс запуска для каждой задачи. В зависимости от архитектуры образа время запуска контейнера может быть значительным.</li>
<li><strong>Совместимость версий:</strong> Обеспечение совместимости между версиями Airflow и Kubernetes, а также отслеживание изменений API Kubernetes может быть задачей обслуживания.</li>
</ul>
<p>В заключение, KubernetesExecutor предлагает высоко динамичную и масштабируемую среду выполнения задач Airflow, используя сильные стороны Kubernetes. Однако он может ввести сложность и накладные расходы, особенно для команд, незнакомых с Kubernetes, или для рабочих процессов с лёгкими задачами. Как всегда, выбор исполнителя должен основываться на конкретных требованиях и контексте развёртывания.</p>
<h2>Dask Executor (Удалённый Executor)</h2>
<p><strong>DaskExecutor</strong> — это исполнитель для Apache Airflow, использующий Dask, гибкую библиотеку параллельных вычислений для аналитических расчётов. Dask может использоваться для построения параллельных распределённых вычислительных систем, масштабируемых от одной машины до кластера машин. При использовании в Airflow, DaskExecutor направляет выполнение задач в кластер Dask.</p>
<p>Основные случаи использования Dask Executor включают:</p>
<ul>
<li><strong>Машинное обучение:</strong> Dask Executor хорошо подходит для задач машинного обучения, таких как обучение и оценка моделей. Это связано с тем, что Dask может распределять эти задачи между несколькими воркерами, что может значительно ускорить процесс обучения.</li>
<li><strong>Data science:</strong> Dask Executor также может использоваться для задач data science, таких как предварительная обработка и анализ данных. Это связано с тем, что Dask может распределять эти задачи между несколькими воркерами, что помогает повысить производительность и масштабируемость.</li>
<li><strong>Другие ресурсоёмкие вычислительные задачи:</strong> Dask Executor также может использоваться для других ресурсоёмких вычислительных задач, таких как обработка видео и научные вычисления.</li>
</ul>
<p>Dask Executor предлагает несколько преимуществ по сравнению с локальными и удалёнными исполнителями:</p>
<ul>
<li><strong>Общая инфраструктура:</strong> Для команд или организаций, которые уже используют Dask для распределённых вычислений, DaskExecutor позволяет использовать ту же инфраструктуру для выполнения задач Airflow, обеспечивая оптимизацию ресурсов.</li>
<li><strong>Гибкость:</strong> Dask предоставляет гибкую платформу, которую можно запускать как на одной машине (в многопоточном или многопроцессном режиме), так и в распределённом кластере. Такая гибкость полезна при разном масштабе развёртывания.</li>
<li><strong>Python-ориентированная экосистема:</strong> Dask тесно интегрирован с экосистемой Python, что делает его естественным выбором для ориентированных на данные рабочих процессов, написанных на Python.</li>
</ul>
<p>Так как Dask Executor является относительно новой опцией в сообществе, важно учитывать следующее:</p>
<ul>
<li><strong>Сложность настройки:</strong> Если у вас ещё нет настроенного Dask, внедрение Dask-кластера и обеспечение его стабильной работы может быть сложным, и в некоторых случаях может быть более целесообразно использовать другой исполнитель.</li>
<li><strong>Операционные накладные расходы:</strong> Управление и мониторинг кластера Dask, особенно в продуктивной среде, может вызвать дополнительные операционные сложности.</li>
<li><strong>Управление зависимостями:</strong> Обеспечение того, чтобы все воркеры Dask имели правильную и согласованную среду и зависимости, может быть затруднительным, особенно когда выполняются разнообразные задачи с разными требованиями.</li>
<li><strong>Потенциальное конкурирование за ресурсы:</strong> Если кластер Dask используется также для других вычислительных задач, помимо Airflow, может возникнуть конкуренция за ресурсы, что приведёт к снижению производительности.</li>
<li><strong>Сетевые накладные расходы:</strong> В зависимости от конфигурации кластера Dask могут возникать сетевые накладные расходы при передаче задач и получении результатов, особенно если задачи зависят от ввода-вывода.</li>
</ul>
<p>В заключение, DaskExecutor предоставляет возможность распределённого выполнения задач в Apache Airflow, особенно для команд, уже использующих Dask или работающих с ресурсоёмкими рабочими процессами на Python. Однако, как и другие распределённые исполнители, он добавляет сложности в настройке, управлении и оптимизации производительности. Решение об использовании DaskExecutor должно основываться на конкретных потребностях рабочего процесса, существующей инфраструктуре и уровне знакомства с Dask.</p>
<h2>Kubernetes Local Executor (гибридный исполнитель)</h2>
<p><strong>Kubernetes Local Executor</strong> — это более новый исполнитель для Apache Airflow, который позволяет запускать задачи локально с использованием Local Executor или в Kubernetes с использованием Kubernetes Executor, в зависимости от очереди экземпляра задачи. Это может быть полезно для задач с разными требованиями к ресурсам или задач, которым необходимо выполняться в определённой среде.</p>
<p>Основные случаи использования Kubernetes Local Executor включают:</p>
<ul>
<li><strong>Разработка и тестирование DAG-файлов Airflow локально:</strong> Kubernetes Local Executor можно использовать для разработки и тестирования DAG-файлов Airflow локально без необходимости развертывания их в кластере Kubernetes. Это может быть полезно для отладки DAG-файлов и для быстрой итерации новых функций.</li>
<li><strong>Запуск DAG-файлов Airflow в кластере Kubernetes в продуктивной среде:</strong> Kubernetes Local Executor также может использоваться для запуска DAG-файлов Airflow в кластере Kubernetes в продуктивной среде. Это может быть полезно для задач с высокими требованиями к ресурсам или задач, которые должны выполняться в масштабируемой и отказоустойчивой среде.</li>
</ul>
<p>Kubernetes Local Executor является важным дополнением к списку исполнителей, так как он обеспечивает гибкость в том, как запускать задачи Airflow, в зависимости от конкретных требований задачи. Это может быть полезно для разработки, тестирования и запуска DAG-файлов Airflow в различных средах.</p>
<p>Вот несколько дополнительных моментов, которые следует учитывать при использовании Kubernetes Local Executor:</p>
<ul>
<li>Убедитесь, что имеется достаточно ресурсов по памяти и CPU для поддержки количества pod&#8217;ов Kubernetes, которые может потребоваться создать Kubernetes Local Executor.</li>
<li>Учитывайте возросшую нагрузку на базу данных при использовании Kubernetes Local Executor.</li>
<li>Помните, что использование Kubernetes Local Executor может привести к параллельному выполнению задач, что может вызвать условия гонки. Чтобы этого избежать, возможно, потребуется реализовать механизмы синхронизации.</li>
</ul>
<h1>Планировщик (Scheduler)</h1>
<p>В предыдущих разделах рассматривалось, как выполняются задачи и как лучше всего обеспечивать выполнение различных вариантов экземпляров задач. Чтобы определить, когда эти задачи должны быть запланированы для выполнения, необходимо подробнее рассмотреть планировщик (Scheduler) и его многочисленные обязанности:</p>
<ul>
<li><strong>Разбор DAG-файлов (DAG Parsing):</strong> Планировщик непрерывно разбирает файлы DAG в каталоге DAG, чтобы найти новые задачи для планирования. Он определяет порядок выполнения на основе зависимостей, установленных в DAG-файлах.</li>
<li><strong>Механизм heartbeat:</strong> Планировщик работает в цикле, часто называемом &#171;heartbeat&#187;, где он постоянно проверяет наличие задач для выполнения, планирует их, а затем делает короткую паузу перед следующей проверкой.</li>
<li><strong>Динамическое планирование задач (Dynamic Task Scheduling):</strong> В отличие от традиционных cron-настроек, где задания фиксированы, планировщик Airflow динамически определяет, какие задачи должны быть запущены, на основе их зависимостей и состояния. Это позволяет создавать более сложные рабочие процессы с условными путями выполнения.</li>
</ul>
<p>Некоторые ключевые настройки планировщика, которые мы считаем наиболее полезными для оптимизации продуктивных сред, включают следующее:</p>
<ul>
<li><strong>Управление параллелизмом (Concurrency Controls):</strong> Планировщик учитывает различные настройки параллелизма, которые можно настроить в конфигурационном файле:<br />
 dag_concurrency: количество экземпляров задач, разрешённых к одновременному выполнению планировщиком для конкретного DAG.<br />
 parallelism: глобальный параллелизм, то есть общее количество экземпляров задач, которые могут выполняться одновременно во всех DAG-файлах.</li>
<li><strong>Обработка сбоев (Handling Failures):</strong> Если задача завершается с ошибкой, планировщик может повторить её выполнение, исходя из параметра retries, заданного в задаче. Он соблюдает настройку retry_delay, определяющую время между повторными попытками.</li>
<li><strong>Backfill и Catch-up:</strong> Планировщик может выполнять backfill исторических данных, запуская пропущенные DAG-запуски за заданный диапазон дат. Кроме того, если для DAG задан catchup=True, и запуск DAG был пропущен (например, из-за простоя), планировщик выполнит этот DAG-запуск, когда Airflow снова будет доступен.</li>
<li><strong>Управление ресурсами и пулы (Resource Management and Pools):</strong> Планировщик обеспечивает планирование задач на основе доступности ресурсов. Используя концепцию пулов в Airflow, можно ограничить количество одновременных задач, использующих определённый ресурс, чтобы избежать его перегрузки. Мы подробнее рассмотрим очереди задач и пулы в следующих главах.</li>
</ul>
<p>Ключевая характеристика планировщика заключается в том, что он отвечает за экземпляры задач до тех пор, пока они не будут помещены в состояние очереди. После того, как задача помещена в очередь, ответственность за её выполнение переходит к выбранному исполнителю (executor).</p>
<h1>Резюме по 3 главе</h1>
<p>В заключение, понимание компонентов Apache Airflow, их отдельных ролей и того, как они взаимодействуют, имеет решающее значение для эффективной настройки, эксплуатации и оптимизации среды Airflow. Эти знания позволяют вам выбирать правильные конфигурации, обеспечивать эффективное выполнение задач и масштабировать систему в соответствии с целями бизнеса. Овладев этими элементами, вы будете хорошо подготовлены к управлению Airflow в продуктивной среде, открывая возможности для оптимизации и использования скрытого потенциала по мере роста ваших потребностей в оркестрации задач и заданий.</p>
<p>В следующей главе мы начнем использовать эти компоненты, рассмотрев основы создания DAG-файлов.</p>
<p>Сообщение <a href="https://datatalks.ru/airflow-best-practices-chapter-3-components-of-airflow/">Airflow Best Practices Перевод 3 главы &#171;Компоненты Airflow&#187;</a> появились сначала на <a href="https://datatalks.ru">DataTalks.RU. Data Engineering / DWH / Data Pipeline</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://datatalks.ru/airflow-best-practices-chapter-3-components-of-airflow/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Apache Airflow Best Practices — Глава 2 «Core Concepts»</title>
		<link>https://datatalks.ru/apache-airflow-best-practices-chapter-2-core-concepts/</link>
					<comments>https://datatalks.ru/apache-airflow-best-practices-chapter-2-core-concepts/#respond</comments>
		
		<dc:creator><![CDATA[Data Engineer (Admin)]]></dc:creator>
		<pubDate>Sun, 20 Jul 2025 08:00:22 +0000</pubDate>
				<category><![CDATA[Apache Airflow Best Practices]]></category>
		<category><![CDATA[Airflow Trigger]]></category>
		<category><![CDATA[airflowctl]]></category>
		<category><![CDATA[Apache Airflow]]></category>
		<category><![CDATA[BashOperator]]></category>
		<category><![CDATA[DockerOperator]]></category>
		<category><![CDATA[HttpOperator]]></category>
		<category><![CDATA[PythonOperator]]></category>
		<category><![CDATA[SqlOperator]]></category>
		<category><![CDATA[XCom]]></category>
		<guid isPermaLink="false">https://datatalks.ru/?p=1772</guid>

					<description><![CDATA[<p>Перевод книги &#171;Apache Airflow Best Practices, by Dylan Intorf, Dylan Storey, Kendrick van Doorn&#187; Packt Publishing подготовлен автором сайта Глава 2. Основные концепции Airflow В основе Airflow лежат основные концепции, упрощающие процесс определения, выполнения и мониторинга задач. Эти концепции включают задачи, группы задач и триггеры. Каждая из них составляет ориентированные ациклические графы (DAG’и) и позволяет [&#8230;]</p>
<p>Сообщение <a href="https://datatalks.ru/apache-airflow-best-practices-chapter-2-core-concepts/">Apache Airflow Best Practices — Глава 2 «Core Concepts»</a> появились сначала на <a href="https://datatalks.ru">DataTalks.RU. Data Engineering / DWH / Data Pipeline</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><em>Перевод книги &#171;Apache Airflow Best Practices, by Dylan Intorf, Dylan Storey, Kendrick van Doorn&#187; Packt Publishing подготовлен автором сайта</em></p>
<h1>Глава 2. Основные концепции Airflow</h1>
<p>В основе <strong>Airflow</strong> лежат основные концепции, упрощающие процесс определения, выполнения и мониторинга задач. Эти концепции включают задачи, группы задач и триггеры. Каждая из них составляет <strong>ориентированные ациклические графы (DAG’и)</strong> и позволяет использовать преимущества Airflow. Понимание каждой из этих строительных блоков является важным для того, чтобы в полной мере использовать потенциал Airflow при масштабировании и обеспечить автоматизацию и оптимизацию рабочих процессов.</p>
<p>Помимо основ DAG’ов, в этой главе будет представлен <strong>концепт интерфейса командной строки</strong> для запуска Apache Airflow локально на компьютере или виртуальной машине. Этот процесс легко повторить, и для его запуска были созданы различные инструменты. Те же инструменты, которые мы будем использовать для начала создания DAG’ов и настройки Airflow, могут применяться и в более сложных ситуациях, поэтому они являются отраслевым стандартом.</p>
<p><strong>В этой главе мы рассмотрим следующие основные темы:</strong></p>
<ul>
<li>Запуск Apache Airflow на вашей локальной машине с помощью <code>airflowctl</code></li>
<li>Строительные блоки DAG’ов</li>
<li>Как максимально эффективно использовать группы задач и организовывать DAG’и</li>
</ul>
<h1>Технические требования</h1>
<p>В предыдущих главах мы в основном рассматривали аспекты, связанные с Apache Airflow, но не затрагивали основы или конкретные сценарии использования. Начиная с этой главы и далее, мы предполагаем, что у вас настроено окружение Airflow на локальной машине и вы понимаете, как к нему получить доступ.</p>
<p><strong>Установка Airflow локально</strong> требует ряда технологий и предварительных условий. В частности, мы рекомендуем установить <strong>актуальную версию Python</strong>, так как старые версии не имеют долгосрочной поддержки. Кроме того, для запуска Airflow требуется как <strong>минимум 4 ГБ оперативной памяти</strong>, хотя это требование зависит от размера развертывания и сложности DAG’ов.</p>
<p><strong>Интерфейс командной строки (CLI)</strong> — это текстовый пользовательский интерфейс, используемый для взаимодействия с программным обеспечением и операционными системами. Через <strong>CLI</strong> пользователи вводят текстовые команды для выполнения определённых задач или операций, напрямую взаимодействуя с системой. Такой интерфейс позволяет эффективно выполнять команды, автоматизировать задачи с помощью скриптов и получать доступ к широкому спектру системных функций. <strong>CLI</strong> особенно ценятся разработчиками и системными администраторами за точность, возможность скриптования и низкое потребление ресурсов по сравнению с <strong>графическими интерфейсами (GUI)</strong>.</p>
<p><strong>Инструмент CLI</strong> <code>airflowctl</code> — это <strong>CLI-инструмент</strong>, специально предназначенный для взаимодействия с окружениями Apache Airflow. Он позволяет пользователям управлять различными аспектами развертываний Airflow напрямую из командной строки. С помощью <code>airflowctl</code> пользователи могут выполнять задачи, такие как <strong>запуск DAG’ов</strong>, <strong>приостановка</strong> или <strong>возобновление</strong> <strong>их выполнения</strong>, <strong>создание</strong> или <strong>отображение подключений</strong>, а также <strong>доступ к логам</strong>. Этот инструмент упрощает процесс управления рабочими процессами в Airflow, обеспечивая эффективную эксплуатацию и мониторинг задач в инстансе Airflow. В следующих главах мы углубимся в более сложные идеи и сценарии использования CLI-инструментов Airflow.</p>
<p>Самый простой и быстрый способ начать работу с Airflow — <strong>использовать команду Airflow CLI</strong> в вашем терминале или командной строке:</p><pre class="urvanov-syntax-highlighter-plain-tag">$ airflowctl</pre><p>Первичный запуск команды проверит, установлена ли она уже на вашей локальной машине. Если вы получите следующий запрос, выполните приведённые далее инструкции по установке:</p>
<p><strong>Рисунок 2.1: настройка airflowctl</strong></p>
<p><a href="https://datatalks.ru/wp-content/uploads/2025/07/airflowctl_setup.jpeg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-1877" src="https://datatalks.ru/wp-content/uploads/2025/07/airflowctl_setup.jpeg" alt="" width="1270" height="94" srcset="https://datatalks.ru/wp-content/uploads/2025/07/airflowctl_setup.jpeg 1270w, https://datatalks.ru/wp-content/uploads/2025/07/airflowctl_setup-300x22.jpeg 300w, https://datatalks.ru/wp-content/uploads/2025/07/airflowctl_setup-1024x76.jpeg 1024w, https://datatalks.ru/wp-content/uploads/2025/07/airflowctl_setup-768x57.jpeg 768w, https://datatalks.ru/wp-content/uploads/2025/07/airflowctl_setup-450x33.jpeg 450w, https://datatalks.ru/wp-content/uploads/2025/07/airflowctl_setup-780x58.jpeg 780w" sizes="(max-width: 1270px) 100vw, 1270px" /></a></p>
<p>Использование <code>airflowctl</code> (<a href="https://github.com/kaxil/airflowctl" target="_blank" rel="noopener">https://github.com/kaxil/airflowctl</a>) позволяет выполнить начальную установку вне Docker-контейнеров или Kubernetes.<br />
Рекомендуется устанавливать CLI с помощью pip. Если у вас не установлен pip, вы можете установить его, следуя этим инструкциям: <a href="https://pip.pypa.io/en/stable/installation/" target="_blank" rel="noopener">https://pip.pypa.io/en/stable/installation/</a>. С помощью pip следующая команда установит CLI:</p><pre class="urvanov-syntax-highlighter-plain-tag">$ pip install airflowctl</pre><p>Если во время установки не возникло ошибок, следующим шагом будет запуск команды инициализации. При выполнении этой команды создаётся каталог проекта с названием my_airflow_project в вашей текущей папке и запускается веб-сервер Airflow:</p><pre class="urvanov-syntax-highlighter-plain-tag">$ airflowctl init my_airflow_project --build-start</pre><p>Сразу после запуска этой команды в верхней части командного терминала вы увидите расположение папки проекта и другую информацию об инициализации:</p>
<p><strong>Рисунок 2.2: Инициализация проекта Airflow</strong></p>
<p><a href="https://datatalks.ru/wp-content/uploads/2025/07/image_2_2_init_airflow_project.jpeg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-1880" src="https://datatalks.ru/wp-content/uploads/2025/07/image_2_2_init_airflow_project.jpeg" alt="" width="1767" height="196" srcset="https://datatalks.ru/wp-content/uploads/2025/07/image_2_2_init_airflow_project.jpeg 1767w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_2_init_airflow_project-300x33.jpeg 300w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_2_init_airflow_project-1024x114.jpeg 1024w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_2_init_airflow_project-768x85.jpeg 768w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_2_init_airflow_project-1536x170.jpeg 1536w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_2_init_airflow_project-450x50.jpeg 450w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_2_init_airflow_project-780x87.jpeg 780w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_2_init_airflow_project-1600x177.jpeg 1600w" sizes="(max-width: 1767px) 100vw, 1767px" /></a></p>
<p>В этом примере папка проекта расположена по пути /Users/kendrickvandoorn/my_airflow_project\ x, и она была инициализирована с предустановленной организацией папок и файлов, необходимых для запуска первого проекта. Обратите внимание, что имя my_airflow_project, заданное в команде CLI, совпадает с именем папки проекта, но его можно изменить в соответствии с вашими потребностями.</p>
<p>По мере продолжения работы CLI airflowctl веб-сервер, триггер и планировщик будут запускаться, а их задачи будут отмечены в начале каждой строки. Например, ознакомьтесь со следующим снимком экрана, отображающим логотип Airflow, который показывает, что триггер и планировщик активны, а следующие действия относятся к различным серверам.</p>
<p><strong>Рисунок 2.3: Командная строка Airflow</strong></p>
<p><a href="https://datatalks.ru/wp-content/uploads/2025/07/image_2_3_airflow.jpeg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-1881" src="https://datatalks.ru/wp-content/uploads/2025/07/image_2_3_airflow.jpeg" alt="" width="1476" height="714" srcset="https://datatalks.ru/wp-content/uploads/2025/07/image_2_3_airflow.jpeg 1476w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_3_airflow-300x145.jpeg 300w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_3_airflow-1024x495.jpeg 1024w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_3_airflow-768x372.jpeg 768w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_3_airflow-450x218.jpeg 450w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_3_airflow-780x377.jpeg 780w" sizes="(max-width: 1476px) 100vw, 1476px" /></a></p>
<p>По мере выполнения командной строки будут запускаться и настраиваться различные базовые проверки, подключения и разрешения. По мере того как мы продолжаем изучать <strong>Airflow</strong>, многие из этих решений, принимаемых перед развертыванием, могут быть заданы и изменены заранее в различных папках с определениями, что гарантирует соответствие инстанса <strong>Airflow</strong> вашим требованиям по размеру, безопасности и подключениям. Если инициализация прошла успешно, вы увидите следующие сообщения с информацией о том, как получить доступ к веб-серверу и войти в систему:</p>
<p><strong>Рисунок 2.4: Успешная инициализация Airflow</strong></p>
<p><a href="https://datatalks.ru/wp-content/uploads/2025/07/image_2_4_airflow.jpeg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-1883" src="https://datatalks.ru/wp-content/uploads/2025/07/image_2_4_airflow.jpeg" alt="" width="1690" height="110" srcset="https://datatalks.ru/wp-content/uploads/2025/07/image_2_4_airflow.jpeg 1690w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_4_airflow-300x20.jpeg 300w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_4_airflow-1024x67.jpeg 1024w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_4_airflow-768x50.jpeg 768w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_4_airflow-1536x100.jpeg 1536w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_4_airflow-450x29.jpeg 450w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_4_airflow-780x51.jpeg 780w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_4_airflow-1600x104.jpeg 1600w" sizes="(max-width: 1690px) 100vw, 1690px" /></a></p>
<p>В этом выводе терминала вы можете увидеть сообщение <strong>Airflow is ready</strong>, которое показывает, что процесс прошёл успешно и Airflow запущен локально на вашей машине. Обратите внимание, что имя пользователя — <strong>admin</strong>, оно используется по умолчанию при первой установке. Указанный пароль — не по умолчанию, а сгенерированный случайным образом, и он специфичен для вашего локального хоста. Оставьте терминал открытым и работающим, чтобы сохранить среду Airflow в активном состоянии на вашем компьютере.</p>
<p><strong>CLI</strong> <code>airflowctl</code> не только запускает <strong>Airflow</strong> локально, но также обеспечивает управление продуктом во время его работы. Все логи и информация, касающаяся <strong>Airflow</strong>, будут отображаться в терминале, пока он работает. Обратите внимание, что различные службы постоянно проверяют и обрабатывают сигналы, которые отображаются в командной строке.</p>
<p><strong>Рисунок 2.5: Сигналы веб-сервера</strong></p>
<p><a href="https://datatalks.ru/wp-content/uploads/2025/07/image_2_5_web_server_signals.jpeg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-1884" src="https://datatalks.ru/wp-content/uploads/2025/07/image_2_5_web_server_signals.jpeg" alt="" width="1766" height="403" srcset="https://datatalks.ru/wp-content/uploads/2025/07/image_2_5_web_server_signals.jpeg 1766w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_5_web_server_signals-300x68.jpeg 300w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_5_web_server_signals-1024x234.jpeg 1024w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_5_web_server_signals-768x175.jpeg 768w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_5_web_server_signals-1536x351.jpeg 1536w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_5_web_server_signals-450x103.jpeg 450w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_5_web_server_signals-780x178.jpeg 780w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_5_web_server_signals-1600x365.jpeg 1600w" sizes="(max-width: 1766px) 100vw, 1766px" /></a></p>
<p>Чтобы перейти к веб-серверу, откройте браузер и введите в адресной строке следующее:</p>
<p><a href="http://localhost:8080" target="_blank" rel="noopener" data-wplink-url-error="true">http://localhost:8080</a></p>
<p>Перейдя на localhost, при условии, что <strong>веб-сервер Airflow</strong> продолжает работать в терминале, вам будет предложено войти в систему с именем пользователя и паролем. Используйте имя пользователя <strong>admin</strong> и <strong>пароль</strong>, который был показан в терминале.</p>
<p>Завершив процесс входа в систему, вы попадёте на домашнюю страницу, которая должна выглядеть следующим образом:</p>
<p><strong>Рисунок 2.6: Домашняя страница DAG</strong></p>
<p><a href="https://datatalks.ru/wp-content/uploads/2025/07/image_2_6_dag_home_page.jpeg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-1885" src="https://datatalks.ru/wp-content/uploads/2025/07/image_2_6_dag_home_page.jpeg" alt="" width="1465" height="576" srcset="https://datatalks.ru/wp-content/uploads/2025/07/image_2_6_dag_home_page.jpeg 1465w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_6_dag_home_page-300x118.jpeg 300w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_6_dag_home_page-1024x403.jpeg 1024w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_6_dag_home_page-768x302.jpeg 768w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_6_dag_home_page-450x177.jpeg 450w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_6_dag_home_page-780x307.jpeg 780w" sizes="(max-width: 1465px) 100vw, 1465px" /></a></p>
<p>В следующих главах мы подробно рассмотрим важные разделы консоли пользовательского интерфейса <strong>Apache Airflow</strong> и приведём примеры типовых сценариев управления.</p>
<p>С работающей средой Airflow давайте рассмотрим основные компоненты Airflow на примере DAG, который уже предоставлен.</p>
<h1>DAG-и</h1>
<p><strong>DAG-и (направленные ациклические графы)</strong> — это основной способ оркестровки пайплайнов данных. Они создаются с использованием Python и опираются на широкий спектр вспомогательных библиотек. Следуя предыдущим шагам по инициализации локальной среды разработки, вы загрузили пример <strong>example_dag_basic</strong>, который мы рассмотрим в этом разделе.</p>
<p>В своей основе DAG-и состоят из <strong>задач (tasks)</strong>, <strong>операторов (operators)</strong> и <strong>сенсоров (sensors)</strong>. За последние годы были также введены новые подходы, такие как <strong>группы задач (task groups)</strong> и <strong>откладываемые операторы (deferrable operators)</strong>, которые мы также рассмотрим в этой главе.</p>
<p>Чтобы получить визуализацию этого DAG-а, просто выберите имя DAG-а в консоли веб-сервера. Сначала вы увидите информацию о конфигурации DAG-а и статусе его предыдущих запусков.</p>
<p><strong>Рисунок 2.7: Конфигурация DAG</strong></p>
<p><a href="https://datatalks.ru/wp-content/uploads/2025/07/image_2_7_configuring_dag_airflow.jpeg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-1889" src="https://datatalks.ru/wp-content/uploads/2025/07/image_2_7_configuring_dag_airflow.jpeg" alt="" width="1499" height="873" srcset="https://datatalks.ru/wp-content/uploads/2025/07/image_2_7_configuring_dag_airflow.jpeg 1499w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_7_configuring_dag_airflow-300x175.jpeg 300w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_7_configuring_dag_airflow-1024x596.jpeg 1024w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_7_configuring_dag_airflow-768x447.jpeg 768w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_7_configuring_dag_airflow-450x262.jpeg 450w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_7_configuring_dag_airflow-780x454.jpeg 780w" sizes="(max-width: 1499px) 100vw, 1499px" /></a></p>
<p>Чтобы просмотреть визуализированное представление этого первоначального примера DAG, выберите опцию <strong>Graph</strong> из списка опций в верхней части области DAG. Это отобразит графическое представление DAG.</p>
<p>Важно отметить, что в этом DAG три задачи, которые выполняются последовательно. В более сложных DAG могут быть задачи, выполняющиеся параллельно, а также те, которые ожидают определённого триггера, и их может быть слишком сложно визуализировать. Всегда рекомендуется, если возможно, разбивать сложное, монолитное программное обеспечение.</p>
<p><strong>Рисунок 2.8: Сопоставление триггеров</strong></p>
<p><a href="https://datatalks.ru/wp-content/uploads/2025/07/image_2_8_trigger_mapping.jpeg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-1890" src="https://datatalks.ru/wp-content/uploads/2025/07/image_2_8_trigger_mapping.jpeg" alt="" width="1495" height="405" srcset="https://datatalks.ru/wp-content/uploads/2025/07/image_2_8_trigger_mapping.jpeg 1495w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_8_trigger_mapping-300x81.jpeg 300w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_8_trigger_mapping-1024x277.jpeg 1024w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_8_trigger_mapping-768x208.jpeg 768w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_8_trigger_mapping-450x122.jpeg 450w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_8_trigger_mapping-780x211.jpeg 780w" sizes="(max-width: 1495px) 100vw, 1495px" /></a></p>
<p>Если <code>example_dag_basic</code> не был включён при инициализации, вы можете найти полный блок рабочего кода в конце этой главы или перейти на GitHub-страницу книги курса (<a href="https://github.com/PacktPublishing/Apache-Airflow-Best-Practices" target="_blank" rel="noopener">https://github.com/PacktPublishing/Apache-Airflow-Best-Practices</a>).</p>
<p><strong>Цель этого примера DAG</strong> — выполнить простую функцию извлечения, преобразования и загрузки данных (<strong>extract, transform, load — ETL</strong>). В конечном итоге мы покажем ожидаемый результат и то, как подтвердить завершение выполнения.</p>
<h2>Декораторы и определение DAG</h2>
<p>Первым шагом в любом Python-файле является импорт необходимых библиотек и вспомогательных функций. DAG в Airflow не является исключением. Мы начинаем с импорта <strong>json</strong> и <strong>декораторов</strong> Airflow:</p><pre class="urvanov-syntax-highlighter-plain-tag">import json
from airflow.decorators import (
    dag,
    task,
)</pre><p><strong>Импорт декораторов dag и task позволяет нам объявить DAG проще, чем раньше.</strong> Такой подход устраняет необходимость объявления конструкции with DAG as, как это требовалось ранее. Давайте объявим DAG и определим некоторые из необходимых полей, таких как расписание:</p><pre class="urvanov-syntax-highlighter-plain-tag">@dag(
    schedule="@daily",
    start_date=datetime(2023,1,1),
    catchup=False,
    default_args={
        "retries": 2,
    },
    tags=["example"],
)</pre><p>В приведённом выше фрагменте кода мы объявили <code>schedule</code> интервал, <code>start_date</code>, <code>catchup</code>, <code>default_args</code> и <code>tags</code>. Давайте кратко рассмотрим каждый из них.</p>
<h2>Планирование с Apache Airflow и отказ от CRON</h2>
<p><strong>Интервал расписания может быть определён как заданная дата/время, которую сохраняет Airflow, или через задания <code>Cron</code>.</strong> Ознакомьтесь с приведённой ниже таблицей для получения дополнительной информации о том, как выполняются интервалы расписания. Каждое из указанных времён установлено в <strong>UTC</strong>, поэтому учитывайте это при планировании выполнения задач.</p>
<p><strong>Таблица 2.1: Интервалы расписания для даты и времени</strong></p>
<table>
<thead>
<tr>
<th>Дата/время</th>
<th>Интервал</th>
</tr>
</thead>
<tbody>
<tr>
<td><code inline="">@none</code></td>
<td>Для выполнения DAG требуется ручной запуск или триггер</td>
</tr>
<tr>
<td><code inline="">@hourly</code></td>
<td>Выполняется в начале каждого часа</td>
</tr>
<tr>
<td><code inline="">@daily</code></td>
<td>Выполняется в полночь каждый день</td>
</tr>
<tr>
<td><code inline="">@weekly</code></td>
<td>Выполняется в полночь каждое воскресенье</td>
</tr>
<tr>
<td><code inline="">@monthly</code></td>
<td>Выполняется в полночь в первый день каждого месяца</td>
</tr>
<tr>
<td><code inline="">@yearly</code></td>
<td>Выполняется 1 января в полночь каждого года</td>
</tr>
</tbody>
</table>
<ul>
<li>Далее указывается <code>start_date</code>. Важно задать дату начала, так как вы можете захотеть, чтобы конвейер выполнялся в тот же день, в будущем или в прошлом. Если <code>start_date</code> установлена в прошлом и <code>catchup</code> задан как <code>True</code>, тогда DAG будет выполняться столько раз, сколько предусмотрено расписанием. В приведённом примере <code>catchup</code> установлен как <code>False</code>, поэтому DAG не будет выполняться за предыдущие дни, начиная с <strong>1 января 2023 года</strong>. Если бы мы установили <code>catchup</code> равным <code>True</code>, то следовало бы ожидать, что Airflow выполнит столько заданий, сколько дней прошло с <strong>1 января 2023 года</strong> по настоящее время.</li>
<li><code>default_args</code> — это значения по умолчанию, которые мы хотим изменить. В этом примере мы изменяем retries, установив его равным 2. Это количество попыток повторного выполнения задач DAG в случае их неудачи. Это полезный инструмент, если DAG пытается подключиться к базе данных, в которой часто возникают проблемы с подключением или обновлением, так как задачи будут пытаться выполниться снова позже, не влияя на другие задачи.</li>
<li>Наконец, мы применяем <code>tags</code> (теги) к DAG, чтобы начать их группировку. Тег отображается в интерфейсе Airflow и может быть полезен для пометки различных DAG для разных систем, команд или пользователей в будущем.</li>
</ul>
<h1>Задачи / Tasks</h1>
<p><strong>Задачи составляют DAG</strong> и выполняются по порядку в зависимости от того, как они определены. Задачи часто отображаются в виде блоков, чтобы показать порядок их выполнения. В этом базовом примере нужно последовательно выполнить три задачи. Задачи также являются самой базовой единицей выполнения в <strong>Airflow</strong>. Задача представляет собой одно действие или задание, которое должно быть выполнено. <strong>Задачи определяются с помощью операторов в Airflow</strong>, каждый из которых определяет тип выполняемой задачи. Распространённые задачи включают выполнение Python-функций, выполнение запросов к базе данных или выполнение преобразования данных.</p>
<p>Прежде чем приступить к анализу этого простого примера DAG и связанных с ним задач, нам нужно рассмотреть операторы и как они используются.</p>
<h2>Операторы задач</h2>
<p><strong>В Apache Airflow оператор представляет собой одну идемпотентную задачу</strong>, которая является частью рабочего процесса, определённого DAG. Каждый оператор в Airflow указывает конкретный тип работы, инкапсулируя логику выполнения конкретной задачи. Каждый оператор предназначен для выполнения определённой функции, например, выполнения Python-функции, SQL-запроса, передачи данных или вызова внешней системы.</p>
<p><strong>Airflow поставляется с большим набором операторов для различных задач</strong>, что позволяет пользователям сразу использовать широкий спектр возможностей. Некоторые из часто используемых типов включают:</p>
<ul>
<li><code>BashOperator</code>: выполняет bash-команду</li>
<li><code>PythonOperator</code>: запускает Python-функцию</li>
<li><code>SqlOperator</code>: выполняет SQL-команду</li>
<li><code>DockerOperator</code>: запускает Docker-контейнер</li>
<li><code>HttpOperator</code>: отправляет HTTP-запрос</li>
</ul>
<p><strong>Операторы также могут быть настроены и расширены в соответствии с конкретными требованиями</strong>, обеспечивая гибкость в том, как задачи выполняются внутри DAGов Airflow. Такая архитектура позволяет пользователям создавать сложные конвейеры обработки данных, которые легко понимать, сопровождать и масштабировать. Большинство операторов являются с открытым исходным кодом и доступны для ознакомления — их можно найти, выполнив поиск по названию на GitHub.</p>
<h2>Первая задача — определение DAG и извлечение данных</h2>
<p>Мы создадим три задачи для выполнения этого базового примера DAG. Задачи извлечения, преобразования и загрузки будут выполнять разные функции. Первая задача — задача извлечения.</p>
<p>Следующий фрагмент кода взят из базового примера DAG:</p><pre class="urvanov-syntax-highlighter-plain-tag">def example_dag_basic():
    @task()
    def extract():
        data_string = '{"1001": 301.27, "1002": 433.21,\
                        "1003":502.22}'
        order_data_dict = json.loads(data_string)
        return order_data_dict</pre><p>Мы начинаем с объявления <code>@task</code>, чтобы указать, что следующая функция будет задачей, используемой в Airflow. В этой первой задаче мы определяем функцию <strong>extract</strong>, которая не принимает аргументов или переменных. Внутри <code>extract()</code> мы определяем простую строку <code>data_string</code>, которая затем преобразуется в словарь данных. В конце функция возвращает полученную информацию.</p>
<p>В более сложных ситуациях и примерах мы будем извлекать данные из API, озёр данных и хранилищ данных. Создание подключения и подготовка извлечения — схожие процессы.</p>
<h2>Определение задачи преобразования (transform task)</h2>
<p>В следующей задаче мы выполним простое действие по преобразованию. Эта задача принимает словарь данных и вычисляет общую сумму заказов:</p><pre class="urvanov-syntax-highlighter-plain-tag">@task(multiple_outputs=True)
def transform(order_data_dict: dict):
    total_order_value = 0
    for value in order_data_dict.values():
        total_order_value += value
    return {"total_order_value": total_order_value}</pre><p>Задача преобразования принимает <code>order_data_dict</code> в качестве входных данных для выполнения задачи преобразования. Далее мы задаём переменную <code>total_order_value</code>, <strong>равную 0</strong>, чтобы подготовить её для использования в следующем цикле. Создаётся цикл <code>for</code>, который рассчитывает общую сумму заказов путём суммирования значений. В конце мы возвращаем общую сумму заказов для использования в следующей задаче.</p>
<p>В этой задаче мы видим новый аргумент, передаваемый в <code>@task</code> — <strong>multiple_outputs</strong>. Его использование разворачивает словарь данных в отдельные значения <strong>XCom</strong>.</p>
<h2>XComs</h2>
<p><strong>XComs, сокращение от Cross-Communications (межзадачные коммуникации)</strong>, — это часто используемая функция в <strong>Apache Airflow</strong>. Она предназначена для облегчения обмена данными между задачами внутри DAG. Эта ключевая идея позволяет задачам обмениваться сообщениями или фрагментами данных, такими как состояния задач, возвращаемые значения или любая другая информация, связанная с выполнением. Это может быть чрезвычайно ценно и полезно при организации выполнения задач после того, как подтверждено завершение или сбой другой задачи.</p>
<p><strong>XComs</strong> работают за счёт того, что одна задача может передавать данные в мета-базу данных Airflow, где они хранятся под определённым ключом. Другая задача затем может извлечь эти данные, используя этот ключ, позволяя передавать данные между задачами, даже если они выполняются на разных рабочих узлах или в разное время. Эта функция особенно полезна для задач, зависящих от результатов или вывода предыдущих задач.</p>
<p><strong>Использование XComs</strong> способствует отделению задач внутри рабочего процесса, повышая модульность и повторное использование компонентов DAG. <span style="color: #ff0000;"><strong>Однако рекомендуется использовать XComs умеренно, так как они предназначены для небольших объёмов данных.</strong></span> Передача больших объёмов данных лучше осуществляется через внешние системы или сервисы хранения данных, в то время как Airflow отвечает за оркестрацию задач, а не за перемещение значительных объёмов данных.</p>
<h2>Определение задачи загрузки (load task)</h2>
<p>На финальном, третьем этапе мы выполним загрузку преобразованных данных. В этом примере мы просто выводим общую сумму заказов через функцию <code>print</code>, чтобы убедиться в её успешности в логах:</p><pre class="urvanov-syntax-highlighter-plain-tag">@task()
def load(order_data_dict: float):
    print(f"Total order value is: {total_order_value:.2f}")</pre><p>В приведённом выше коде загрузка данных ограничена простым примером печати, чтобы продемонстрировать, как выполняется задача. В более сложных примерах мы ожидаем загрузку преобразованных данных в хранилище данных или другую систему для последующего использования.</p>
<h2>Установка порядка выполнения задач и зависимостей</h2>
<p>Наконец, нам нужно задать порядок задач. В этом примере мы объявляем их по порядку с помощью переменных. В других примерах, которые мы рассмотрим, можно увидеть использование символа <code>&gt;&gt;</code>, который указывает, что задача слева предшествует задаче справа. Пример такого использования:</p><pre class="urvanov-syntax-highlighter-plain-tag">extract &gt;&gt; transform &gt;&gt; load</pre><p>Однако это не тот способ, который используется в данном примере DAG, и мы рекомендуем следовать следующей структуре:</p><pre class="urvanov-syntax-highlighter-plain-tag">order_data = extract()
    order_summary = transform(order_data)
    load(order_summary["total_order_value"])

example_dag_basic()</pre><p>Как видно из приведённого фрагмента кода, мы делаем функцию <code>extract()</code> вызываемой через <strong>order_data</strong>, который затем передаётся через <code>transform()</code>. Затем результат <strong>transform</strong> передаётся через <code>load()</code> с помощью переменной <strong>order_summary</strong>. Задание такого порядка влияет на ожидаемую последовательность выполнения задач.</p>
<p>Последним, но самым важным шагом является вызов функции <code>example_dag_basic()</code> в конце кода. Если этот шаг не будет выполнен, то DAG никогда не будет работать.</p>
<h2>Выполнение примера DAG</h2>
<p>Чтобы выполнить DAG, необходимо вернуться в интерфейс Airflow. Внутри интерфейса выполните следующие шаги для запуска базового примера DAG и подтверждения завершения операций извлечения, преобразования и загрузки:</p>
<ol>
<li>Перейдите по адресу: <a href="http://localhost:8080" target="_blank" rel="noopener" data-wplink-url-error="true">http://localhost:8080</a></li>
<li>Войдите под пользователем admin с соответствующим паролем.</li>
<li>Убедитесь, что пример DAG отображается в интерфейсе.</li>
<li>В разделе «Actions» справа нажмите зелёную кнопку воспроизведения, чтобы запустить DAG.</li>
</ol>
<p>Другой способ — нажать на переключатель слева от имени DAG, чтобы включить DAG и начать его выполнение по заданному расписанию.</p>
<p>Выполнение задач DAG может занять до 30 минут. После завершения выполните следующие шаги, чтобы подтвердить выполнение:</p>
<ol>
<li>Нажмите на имя DAG <code>example_dag_basic</code>, чтобы перейти к обзору DAG.</li>
<li>Выберите вкладку <strong>Graph</strong>.</li>
<li>Выберите задачу <code>load</code>, нажав на соответствующий блок задачи.</li>
<li>В верхнем меню выберите <strong>Logs</strong>.</li>
<li>Убедитесь, что итоговая сумма заказов равна 1236.70.</li>
</ol>
<h2>Группы задач (Task groups)</h2>
<p>Для управления сложностью и повышения читаемости DAG в Airflow 2 были введены <strong>группы задач (task groups)</strong>. <strong>Группы задач</strong> позволяют организовывать задачи в иерархически сгруппированные подмножества внутри DAG. Такая организация полезна не только для визуального упрощения в интерфейсе Airflow, но и для логического разделения, делая крупные рабочие процессы более управляемыми и понятными.</p>
<p>По мере усложнения DAG, особенно в масштабных корпоративных структурах и моделях, их становится сложнее понимать. Эти группировки позволяют визуально объединять задачи в интерфейсе Airflow, предоставляя уменьшенные обзоры, которые отображают различные секции DAG.</p>
<p>Обычный пример группировки задач — когда у команды есть несколько этапов преобразования или извлечения данных в рамках DAG, и они хотят визуализировать эту секцию максимально просто.</p>
<p>Если бы приведённый выше базовый пример DAG включал два отдельных источника данных, мы могли бы создать задачи для каждого из этих извлечений и сгруппировать их. Пример кода мог бы выглядеть следующим образом:</p><pre class="urvanov-syntax-highlighter-plain-tag">@task_group(group_id='extraction_task_group')
def tg1():
    t1 = extract()
    t2 = EmptyOperator(task_id='task_2')
    t1 &gt;&gt; t2</pre><p>В этом примере мы определили группу задач как <strong>extraction_task_group</strong> и создали две отдельные задачи. Задача <strong>t1</strong> использует исходную функцию <code>extract()</code>, а задача <strong>t2</strong> использует <code>EmptyOperator</code>, который ничего не делает. Мы указываем, что <strong>задача t1</strong> должна выполняться до <strong>t2</strong>.</p>
<p>Мы можем изменить порядок выполнения задач, чтобы отразить новую группу задач как начальную точку. Следующая диаграмма отображает новый визуализированный порядок задач:</p>
<p><strong>Рисунок 2.9: Обновлённый порядок задач</strong></p>
<p><a href="https://datatalks.ru/wp-content/uploads/2025/07/image_2_9_updated_task_order.jpeg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-1891" src="https://datatalks.ru/wp-content/uploads/2025/07/image_2_9_updated_task_order.jpeg" alt="" width="1453" height="175" srcset="https://datatalks.ru/wp-content/uploads/2025/07/image_2_9_updated_task_order.jpeg 1453w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_9_updated_task_order-300x36.jpeg 300w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_9_updated_task_order-1024x123.jpeg 1024w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_9_updated_task_order-768x92.jpeg 768w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_9_updated_task_order-450x54.jpeg 450w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_9_updated_task_order-780x94.jpeg 780w" sizes="(max-width: 1453px) 100vw, 1453px" /></a></p>
<p>Чтобы развернуть группу задач, просто выберите + 2 tasks на группе задач и разверните секцию для просмотра задач внутри:</p>
<p><strong>Рисунок 2.10: Развёрнутый вид новой задачи</strong></p>
<p><a href="https://datatalks.ru/wp-content/uploads/2025/07/image_2_10_expanded_view_of_new_task_airflow.jpeg"><img loading="lazy" decoding="async" class="aligncenter size-full wp-image-1893" src="https://datatalks.ru/wp-content/uploads/2025/07/image_2_10_expanded_view_of_new_task_airflow.jpeg" alt="" width="1449" height="226" srcset="https://datatalks.ru/wp-content/uploads/2025/07/image_2_10_expanded_view_of_new_task_airflow.jpeg 1449w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_10_expanded_view_of_new_task_airflow-300x47.jpeg 300w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_10_expanded_view_of_new_task_airflow-1024x160.jpeg 1024w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_10_expanded_view_of_new_task_airflow-768x120.jpeg 768w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_10_expanded_view_of_new_task_airflow-450x70.jpeg 450w, https://datatalks.ru/wp-content/uploads/2025/07/image_2_10_expanded_view_of_new_task_airflow-780x122.jpeg 780w" sizes="(max-width: 1449px) 100vw, 1449px" /></a></p>
<p>Кроме того, <strong>Apache Airflow поддерживает вложенные группы задач</strong>, позволяя пользователям дополнительно организовывать и структурировать рабочие процессы с большей детализацией. <strong>Вложенные группы задач позволяют создавать иерархические структуры внутри DAG, где одна группа задач может содержать другие группы задач.</strong> Такая иерархическая организация также помогает в отладке и сопровождении рабочего процесса, так как она инкапсулирует связанные задачи в отдельные, управляемые единицы, которые можно разрабатывать, тестировать и отслеживать независимо.</p>
<h2>Триггеры</h2>
<p><strong>Триггеры</strong> определяют условия, при которых выполняется задача или DAG. Airflow предоставляет гибкие механизмы триггеров, позволяющие задачам выполняться на основе расписаний (например, ежедневно или ежечасно), внешних событий или по завершении других задач. Понимание триггеров имеет ключевое значение для планирования задач в соответствии с вашими операционными требованиями и зависимостями. Ранее мы рассматривали планирование и установку интервалов для DAG, но конкретные триггеры внутри DAG и задач могут корректировать время выполнения действий.</p>
<p>Примером триггера в Apache Airflow является <code>TimeDeltaTrigger</code>, который планирует задачи для выполнения через определённый интервал времени после завершения другой задачи. Этот триггер является частью возможностей Airflow по динамическому отображению задач и отложенным операторам, позволяя рабочим процессам динамически адаптироваться на основе условий во время выполнения.</p>
<p>Например, предположим, что в базовом примере DAG мы решаем, что начальная задача преобразования должна выполняться через 30 минут после успешного завершения первой группы задач извлечения. Вы можете использовать <code>TimeDeltaTrigger</code> для этого, установив задержку выполнения задачи агрегации на 30 минут.</p>
<p>Это достигается путём добавления триггера в определение задачи в следующей форме:</p><pre class="urvanov-syntax-highlighter-plain-tag">trigger=TimeDeltaTrigger(timedelta(minutes=30)),</pre><p>Распространённые типы триггеров, используемые командами, включают триггеры на основе времени, зависимостей и событий. Кратко объясним каждый из них.</p>
<p>Триггеры на основе времени включают как выражения CRON, так и планирование на основе интервалов. Выражения <strong>cron</strong> — одни из самых распространённых триггеров, позволяющие запускать задачи через регулярные промежутки времени, заданные синтаксисом cron</p>
<p>(например, <code>0 0 * * *</code> — для задачи, выполняемой ежедневно в полночь).</p>
<p><strong>Планирование на основе интервалов</strong> — это когда задачи могут выполняться через фиксированные промежутки времени (например, каждый час или каждый день) с использованием предопределённого интервала.</p>
<p>Триггеры на основе зависимостей включают завершение предыдущих задач, внешний датчик задач и множество других опций. <strong>Завершение предыдущих задач (upstream task completion)</strong> — это когда задача может быть запущена после успешного выполнения одной или нескольких указанных предыдущих задач. Это критически важно для рабочих процессов, где задачи напрямую зависят от результата или успешности других задач. <strong>Датчик внешних задач (external task sensor)</strong> ожидает завершения определённой задачи в другом DAG перед продолжением. Это полезно для координации задач между различными рабочими процессами.</p>
<p>Последними из наиболее распространённых являются триггеры на основе событий, к которым относятся <strong>Webhooks и датчики</strong>, связанные с электронной почтой или другими сервисами уведомлений. <strong>Задачи-триггеры Webhook</strong> могут запускаться внешними событиями через Webhook. Это полезно для рабочих процессов, которые должны запускаться на основе действий или сигналов из внешних систем. <strong>Датчик электронной почты</strong> запускает задачу при получении электронной почты, удовлетворяющей определённым критериям, что полезно для рабочих процессов, начинающихся в ответ на уведомления по электронной почте.</p>
<p>Другие триггеры, заслуживающие внимания, включают триггеры доступности данных и ручные триггеры, которые могут запускаться вручную пользователем.</p>
<h1>Резюме 2 главы</h1>
<p>В этой главе мы рассмотрели <strong>основы DAG, задач, операторов, XCom, групп задач, триггеров и интерфейса командной строки <code>airflowctl</code></strong>. Мы изучили взаимодействие с консолью Airflow и рассмотрели основы написания DAG с типичным примером, включённым в начальную локально запущенную версию Airflow.</p>
<p>Каждая из этих тем имеет критическое значение для понимания и освоения перед тем, как пытаться создавать крупные ETL-пайплайны или другие случаи использования <strong>ML/AI с Airflow</strong>. Рекомендуется уделить время изучению базового примера DAG и попрактиковаться с различными триггерами или операторами, рассмотренными в этой главе, чтобы вы чувствовали себя уверенно при построении систем более крупного масштаба по мере вашего развития как инженера.</p>
<p>В следующих главах мы расширим этот начальный пример базового DAG с помощью реального ETL-пайплайна и выполним нашу первую загрузку данных.</p>
<p>Сообщение <a href="https://datatalks.ru/apache-airflow-best-practices-chapter-2-core-concepts/">Apache Airflow Best Practices — Глава 2 «Core Concepts»</a> появились сначала на <a href="https://datatalks.ru">DataTalks.RU. Data Engineering / DWH / Data Pipeline</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://datatalks.ru/apache-airflow-best-practices-chapter-2-core-concepts/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Apache Airflow Best Practices &#8212; Глава 1 &#171;Начало работы с Airflow 2.0&#187;</title>
		<link>https://datatalks.ru/apache-airflow-best-practices-chapter-1-getting-started/</link>
					<comments>https://datatalks.ru/apache-airflow-best-practices-chapter-1-getting-started/#respond</comments>
		
		<dc:creator><![CDATA[Data Engineer (Admin)]]></dc:creator>
		<pubDate>Sat, 19 Jul 2025 18:29:49 +0000</pubDate>
				<category><![CDATA[Apache Airflow Best Practices]]></category>
		<category><![CDATA[Apache Airflow]]></category>
		<category><![CDATA[Начало работы с Airflow 2.0]]></category>
		<guid isPermaLink="false">https://datatalks.ru/?p=1769</guid>

					<description><![CDATA[<p>Перевод книги &#171;Apache Airflow Best Practices, by Dylan Intorf, Dylan Storey, Kendrick van Doorn&#187; Packt Publishing подготовлен автором сайта Глава 1 — Начало работы с Airflow 2.0 В современной разработке программного обеспечения и обработке данных оркестрация играет ключевую роль в обеспечении координации и выполнения сложных рабочих процессов. По мере того как организации стремятся управлять постоянно [&#8230;]</p>
<p>Сообщение <a href="https://datatalks.ru/apache-airflow-best-practices-chapter-1-getting-started/">Apache Airflow Best Practices &#8212; Глава 1 &#171;Начало работы с Airflow 2.0&#187;</a> появились сначала на <a href="https://datatalks.ru">DataTalks.RU. Data Engineering / DWH / Data Pipeline</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><em>Перевод книги &#171;Apache Airflow Best Practices, by Dylan Intorf, Dylan Storey, Kendrick van Doorn&#187; Packt Publishing подготовлен автором сайта</em></p>
<h1>Глава 1 — Начало работы с Airflow 2.0</h1>
<p>В современной разработке программного обеспечения и обработке данных оркестрация играет ключевую роль в обеспечении координации и выполнения сложных рабочих процессов. По мере того как организации стремятся управлять постоянно растущими объемами данных и расширяющимся ландшафтом приложений, потребность в эффективной системе оркестрации становится критически важной. С момента выхода <strong>Airflow 2.0</strong> прошло уже некоторое время, и платформа стремительно развивается, расширяя свои возможности. Мы решили обобщить наш опыт эксплуатации Airflow, чтобы помочь другим, продемонстрировав проверенные шаблоны, которые уже зарекомендовали себя на практике.</p>
<p><strong>Наша цель в этой книге</strong> — помочь инженерам и организациям, внедряющим Apache Airflow в качестве решения для оркестрации, максимально эффективно использовать выбранную технологию, направляя их к более обоснованным решениям в процессе адаптации и масштабирования.</p>
<p><strong>В этой главе</strong> мы разберёмся, что такое оркестрация данных и как она применяется в различных отраслях, сталкивающихся с проблемами работы с данными. Кроме того, мы рассмотрим основные преимущества Apache Airflow и его функциональные возможности, которые могут быть полезны для вашей организации. Мы также заглянем вперёд и узнаем, чему вы сможете научиться, читая эту книгу и применяя передовые индустриальные практики оркестрации пайплайнов данных с помощью Apache Airflow. Apache Airflow остаётся лидером отрасли в области оркестрации данных и управления пайплайнами. С этим успехом связаны определённые принципы и подходы, которые были признаны лучшими практиками. Мы рассмотрим некоторые из этих практик и подходов в этой главе, а также определим навыки, необходимые для успешной работы.</p>
<p><strong>В этой главе мы рассмотрим следующие основные темы:</strong></p>
<ul>
<li>Что такое оркестрация данных?</li>
<li>Обзор Apache Airflow</li>
<li>Базовые концепции Airflow</li>
<li>Навыки для эффективного использования Apache Airflow</li>
</ul>
<h1>Что такое оркестрация данных?</h1>
<p>В современном мире, ориентированном на данные, организации сталкиваются с задачей обработки огромных объёмов данных из разнообразных источников. <strong>Оркестрация данных</strong> — это ключ к эффективному управлению этим сложным ландшафтом данных. Она включает координацию, автоматизацию и мониторинг рабочих процессов обработки данных, обеспечивая стабильное выполнение задач и своевременную доставку ценных инсайтов.</p>
<p><strong>Оркестрация в контексте разработки программного обеспечения и data engineering</strong> — это процесс автоматизации и управления выполнением взаимосвязанных задач или процессов для достижения конкретной цели. Эти задачи могут включать обработку данных, планирование выполнения рабочих процессов, развёртывание сервисов и многое другое. <strong>Цель оркестрации</strong> — оптимизировать поток операций, повысить эффективность использования ресурсов и гарантировать согласованное выполнение задач.</p>
<p>Традиционная, ручная оркестрация трудоёмка и подвержена ошибкам, особенно по мере роста сложности пайплайнов. Однако с появлением современных инструментов и фреймворков для оркестрации разработчики могут автоматизировать эти сложные процессы, повышая их эффективность и надёжность.</p>
<h2>Примеры использования в индустрии</h2>
<p>Независимо от отрасли, Apache Airflow может приносить пользу любым командам, занимающимся data engineering или аналитикой. Для лучшего понимания приведём несколько примеров того, как ключевые отрасли, с которыми мы работали, могут использовать этот ведущий оркестратор данных для удовлетворения своих потребностей:</p>
<ul>
<li><strong>E-commerce:</strong> Бренд в сфере электронной коммерции может нуждаться в автоматизированном ETL/ELT пайплайне для автоматизации извлечения, трансформации и загрузки данных из различных источников, таких как продажи, взаимодействие с клиентами и текущие остатки на складе.</li>
<li><strong>Банковский сектор / финтех:</strong> Ведущие финансовые компании могут использовать Apache Airflow для оркестрации обработки транзакционных данных с целью выявления мошеннических операций или рисков в отчётных и биллинговых системах.</li>
<li><strong>Ритейл:</strong> Крупные розничные сети и бренды могут использовать Apache Airflow для автоматизации ML-нагрузок с целью более точного прогнозирования пользовательских предпочтений и покупательского поведения с учётом сезонности или текущей рыночной конъюнктуры.</li>
</ul>
<p>Теперь, когда мы узнали, что такое оркестрация данных, почему она важна для организаций и какие существуют базовые сценарии её применения, давайте перейдём к изучению Apache Airflow — одной из самых популярных платформ и основной темы этой книги.</p>
<h1>Обзор Apache Airflow</h1>
<p><strong>Apache Airflow</strong> известен в сообществе инженеров по данным как наиболее популярная <strong>open source платформа</strong> для разработки, планирования и мониторинга <strong>batch-ориентированных рабочих процессов.</strong><br />
(Документация Apache.org Airflow: <a href="https://airflow.apache.org/docs/apache-airflow/stable/index.html" target="_blank" rel="noopener">https://airflow.apache.org/docs/apache-airflow/stable/index.html</a>)</p>
<p>Apache Airflow зарекомендовал себя как основная open source платформа для оркестрации данных и остаётся лидером благодаря активному сообществу разработчиков. Она предлагает надёжное и гибкое решение задач управления сложными рабочими процессами обработки данных. Airflow позволяет <strong>инженерам по данным, data scientist’ам, инженерам в области искусственного интеллекта (AI)/машинного обучения (ML), а также специалистам по MLOps и DevOps</strong> легко разрабатывать, планировать и мониторить пайплайны данных. Сила Apache Airflow заключается в его способности представлять рабочие процессы данных в виде ориентированных ациклических графов (DAG’ов). Такой интуитивный подход позволяет пользователям визуализировать и понимать взаимосвязи между задачами, что упрощает создание и сопровождение сложных пайплайнов данных. Более того, расширяемость и модульность Airflow позволяют пользователям настраивать платформу под свои специфические нужды, делая её идеальным выбором для компаний любого размера и из любой отрасли.</p>
<h2>Apache Airflow 2.0</h2>
<p><strong>Релиз Apache Airflow 2 в декабре 2020 года</strong> стал одним из крупнейших достижений сообщества с момента создания Airflow как внутреннего решения в Airbnb в 2014 году. Переход на версию 2.0 стал серьёзным шагом для сообщества и включал сотни обновлений и исправлений ошибок после опроса сообщества Airflow в 2019 году. Этот релиз принёс с собой обновлённый пользовательский интерфейс, новый планировщик, исполнитель на базе Kubernetes и более простой способ группировки задач внутри DAG’ов. Это было прорывное достижение, которое определило дорожную карту для последующих релизов, только усиливших ценность Airflow как инструмента для сообщества.</p>
<h2>Ключевые особенности Apache Airflow</h2>
<p>Apache Airflow предлагает множество функций для поддержки разнообразных потребностей организаций и команд. Некоторые из наших любимых касаются сенсоров, группировки задач и операторов, но каждая из этих функций может быть отнесена к одной из следующих категорий:</p>
<ul>
<li><strong>Расширяемость:</strong> Пользователи могут создавать собственные операторы и сенсоры или использовать широкий спектр плагинов, созданных сообществом, что обеспечивает бесшовную интеграцию с различными технологиями и сервисами. Такая расширяемость повышает адаптивность Airflow к разным средам и сценариям использования, делая его потенциал ограниченным лишь воображением инженера.</li>
<li><strong>Динамичность:</strong> Платформа поддерживает динамические рабочие процессы, то есть количество задач и их конфигурации могут определяться во время выполнения, на основе переменных, внешних сенсоров или данных, полученных в ходе выполнения. Эта функция делает Airflow более гибким, поскольку рабочие процессы могут адаптироваться к изменяющимся условиям или входным параметрам, что приводит к лучшему использованию ресурсов и повышению эффективности.</li>
<li><strong>Масштабируемость:</strong> Распределённая архитектура Airflow обеспечивает масштабируемость для обработки крупномасштабных и ресурсоёмких рабочих процессов. По мере роста бизнеса и увеличения потребностей в обработке данных Airflow способен удовлетворить эти требования, распределяя задачи между несколькими воркерами, сокращая время обработки и повышая общую производительность.</li>
<li><strong>Встроенный мониторинг:</strong> Airflow предоставляет веб-интерфейс для мониторинга состояния рабочих процессов и отдельных задач. Этот интерфейс позволяет пользователям визуализировать выполнение задач и просматривать логи, способствуя прозрачности и упрощая отладку. Получая информацию о производительности рабочих процессов, пользователи могут оптимизировать свои процессы и выявлять потенциальные узкие места.</li>
<li><strong>Экосистема:</strong> Airflow бесшовно интегрируется с широким спектром технологий и облачных провайдеров. Эта интеграция позволяет пользователям получать доступ к различным источникам данных и сервисам, что упрощает проектирование комплексных рабочих процессов, взаимодействующих с различными системами. Работаете ли вы с базами данных, облачным хранилищем или другими инструментами — Airflow способен обеспечить связующее звено между компонентами.</li>
</ul>
<p><strong>Apache Airflow</strong> — это результат многолетней разработки в open source и тщательно продуманного дизайна от сотен контрибьюторов. Это ведущий инструмент оркестрации данных, и изучение его ключевых возможностей поможет вам стать лучшим инженером по данным и менеджером.</p>
<h2>Взгляд вперёд</h2>
<p>На протяжении всей книги мы будем исследовать основные функции Apache Airflow, предоставляя вам знания для использования его полного потенциала на пути оркестрации данных. Ключевые темы включают следующее:</p>
<ul>
<li><strong>Почему использовать Airflow?</strong>: Принципы, навыки и базовые концепции</li>
<li><strong>Основы Airflow:</strong> Понимание основных понятий (DAG’и, задачи, операторы, deferrable-компоненты, подключения и т. д.), компонентов Airflow и основы написания DAG’ов</li>
<li><strong>Типовые сценарии использования:</strong> Раскрытие потенциала Airflow с помощью ETL-пайплайнов, пользовательских плагинов и оркестрации нагрузок между системами</li>
<li><strong>Масштабирование с командой:</strong> Подготовка инстанса Airflow к продакшн-нагрузкам с использованием CI/CD, мониторинга и облака</li>
</ul>
<p>К окончанию этой книги вы получите всестороннее понимание лучших практик работы с Apache Airflow, что позволит вам строить надёжные, масштабируемые и эффективные пайплайны данных, способствующие успеху вашей организации.</p>
<p>Давайте начнём это практическое руководство по оркестрации пайплайнов данных с использованием Apache Airflow и раскроем истинный потенциал принятия решений на основе данных.</p>
<h2>Основные концепции Airflow</h2>
<p><strong>Apache Airflow</strong> — это динамичный, расширяемый и гибкий фреймворк, позволяющий создавать рабочие процессы как код. Airflow позволяет описывать эти автоматизированные рабочие процессы в виде кода. Это обеспечивает лучшую версионируемость, разработку через CI/CD, лёгкость в тестировании и возможность использовать расширяемые компоненты и операторы от активного сообщества контрибьюторов.</p>
<p>Airflow известен своим подходом к планированию задач и рабочих процессов. Он может использовать планирование по CRON или встроенные функции планировщика. Кроме того, такие функции, как <strong>backfilling</strong> (выполнение пайплайнов задним числом), позволяют переисполнять пайплайны и обновлять их при изменении логики. Это означает наличие мощных эксплуатационных компонентов, которые необходимо учитывать при проектировании.</p>
<p>Следование этим рекомендациям поможет вам заложить основу для масштабирования ваших развёртываний Airflow и повысить эффективность рабочих процессов как с точки зрения разработки, так и эксплуатации.</p>
<h2>Почему Airflow может вам не подойти</h2>
<p>Прежде чем мы перейдём к принципам Apache Airflow, стоит на мгновение остановиться и признать случаи, когда он не является хорошим выбором для организаций. Хотя каждое из следующих утверждений, вероятно, может быть опровергнуто достаточно мотивированными и умными инженерами (а таких мы все знаем немало), они в целом считаются антипаттернами и их следует избегать.</p>
<p><strong>Некоторые из этих антипаттернов включают следующее:</strong></p>
<ul>
<li>Команды, в которых отсутствует или ограничен опыт программирования на Python. Реализация DAG’ов на Python может быть сложным процессом и требует активного опыта для поддержки кода.</li>
<li>Стриминговые или не batch-ориентированные рабочие процессы и пайплайны, где сценарий использования требует немедленного обновления. Airflow предназначен для batch-ориентированных и запланированных задач.</li>
</ul>
<h2>Когда выбирать Airflow</h2>
<p><strong>Airflow лучше всего использовать для реализации «batch-ориентированных» запланированных пайплайнов данных.</strong> Часто сценарии использования включают ETL/ELT, обратный ETL, ML, AI и бизнес-аналитику (BI). На протяжении всей книги мы рассмотрим основные сценарии использования, наблюдаемые в различных лидирующих компаниях отрасли. Некоторые ключевые сценарии включают следующее:</p>
<ul>
<li><strong>ETL-пайплайны данных:</strong> Почти каждая реализация Airflow помогает автоматизировать задачи такого типа, будь то консолидация данных в хранилище данных или перемещение данных между различными инструментами</li>
<li><strong>Разработка и распространение пользовательских плагинов для организаций с уникальным стеком и потребностями, которые не были учтены open source сообществом:</strong> Airflow позволяет легко адаптировать окружение и экосистему под ваши нужды</li>
<li><strong>Расширение функциональности UI с помощью плагинов:</strong> Модификация и настройка интерфейса для добавления новых представлений, графиков и виджетов, интегрирующихся с внешними системами</li>
<li><strong>ML-воркфлоу и оркестрация между системами:</strong> Команды, создающие и поддерживающие ML-воркфлоу, часто зависят от Airflow для автоматизации обучения, трансформации и оценки моделей</li>
</ul>
<p>Каждый из этих сценариев требует разного набора базовых навыков для реализации и масштабирования в поддержку крупной организации.</p>
<h2>Zen of Python</h2>
<p><strong>Zen of Python</strong> — отличный ориентир для любого Python-разработчика при принятии решений при написании кода; некоторые его части особенно полезны для инженеров по данным, работающих с Airflow. Это набор из 19 «руководящих принципов» написания кода на Python от пионера в области разработки программного обеспечения Тима Питерса. Прежде чем перейти к деталям того, что, на наш взгляд, наиболее ценно при разработке на Apache Airflow, давайте <strong>ознакомимся с Zen of Python:</strong></p>
<ol>
<li>Красивое лучше, чем уродливое.</li>
<li>Явное лучше, чем неявное.</li>
<li>Простое лучше, чем сложное.</li>
<li>Сложное лучше, чем запутанное.</li>
<li>Плоское лучше, чем вложенное.</li>
<li>Разреженное лучше, чем плотное.</li>
<li>Читаемость имеет значение.</li>
<li>Частные случаи недостаточно особенные, чтобы нарушать правила.</li>
<li>Хотя практичность важнее чистоты.</li>
<li>Ошибки никогда не должны замалчиваться.</li>
<li>Если только они не замалчиваются явно.</li>
<li>В условиях неоднозначности откажитесь от соблазна угадывать.</li>
<li>Должен быть один — и желательно только один — очевидный способ сделать это.</li>
<li>Хотя он может быть не очевиден с первого взгляда, если только вы не голландец.</li>
<li>Сейчас лучше, чем никогда.</li>
<li>Хотя «никогда» часто лучше, чем прямо сейчас.</li>
<li>Если реализацию сложно объяснить — это плохая идея.</li>
<li>Если реализацию легко объяснить — возможно, это хорошая идея.</li>
<li>Пространства имён — отличная штука, давайте использовать их больше!</li>
</ol>
<p><strong>Несколько ключевых руководящих принципов, которые помогут вам в обучении:</strong></p>
<ul>
<li><strong>Явное лучше, чем неявное.</strong> Особенно в задачах, связанных с данными, важно быть явным в своих намерениях — это критично как для защиты от ошибок, так и для поддержки. Отлаживать продакшн-пайплайн проще, когда вы точно знаете, каков был изначальный замысел.</li>
<li><strong>Простое лучше, чем сложное.</strong> Сложное лучше, чем запутанное. Лучшие практики проектирования DAG’ов предполагают наличие множества простых задач, которые затем объединяются в сложный рабочий процесс. Это, как правило, означает, что рабочие процессы лучше справляются с восстановлением после ошибок и повторными запусками, их проще расширять в будущем и легче отлаживать при сбоях.</li>
<li><strong>Частные случаи недостаточно особенные, чтобы нарушать правила.</strong> Хотя практичность важнее чистоты. Согласованность — ключ к сопровождаемому коду, поэтому по возможности нужно стремиться к единообразному подходу. Однако инженерным командам нужно добиваться результата, так что вы должны быть готовы учитывать особые случаи как при проектировании, так и при реализации.</li>
<li><strong>Ошибки никогда не должны замалчиваться.</strong> Если только они не замалчиваются явно. Это частный случай противопоставления явного и неявного. Особенно в работе с данными ошибки должны обрабатываться явно — либо через исключения, либо логироваться для последующего анализа.</li>
<li><strong>Сейчас лучше, чем никогда.</strong> <strong>Хотя “никогда” часто лучше, чем прямо сейчас.</strong> При построении DAG’ов и всей операционной архитектуры возникает естественное стремление сразу «подготовить систему к будущему» или избыточно абстрагировать — не делайте этого. Следует проявлять особую осторожность при реализации интерфейсов и систем: в конечном итоге вы будете отвечать за их поддержку, и усилия, вложенные сейчас, могут оказаться нужнее в другом месте.</li>
<li><strong>Если реализацию сложно объяснить — это плохая идея. Если реализацию легко объяснить — возможно, это хорошая идея.</strong> Это ещё один случай темы простого/сложного/запутанного, особенно актуальный на этапе проектирования. Если вы не можете объяснить что-то достаточно просто — вероятно, это плохая идея для реализации. Данные постоянно меняются и эволюционируют, и ваши системы должны уметь меняться вместе с ними.</li>
</ul>
<h2>Идемпотентность</h2>
<p><strong>Идемпотентность</strong> описывает операцию, которую можно применять многократно без изменения результата после первого применения. Наш опыт показывает, что большинство эксплуатационных проблем можно избежать, если учитывать этот принцип с самого начала и на всех этапах проектирования задач в Airflow.</p>
<h2>Конфигурация как код</h2>
<p><strong>Конфигурация как код</strong> — это принцип проектирования программного обеспечения, при котором конфигурационные настройки и параметры хранятся в виде кода отдельно от основного исполняемого кода. Это позволяет определять параметры конфигурации как код и напрямую интегрировать их в кодовую базу с использованием таких конструкций, как переменные, классы или функции. Такой подход позволяет разработчикам управлять поведением приложения и изменять его более системно и под контролем версионности. Этот подход даёт преимущества в виде повышения согласованности, упрощения совместной работы и более удобных рабочих процессов, которые позволяют применять общие парадигмы разработки и эксплуатации программного обеспечения.</p>
<p>Эта парадигма очень мощная и действительно позволяет обращаться с конфигурациями как с полноценным кодом; однако разработчики должны помнить, что код предназначен для описания дискретных конфигурационных единиц, и некоторые «очевидные» паттерны программирования могут привести к нежелательным (или недокументированным) зависимостям, если не обеспечить чётких границ конфигурации в кодовых базах, описывающих ваши рабочие процессы.</p>
<p>Теперь, когда мы на мгновение нашли свой дзен в написании кода на Python и поделились ключевыми соображениями о том, когда стоит выбирать Apache Airflow, давайте перейдём к навыкам, которые наиболее необходимы.</p>
<h1>Навыки для эффективного использования Apache Airflow</h1>
<p>Недостаточно просто знать Airflow; существуют и другие навыки, которые необходимы и будут влиять на эффективность вашей команды в обеспечении ценности с помощью Airflow. Читая эту книгу, подумайте о навыках вашей команды в следующих областях, чтобы понять, нужна ли вам дополнительная поддержка или возможности для максимально эффективного использования платформы:</p>
<ul>
<li><strong>Python:</strong> DAG’и и плагины пишутся на Python. Следовательно, для эффективного внедрения Airflow вы должны уметь писать и понимать код на Python.</li>
<li><strong>Тестирование приложений:</strong> В вашей компании должны быть достаточно зрелые процессы и практики тестирования, чтобы гарантировать, что написанные вами плагины и DAG’и будут работать так, как задумано. Команда, ответственная за работоспособность вашего инстанса(ов) Airflow и плагинов, должна уметь помогать другим командам в подготовке к обновлениям, а команды, пишущие рабочие процессы, — выполнять автоматизированные тесты до вывода в продакшн.</li>
<li><strong>Экспертиза в предметной области:</strong> Знание своей предметной области данных — самый критически важный навык для успешного внедрения Airflow. Независимо от того, насколько вы технически подкованы, если вы не понимаете бизнес-домена, с которым работаете, вам не удастся добиться успеха ни в краткосрочной, ни в долгосрочной перспективе.</li>
<li><strong>Мониторинг/оповещение приложений:</strong> У вас должны быть достаточно зрелые возможности для наблюдения, мониторинга и оповещения, чтобы эффективно использовать Airflow. Некоторые распространённые действия включают мониторинг активности приложения (например, health checks), отправку оповещений дежурным членам команды и автоматические сообщения о статусе.</li>
</ul>
<p>Эти навыки являются ключевыми для максимально эффективного использования Apache Airflow и должны периодически пересматриваться, чтобы отслеживать прогресс и возможности для улучшения в разных областях.</p>
<h1>Резюме по 1 главе</h1>
<p>В этой главе мы познакомились с основами того, что такое оркестрация данных и с какими проблемами сегодня сталкиваются компании и инженеры. Кроме того, мы представили Apache Airflow — ведущий инструмент оркестрации данных и управления рабочими процессами. Мы также рассмотрели, чего ожидать в ходе прочтения этой книги. Важно помнить, что для успешной работы с Apache Airflow требуются несколько базовых инструментов и областей знаний. Хотя эти области необходимы для наилучшего использования, каждая из них поддаётся изучению и может быть освоена достаточно быстро.</p>
<p>В основе использования Airflow лежит код на Python. Чтобы быть лучшим инженером по данным, работающим с Airflow, вам нужно понимать основные концепции кода на Python и то, как он будет оркестровать ваш стек инструментов работы с данными. Потратив время на изучение этих базовых концепций и понимание сценариев использования, решаемых с помощью Airflow, вы сможете строить масштабируемые системы и находить возможности для оптимизации.</p>
<p>В следующей главе мы представим основы DAG’ов и задач. Мы рассмотрим новые советы по декораторам задач и организации групп задач, а также разберём пример. Вы можете рассчитывать на то, что ваш первый DAG в Airflow будет запущен и готов к эксплуатации.</p>
<p>Сообщение <a href="https://datatalks.ru/apache-airflow-best-practices-chapter-1-getting-started/">Apache Airflow Best Practices &#8212; Глава 1 &#171;Начало работы с Airflow 2.0&#187;</a> появились сначала на <a href="https://datatalks.ru">DataTalks.RU. Data Engineering / DWH / Data Pipeline</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://datatalks.ru/apache-airflow-best-practices-chapter-1-getting-started/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
