This guide shows you how to write an Apache Airflow directed acyclic graph (DAG) that runs in a Cloud Composer environment.
Structuring a DAG
An Airflow DAG is defined in a Python file and is composed of the following components: A DAG definition, operators, and operator relationships. The following code snippets show examples of each component out of context:
A DAG definition.
Airflow 2
Airflow 1
Operators to describe the work to be done. An instantiation of an operator is called a task.
Airflow 2
Airflow 1
Task relationships to describe the order in which the work should be completed.
Airflow 2
Airflow 1
The following workflow is a complete working example and is composed of two tasks: a hello_python task and a goodbye_bash task:
Airflow 2
Airflow 1
See the Airflow tutorial and Airflow concepts for more information on defining Airflow DAGs.
Operators
The following examples show a few popular Airflow operators. For an authoritative reference of Airflow operators, see the Apache Airflow API Reference or browse the source code of the core, contrib, and providers operators.
BashOperator
Use the BashOperator to run command-line programs.
Airflow 2
Airflow 1
Cloud Composer runs the provided commands in a Bash script on a worker. The worker is a Debian-based Docker container and includes several packages.
PythonOperator
Use the PythonOperator to run arbitrary Python code.
Cloud Composer runs the Python code in a container that includes packages for the Cloud Composer image version used in your environment.
To install additional Python packages, see Installing Python Dependencies.
Google Cloud Operators
Use the Google Cloud Airflow operators to run tasks that use Google Cloud products. Cloud Composer automatically configures an Airflow connection to the environment's project.
BigQuery operators query and process data in BigQuery.
Airflow 2
Airflow 1
Dataflow operators run Apache Beam jobs in Dataflow.
Cloud Data Fusion operators manage and run Cloud Data Fusion pipelines.
Dataproc operators run Hadoop and Spark jobs in Dataproc.
Datastore operators read and write data in Datastore.
AI Platform operators run training and prediction jobs in AI Platform.
Cloud Storage operators read and write data in Cloud Storage.
EmailOperator
Use the EmailOperator to send email from a DAG. To send email from a Cloud Composer environment, you must configure your environment to use SendGrid.
Airflow 2
Airflow 1
Notifications
Set email_on_failure to True to send an email notification when an operator
in the DAG fails. To send email notifications from a Cloud Composer
environment, you must
configure your environment to use SendGrid.
Airflow 2
Airflow 1
Guidelines
Place any custom Python libraries in a DAG's ZIP archive in a nested directory. Do not place libraries at the top level of the DAGs directory.
When Airflow scans the
dags/folder, Airflow only checks for DAGs in Python modules that are in the top-level of the DAGs folder and in the top level of a ZIP archive also located in the top-leveldags/folder. If Airflow encounters a Python module in a ZIP archive that does not contain bothairflowandDAGsubstrings, Airflow stops processing the ZIP archive. Airflow returns only the DAGs found up to that point.For fault tolerance, do not define multiple DAG objects in the same Python module.
Do not define subDAGs as top-level objects.
In general, Airflow picks up DAG objects in the global namespace of a module in the
dags/directory as top-level DAGs. Any subDags defined as top-level objects execute on their own schedules in addition to the schedules of other DAGs that embed the subDags.Place files that are required at DAG parse time in the
dags/directory not in thedata/directory. Thedata/directory is not mounted in the webserver.
FAQs for writing DAGs
How do I minimize code repetition if I want to run the same or similar tasks in multiple DAGs?
We suggest defining libraries and wrappers to minimize the code repetition.
How do I reuse code between DAG files?
Put your utility functions in
a local Python library
and import the functions. You can reference the functions in any DAG located
in the dags/ folder in your environment's bucket.
How do I minimize the risk of different definitions arising?
For example, you have two teams that want to aggregate raw data into revenue metrics. The teams write two slightly different tasks that accomplish the same thing. Define libraries to work with the revenue data so that the DAG implementers must clarify the definition of revenue that's being aggregated.
How do I set dependencies between DAGs?
This depends on how you want to define the dependency.
If you have two DAGs (DAG A and DAG B) and you want DAG B to trigger after DAG A, you can put
a TriggerDagRunOperator
at the end of Dag A.
If DAG B depends only on an artifact that DAG A generates, such as a Pub/Sub message, then a sensor might work better.
If DAG B is integrated closely with DAG A, you might be able to merge the two DAGs into one DAG.
How do I pass unique run IDs to a DAG and its tasks?
For example, you want to pass Dataproc cluster names and file paths.
You can generate a random unique ID by returning str(uuid.uuid4()) in
a PythonOperator. This places the ID into
XComs
so that you can refer to the ID in other operators via templated fields.
Before generating a uuid, consider whether a DagRun-specific ID would be
more valuable. You can also reference these IDs in Jinja substitutions by
using macros.
How do I separate tasks in a DAG?
Each task should be an idempotent unit of work. Consequently, you should avoid
encapsulating a multi-step workflow within a single task, such as a complex
program running in a PythonOperator.
The Airflow-native mechanism for re-using workflow definition code is
the SubDagOperator.
However, there are caveats when using this operator in Cloud Composer.
Should I define multiple tasks in a single DAG to aggregate data from multiple sources?
For example, you have multiple tables with raw data and want to create daily aggregates for each table. The tasks are not dependent on each other. Should you create one task and DAG for each table or create one general DAG?
If you are okay with each task sharing the same DAG-level properties, such as
schedule_interval, then it makes sense to define multiple tasks in a single
DAG. Otherwise, to minimize code repetition, multiple DAGs can be generated
from a single Python module by placing them into the module's globals().
How do I limit the number of concurrent tasks running in a DAG?
For example, you want to avoid exceeding API usage limits/quotas or avoid running too many simultaneous processes.
You can define Airflow pools in the Airflow web UI and associate tasks with existing pools in your DAGs.
FAQs for using operators
Should I use the DockerOperator?
We do not recommend using
the DockerOperator,
unless it's used to launch containers on a remote Docker installation (not within an environment's cluster). In a Cloud Composer environment the operator does not have access to Docker daemons.
Instead, use KubernetesPodOperator or GKEStartPodOperator. These operators can launch Kubernetes pods into Kubernetes or GKE clusters respectively. Note that we don't recommend launching pods into an environment's cluster, as this can lead to resource competition.
Should I use the SubDagOperator?
We do not recommend using the SubDagOperator.
Although the SubDagOperator can provide encapsulation, SubDag tasks require
a task slot. If an Airflow worker running the SubDag task crashes, all tasks within the SubDag fail, resulting in unreliable workflows.
Should I run Python code only in PythonOperators to fully separate Python operators?
Depending on your goal, you have a few options.
If your only concern is maintaining separate Python dependencies, you
can use the PythonVirtualenvOperator.
Consider
using the KubernetesPodOperator.
This operator allows you to define Kubernetes pods and run the pods in other clusters.
How do I use the KubernetesPodOperator outside Google Cloud?
You can mount a config file that specifies how to authenticate with the
GKE cluster and place the file in the /data folder in your environment's bucket.
This folder is mounted across the Cloud Composer environment.
How do I add custom binary or non-PyPI packages?
You can install packages hosted in private package repositories.
You can also
use the KubernetesPodOperator to run a Kubernetes pod with your own image built with custom packages.
How do I uniformly pass arguments to a DAG and its tasks?
You can use Airflow's built-in support for Jinja templating to pass arguments that can be used in templated fields.
When does template substitution happen?
Template substitution occurs on Airflow workers just before the pre_execute
function of an operator is called. In practice, this means that templates are not substituted until just before a task runs.
How do I know which operator arguments support template substitution?
Operator arguments that support Jinja2 template substitution are explicitly marked as such.
Look for the template_fields field in the Operator definition,
which contains a list of argument names that will undergo template
substitution.
For example, see
the BashOperator,
which supports templating for the bash_command and env arguments.
What's next
- Troubleshooting DAGs
- Apache Airflow Tutorial
- Apache Airflow API Reference
- Core Airflow operators on GitHub. See the branch for your Airflow release.
- Contrib Airflow operators on GitHub. See the branch for your Airflow release.

