| von Tim Otter

setup of Apache Airflow 2.0 in Azure App-Service with Terraform

1. introduction

Apache Airflow is an open source based process management platform for programmatic workflow creation, scheduling and monitoring.  

When workflows are defined as code, they are easier to maintain, version, and test. By creating so-called DAGs (Directed Acyclic Graphs), the entire complex process can be broken down into individual, interdependent steps. The user interface makes it easy to visualize the different operations in running pipelines, to monitor them and to fix problems if necessary. 

Abbildung 1: Airflow GUI Quelle

 

In Azure stehen verschiedene Möglichkeiten zur Verfügung, um Airflow aufzusetzen. Die Nutzung von Kubernetes ist dabei ein oft gewählter Ansatz, für den es deshalb auch schon einige nützliche und gut dokumentierte Einträge gibt. Dieser Blog beschäftigt sich mit einer weiteren Alternative, dem Azure App Service. Da Airflow 2.0 einen Multicontainer Ansatz empfiehlt, gehen wir hierbei besonders auf die Nutzung der Docker Compose Option im App Service ein, welche sich bisher noch in der Vorschauversion befindet. 

Weiterhin wird die Automatisierung der zugrundeliegenden Servicearchitektur mit Hilfe von Azure DevOps Pipelines unter Verwendung von Terraform aufgezeigt. 

 

2. Docker Compose in Azure App Service 

Die ausführliche Apache Airflow Quick Start Dokumentation ist unter diesem Link https://airflow.apache.org/docs/apache-airflow/stable/index.html zu finden. Das Docker Compose Multicontainer Setup für den Quick-Start wird in der folgenden docker-compose.yaml Datei beschrieben https://airflow.apache.org/docs/apache-airflow/stable/docker-compose.yaml. 

 

2.1 Airflow Container Setup

Das Docker Compose Setup beinhaltet folgende Container: 

  • Airflow-Webserver – Über den Webserver steht eine GUI zur Verfügung, die das Management und Monitoring der Workflows steuert. 
  • Airflow-Scheduler – Der Scheduler überwacht alle Tasks und DAG’s und startet die Task Instanzen sobald die Abhängigkeiten erfüllt sind. 
  • Airflow-Worker – Der Worker führt die vom Scheduler zugewiesenen Tasks aus. 
  • Airflow-Init – Airflow Initialisierungsservice 
  • PostgreSQL – Datenbank zur Speicherung der Metadaten 
  • Redis – Broker, der die Nachrichtenübermittlung von Scheduler zum Worker übernimmt 
  • Flower – App zur Überwachung der Applikation 

 

2.2. Tipps und Tricks beim Aufsetzen von Airflow mit docker-compose

Da sich das Docker Compose Feature des Azure App Service noch in der Vorschau befindet, sind einige Dinge zu beachten, bevor man die Anwendung mit dem docker-compose.yaml starten kann: 

  • Aktuell sind nur bestimmte Optionen von Docker Compose verfügbar. So wird beispielsweise die Option „depends_on“ nicht unterstützt, was zu Schwierigkeiten bei der Startreihenfolge der einzelnen Container führen kann. Als Alternative müsste der „command” Befehl genutzt werden, was allerdings eine aufwendigere manuelle Konfiguration bedeutet. Eine allgemeine Auflistung der unterstützten und nicht unterstützten Docker Compose Optionen wird hier dargestellt https://docs.microsoft.com/en-us/azure/app-service/configure-custom-container?pivots=container-linux#configure-multi-container-apps. 
  • Im aufgeführten docker-compose.yaml wird die Erweiterung „x-airflow-common“ definiert, welche mit Hilfe von YAML „anchors“ in mehreren Containern referenziert werden kann. Azure App Service unterstützt diese Art der Referenz jedoch nicht, weshalb die vorhandene Quick Start Dokumentation in Bezug auf die Implementierung der wiederverwendbaren Konfigurationselemente nicht übernommen werden kann. 
  • Eine weitere Fehlerquelle beim Setup mit Docker Compose liegt in der Beschränkung der maximalen Zeilenlänge für das Docker Image. Diese liegt aktuell bei 4000 Zeichen. 
  • Außerdem bietet Azure App Service mit der vorhandenen Docker Compose Version nicht die Möglichkeit einer vorgeschalteten Authentifizierung unter der Verwendung von Azure Active Directory. Weiterhin sind wichtige Features wie beispielsweise „CORS“ oder „Managed Identities“ nicht verfügbar.  

 

Für eine erste lauffähige Version bearbeiten wir das docker-compose.yaml daher wie folgt: 

  • Die im Block „x-airflow-common“ definierten Variablen können als Umgebungsvariablen in Azure App Service angelegt werden. Dabei bietet sich zusätzlich der Vorteil, dass Secrets über eine Key Vault Referenz aufgelöst werden können (siehe AIRFLOW__CELERY__RESULT_BACKEND im Abbildung 2). Dazu muss lediglich „@Microsoft.KeyVault(VaultName=<key vault name>;SecretName=<secret name>)“ als Wert  angegeben werden.

 

Abbildung 2: Azure App Service UmgebungsVariablen

 

  • Da die Initialisierung oder das Updaten der Metadatenbank lediglich beim ersten Setup oder bei Versionsänderungen benötigt wird, bietet es sich an, die Durchführung in einen separaten Schritt auszulagern. Das kann beispielsweise über eine Azure DevOps Pipeline erfolgen, auf die in Kapitel 3 näher eingegangen wird.
  • Möchte man die Dags in einen Azure Blob Storage mounten, so kann im Azure App Service ein Mount Pfad definiert werden.

 

Abbildung 3: Azure App Service Mount Pfad
 Ein erstes docker-compose.yaml kann dann wie folgt aussehen:
--- 
  version: '3' 
 
  services: 
 
    redis: 
      image: redis:latest 
      restart: always 
 
    airflow-webserver: 
      image: apache/airflow:2.2.2 
      command: webserver 
      volumes: 
        - dags:/opt/airflow/dags 
      restart: always 
      ports: 
        - 8080:8080 
 
    airflow-scheduler: 
      image: apache/airflow:2.2.2 
      command: scheduler 
      volumes: 
        - dags:/opt/airflow/dags 
      restart: always 
 
    airflow-worker: 
      image: apache/airflow:2.2.2 
      volumes: 
        - dags:/opt/airflow/dags 
      command: celery worker 
      restart: always

 

3. Automatisierung der Infrastruktur in Azure DevOps

Für die Versionskontrolle und das Release Management in der Microsoft Azure Welt empfiehlt sich Azure DevOps als Plattform zur Überwachung und Steuerung des Software Lebenszyklus.

Eine Pipeline wird durch eine YAML Datei in dem ausgewählten Repository definiert. Die erstellte Pipeline kann in Azure DevOps Pipelines ausgewählt, gestartet und gegebenenfalls angepasst werden. Grundsätzlich beschreibt die Pipeline einen CI/CD Prozess, der aus einer oder mehreren Ebenen besteht.

Eine Pipeline für die Infrastruktur von Airflow kann beispielsweise aus den folgenden Schritten bestehen:

Bauen und Hochladen des Images in eine Azure Container Registry

Aufsetzen der Ressourcen (Datenbank, App Service, Key Vault etc.) mit Hilfe von Terraform

Initialisierung/Updaten der Metadatenbank

 

3.1. Docker Build und Push

Da in den meisten Fällen individuelle Anpassungen am Airflow Base-Image (https://hub.docker.com/r/apache/airflow/) notwendig sind, empfiehlt es sich zunächst, das zugrundeliegende Docker Image in einer privaten Docker Registry abzulegen.

Zum Bauen und Pushen des Images kann der von Azure DevOps vorgefertigte Docker Task verwendet werden. Hierzu müssen lediglich die notwendigen Inputs (zum Beispiel Docker Registry) angegeben werden. Des Weiteren können die Tags der Images auch durch eine konfigurierbare Variable gesteuert werden. Der Docker Build und Push Schritt kann dabei wie folgt aussehen:

steps: 
  - task: Docker@2 
    displayName: Build and push an image to container registry 
    inputs: 
      command: build 
      arguments: '--build-arg AIRFLOW_VERSION=$(airflowVersion)' 
      repository: $(imageRepository) 
      dockerfile: $(dockerfilePath) 
      containerRegistry: $(dockerRegistryServiceConnection) 
      tags: | 
        $(airflowVersion) 
  - task: Docker@2 
    displayName: Push to docker registry 
    inputs: 
      command: push 
      repository: $(imageRepository) 
      containerRegistry: $(dockerRegistryServiceConnection) 
      tags: | 
        $(airflowVersion)

3.2 Terraform Ressourcen

Als plattformunabhängiges IaC-Tool bietet Terraform die Möglichkeit, verwendete Ressourcen unabhängig voneinander zu provisionieren und in einem baukastenähnlichen Prinzip miteinander zu verknüpfen. 

Um in Azure DevOps Terraform Ressourcen anzulegen, empfiehlt sich die Verwendung des vorgefertigten Terraform Cli Tasks (https://marketplace.visualstudio.com/items?itemName=charleszipp.azure-pipelines-tasks-terraform). 

Damit der Terraform Task in der Pipeline die nötigen Berechtigungen zum Anlegen von Azure Ressourcen in unserer Subscription hat, muss zunächst ein Service Principal angelegt werden (https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/service_principal_client_secret). Als Alternative zum manuellen Konfigurieren des Service Principals in Azure Active Directory gibt es ein einfaches Vorgehen in Azure DevOps. Dazu kann unter den Projekt Einstellungen eine neue Service Verbindung vom Typ „Azure Resource Manager“ angelegt werden. Als Authentifizierungsmethode wählen wir „Service Principal“ und unser Scope Level ist die gewünschte Subscription.  

Abbildung 4: Einrichtung einer Service Verbindung

 

Nachdem die Service Verbindung erfolgreich erstellt wurde, kann der Service Principal in Verbindung mit dem zugehörigen Client Secret zur Authentifizierung verwendet werden. Dies kann über die Terraform Umgebungsvariablen ARM_TENANT_ID, ARM_CLIENT_ID, ARM_CLIENT_SECRET und ARM_SUBSCRIPTION_ID erfolgen. Die benötigten Passwörter können unter „Manage Service Principal“ eingesehen und verwaltet werden. Damit die Secrets nicht öffentlich im Code definiert werden müssen, bietet es sich an, diese in einem Azure Key Vault zu speichern. Mit Hilfe des Azure Key Vault Tasks können diese dann als Secret Variablen in die Pipeline übergeben werden.  

 

3.3 Initialisierung/Updaten der Metadatenbank 

Nachdem alle Ressourcen angelegt wurden, erfolgt die Initialisierung oder das Updaten der Metadatenbank. Dazu kann beispielsweise der Airflow Init Docker Container, bei dem die Umgebungsvariable AIRFLOW_DB_UPGRADE auf „true“ gesetzt wird, gestartet werden. In der Pipeline kann dafür der Docker Compose Task verwendet werden. Im zugehörigen docker-compose.yaml ist darauf zu achten, dass die Datenbank Verbindung über die Variable AIRFLOW__CORE__SQL_ALCHEMY_CONN richtig konfiguriert ist und die Airflow Version mit der verwendeten Version im App Service übereinstimmt. Das sollte über eine Pipeline Variable, die dann über den Task Input „dockerComposeFileArgs“ übergeben wird, sichergestellt werden. Durch die Automatisierung in der Pipeline ist somit gewährleistet, dass die Anwendung das richtige Datenbank Schema verwendet. 

 

3.4 Allgemeine Tipps 

  • Die Airflow Version wird an verschiedenen Stellen in der Pipeline verwendet. Um eine einheitliche Versionierung zu garantieren, sollte die Version daher als Pipeline Variable gesetzt werden. Wird das docker-compose.yaml für den App Service über Terraform eingelesen, so kann die Variable über die Terraform Funktion „templateFile“ übergeben werden.  
  • Um zu verhindern, dass ungewollte Infrastruktur Änderungen durch die Pipeline vorgenommen werden, empfiehlt es sich, zunächst den Terraform Plan („terraform plan“) Schritt durchzuführen. Das Ausführen des Terraform Plans („terraform apply“) sollte anschließend mit Hilfe des Azure DevOps Tasks „manual validation“ vom User manuell bestätigt werden.

 

4. Fazit

Der Azure App Service bietet eine einfache Möglichkeit für das Setup von Airflow. Allerdings gibt es aufgrund der aktuellen Vorschauversion noch einige nicht vorhandene Features, wie beispielsweise die Möglichkeit einer vorgeschalteten Authentifizierung mit Azure Active Directory. Aufgrund fehlender Optionen im docker-compose.yaml wird die Konfiguration der Container erschwert. Außerdem ist die automatische Skalierbarkeit auf den zugrundeliegenden App Service Plan beschränkt. 

Über den Autor

Tim ist seit 2019 als Big Data Consultant bei der Woodmark tätig. Im Rahmen seiner Kundenprojekte beschäftigt er sich intensiv mit der Entwicklung von individuellen Softwarelösungen für Big Data Anwendungsfälle in der Cloud.

Zurück