PARTAGE

Le 13 septembre 2025 | Update 14 septembre 2025 | 8 mins read

Comment exécuter un workflow Apache Hop avec Docker et Airflow

Introduction

Concevoir des workflows et des pipelines avec Apache Hop est une étape essentielle pour transformer et déplacer de la données, mais les exécuter de façon optimale et en toute fiabilité pour délivrer au bon moment, c’est encore mieux! C’est le rôle d’Apache Airflow ici. Cet article présentera plusieurs approches pour lancer des workflows Hop avec Airflow, avant de mettre l’accent sur la méthode que j’ai retenue.

Modes d'exécutions

Il existe plusieurs approches selon l’architecture choisie :

Exécution locale dans un conteneur Docker

  • Airflow télécharge l’image Apache Hop (officielle ou customisée)
  • Le workflow .hwf, les pipelines .hpl et les metadatas sont montés dans le conteneur et exécuté avec hop-run.sh via le DockerOperator

Exécution sur un serveur Apache Hop distant via Docker

  • Airflow télécharge l’image Apache Hop (officielle ou customisée)
  • Le workflow .hwf, les pipelines .hpl et les metadatas sont montés dans le conteneur et exécuté avec hop-run.sh puis l'exécution est réalisée sur un serveur distant hop.
  • Le container Docker sur apache airflow n'a pas un fort besoin en ram et cpu dans ce cas.
  • On peut choisir sur quelle machine exécuter le workflow très facilement.

Exécution sur un serveur Apache Hop distant via SSH

  • Airflow se connecte à un serveur où Hop est déjà installé
  • Le DAG déclenche le workflow via un SSHOperator

Exécution via l’API REST de Hop (mode serveur)

  • Airflow appelle l’API REST du server Hop pour lancer un workflow via le HttpOperator ou un script bash ou des fonctions python
  • Plus complexe à mettre en œuvre

Exécution via le BashOperator sur l'instance Apache Airflow

  • Apache Hop est installé sur l'instance Airflow
  • Airflow lance via le BashOperator hop_run.sh
  • Attention a ne pas surcharger la machine Airflow

Exécution via des moteurs cloud (Beam , Spark)

Dans ce tutoriel, nous allons nous concentrer sur la deuxième méthode (DockerOperator+Hop Server). C'est la méthode que j'ai retenu pour exécuter mes workflows. Pour chaque workflow, je peux choisir la quantité de ram et de cpu alloué, si l'exécution se fait localement dans le container ou si l'exécution se fait sur une machine sur laquelle tourne un serveur hop et choisir quel serveur (dev, test, prod...). De plus, aucun besoin d'installer quoi que ce soit sur l'instance Apache Airflow.

Environnement Apache Hop

Hop Server

Premiere étape, il faut définir les serveurs hop sur lesquels on peut executer nos workflow et nos pipelines.

Rien de particulier, il faut définir le hostname ou l'ip du server. Le port a utiliser, username et password. Toutes ces informations proviennent du fichier .xml qui sert a paramétrer le serveur hop.

La seconde étape consiste à créer différents profils pour l'execution des workflows. A chaque profil, il faut associer un "Hop Server". Très important, cocher "Export linked resources to server"

Vous pouvez vérifier que tout est ok dans hop gui en cliquant sur lancer un workflow. Vous pouvez maintenant sélectionner la location (workflow run configuration) ou sera exécuter le workflow. Faites un test d'exécution sur un de vos serveurs hop pour tester le bon fonctionnement.

Airflow

Dag Test

La variable python : DOCKER_RUN_LOCATION permet de choisir ou se fera l'exécution du workflow. Si on note "local", l'exécution se fera comme son nom l'indique localement dans le container docker. Si on choisit le nom d'un serveur Hop, l'execution se fera à distance sur le serveur choisis. On peut bien évidement ajouter une logique plus poussée pour déterminer le server à utiliser. On pourrait aller plus loin et définir une variable pour les pipelines pour lancer le workflows localement mais exécuter un pipeline sur un serveur hop.

Si vous choisissez local, faites bien attention au nombre de cpu et à la quantité de ram allouée au container.

Attention, par exemple ici j'exécute mon workflow sur la version 2.11, il faut que les serveurs exécutent la même version. Sinon, vous pourriez avoir des conflits de métadonnées.

Les dossiers contenant les fichiers .hwf et .hpl ainsi que les metadatas sont mappés sur la machine airflow. Une autre alternative serait de pull un projet depuis un dépôt git.

  • Le repertoire du projet apache hop dans le container avec : Mount(source=HopConfigDocker['source_project'], target='/project', type='bind')
  • Les metadatas sont montés dans le container avec : Mount(source=HopConfigDocker['source_config'], target='/project-config', type='bind')
  • Les pilotes jdbc spécifiques sont ajoutés au container avec : Mount(source=HopConfigDocker['source_jdbc'], target='/jdbc', type='bind')

Consultez la doc sur le site officiel pour plus de détail sur les options du container docker officiel :

from datetime import datetime, timedelta
import pendulum
from airflow import DAG
from airflow.operators.docker_operator import DockerOperator
from docker.types import Mount
from airflow.operators.dummy_operator import DummyOperator

##########################################################################
#            IMPORTS VAR PERSO                                           #
import env.config as config
import env.env as env

##########################################################################
#            DEFINITION GLOBALE DU DAG                                   #
DAG_ID = 'test_hop_docker'
DAG_NAME = 'Test Hop Docker'
DAG_TAG = ["hop","test",'docker']
TIMEZONE = env.TIMEZONE
DAG_TIMEOUT = 20
DAG_RETRY_DELAY = 1
DAG_RETRIES = 0

DOCKER_IMAGE = 'apache/hop:2.11.0'  #" = nom de l'image docker exemple : 'apache/hop:2.11.0'
DOCKER_RUN_LOCATION = 'local' # options : local  / ServerDev / ServerProd / env.HOP_SERVEUR
DOCKER_TIMEOUT = 10
DOCKER_CPU = 1
DOCKER_RAM = '512m' # 1g, 2g, etc...

default_args = {
    'owner'                     : 'nicodl',
    'description'               : DAG_NAME,
    'depend_on_past'            : False,
    'start_date'                : datetime(2025, 9, 3),
    'email_on_failure'          : False,
    'email_on_retry'            : False,
    'retries'                   : DAG_RETRIES,
    'retry_delay'               : timedelta(minutes=DAG_RETRY_DELAY),
}

##########################################################################
#            DECLARATION DU DAG                                          #

with DAG(DAG_ID,
        dag_display_name = DAG_NAME,
        default_args = default_args,
        catchup = False,
        tags= DAG_TAG,
        schedule_interval = None, # https://crontab.cronhub.io/
        is_paused_upon_creation = True,
        max_active_runs=1,
        on_failure_callback=None,
        on_success_callback=None,
        dagrun_timeout=timedelta(minutes=DAG_TIMEOUT),
        ) as dag:

    ##########################################################################
    #            DECLARATION DES TASKS                                       #

    #---------------------------------------------------------------
    # Task 1 => test hop run location
    # Paramétrages
    HopConfigDocker = {
        'image': DOCKER_IMAGE,
        'cpus': DOCKER_CPU,
        'mem_limit': DOCKER_RAM,
        'log_level':'Basic',
        'workflow':'${PROJECT_HOME}/test_hop_docker.hwf',
        'priority_weight':config.PriorityWeight('normal'),
        'execution_timeout':timedelta(minutes=DOCKER_TIMEOUT),
        'project_name':'test',
        'source_project':f'{env.HOP_DOCKER_BASE_MOUNT}/projets/test',
        'source_config':f'{env.HOP_DOCKER_BASE_MOUNT}/config/',
        'source_jdbc': f'{env.HOP_DOCKER_BASE_MOUNT}/jdbc',
        'hop_conf':'/project-config/hop-config.json',
        'metadata': '/project-config/metadata.json',
        'jdbc': 'lib/jdbc,/jdbc',
        'api_url':'http://host.docker.internal:8000',
        'docker_url' : env.DOCKER_URL, # "tcp://host.docker.internal:2375 pour windows / linux : unix://var/run/docker.sock"
        'RUN_LOCATION' : DOCKER_RUN_LOCATION, # local / ServerDev / ServerProd
    }

    wkf_hop = DockerOperator(
        task_id='wkf_hop',
        image = HopConfigDocker['image'],
        api_version='auto',
        mount_tmp_dir=False,
        auto_remove=True,
        cpus= HopConfigDocker['cpus'],
        mem_limit= HopConfigDocker['mem_limit'],
        priority_weight= HopConfigDocker['priority_weight'],
        execution_timeout= HopConfigDocker['execution_timeout'],
        environment= {
            'HOP_RUN_PARAMETERS': f'INPUT_DIR=',
            'HOP_LOG_LEVEL': HopConfigDocker['log_level'],
            'HOP_OPTIONS' : '-XX:+AggressiveHeap',
            'HOP_FILE_PATH': HopConfigDocker['workflow'],
            'HOP_PROJECT_DIRECTORY': '/project',
            'HOP_PROJECT_NAME': HopConfigDocker['project_name'],
            'HOP_ENVIRONMENT_NAME': 'env-hop-airflow-sample.json',
            'HOP_ENVIRONMENT_CONFIG_FILE_NAME_PATHS': HopConfigDocker['hop_conf'],
            'HOP_RUN_CONFIG': HopConfigDocker['RUN_LOCATION'],
            'API_BASE_URL': HopConfigDocker['api_url'],
            'HOP_RUN_METADATA_EXPORT': HopConfigDocker['metadata'],
            'HOP_SHARED_JDBC_FOLDERS': HopConfigDocker['jdbc'],
        },
        docker_url=HopConfigDocker['docker_url'],
        network_mode="bridge",
        mounts=[Mount(source=HopConfigDocker['source_project'], target='/project', type='bind'),
                Mount(source=HopConfigDocker['source_config'], target='/project-config', type='bind'),
                Mount(source=HopConfigDocker['source_jdbc'], target='/jdbc', type='bind')
                ],
        force_pull=False
    )
    
        #task    
    start_dag = DummyOperator(task_id='start')
    #task   
    end_dag = DummyOperator( task_id='end')


start_dag >> wkf_hop >> end_dag

Le graph du dag. Très simple.

Résultats

Le résultat de l'exécution du DAG. On voit bien que l'exécution a été lancé sur le serveur "ServerDev".

Vérification sur l'interface web du ServerDev :

Vérification du log sur le server. Iso log.

Résumé

Airflow offre plusieurs moyens d’exécuter des workflows Hop, api, docker, bash, cloud. Chaque approche a ses avantages. Mais l’association DockerOperator + Hop Server se distingue par sa praticité selon moi. Elle permet de définir très facilement l’environnement d’exécution (local ou serveur(s) hop) tout en maîtrisant les ressources allouées. Autre atout, Hop n’a pas besoin d’être installé directement sur l'instance Airflow ce qui facilite grandement les déploiements et les mises à jour.

Le dag et le workflow de tests sont disponibles sur mon github.