Utilisation d’un environnement Cloud Composer en multi-projets

Thomas de Lard
neoxia
10 min readNov 3, 2023

--

Airflow est un outil d’orchestration publié en 2015 par AirBnb qui s’est rapidement imposé comme une référence dans le domaine du Data Engineering.

Sur GCP, il existe un service d’orchestration complètement managé basé sur Airflow : Cloud Composer.

Le pricing de Cloud Composer inclut des frais horaires d’utilisation. Ainsi, si l’on déploie plusieurs environnements Cloud Composer pour assurer l’orchestration des workflows de différents projets, ces frais d’utilisation nous seront facturés plusieurs fois.

Dans cet article, nous proposons une méthodologie permettant d’utiliser un unique environnement Cloud Composer pour orchestrer différents projets indépendants, sans faire de concessions sur la sécurité. Nous veillerons à ce que les utilisateurs d’un projet ne puissent pas visualiser les DAGs d’un autre projet ni accéder à ses variables.

En particulier, nous nous placerons dans le cas où un environnement Cloud Composer est utilisé dans le cadre de deux projets ProjectA et ProjectB, chacun des projets nécessitant l’exécution de deux workflows. Nous considérerons que des groupes IAM regroupent les développeurs de ces projets.

I- Focus sur la configuration de Cloud Composer

Avant de proposer une méthodologie permettant de travailler sur un environnement Cloud Composer en “multi-projets”, nous rappelons comment Google gère le stockage de l’ensemble des fichiers nécessaires à l’utilisation d’Airflow, ainsi que les accès à Airflow.

A) Gestion du stockage

Lors du déploiement de l’environnement Cloud Composer, Google crée automatiquement un bucket GCS dont le nom dépend de celui de l’environnement et de sa localisation :
{location}-{composer_name}-xxxxxxxx-bucket

Airflow utilisera ce bucket en tant que file system grâce à Cloud Storage FUSE.

Ce bucket regroupe différents dossiers, dont :

  • dags : pour stocker les DAGs Airflow
  • data : pour stocker des fichiers
  • logs : pour stocker des fichiers de logs automatiquement générés suite à l’exécution des dags

On notera que ce bucket ne sera pas détruit si l’environnement Cloud Composer l’est.

B) Gestion des accès

Tous les utilisateurs GCP disposant des permissions composer.environments.list et composer.environments.get peuvent accéder à l’interface graphique Airflow.

Les rôles suivants combinent ces deux permissions :

  • Owner (roles/owner)
  • Editor (roles/editor)
  • Viewer (roles/viewer)
  • Composer Administrator (roles/composer.admin)
  • Environment and Storage Object Administrator (roles/composer.environmentAndStorageObjectAdmin)
  • Environment and Storage Object User (roles/composer.environmentAndStorageObjectUser)
  • Environment and Storage Object Viewer (roles/composer.environmentAndStorageObjectViewer)
  • Composer User (roles/composer.user)

Ainsi, un utilisateur GCP disposant par exemple du rôle Viewer (roles/viewer) sur le projet où l’environnement est déployé peut par défaut accéder à Airflow. Lors de sa première connexion, un utilisateur Airflow est créé, basé sur les informations de son compte GCP.

Les permissions Airflow de cet utilisateur dépendent de la valeur de la variable rbac_user_registration_role, dans la section webserver de la configuration Airflow.
Le rôle Airflow par défaut est Op. Il permet entre autres d’accéder à l’ensemble des DAGs et de les exécuter.

Ainsi, dans notre cas, un utilisateur aurait par défaut accès aux DAGs suivants :

Aperçu de l’interface graphique Airflow avec le rôle par défaut Op

II- Utilisation de l’environnement en multi-projets

Nous présentons à présent la méthodologie permettant de mutualiser un environnement Cloud Composer à différents projets.

Nous verrons dans un premier temps comment s’assurer que les développeurs d’un projet aient accès uniquement aux DAGs de leur projet.

Puis, nous décrirons les challenges relatifs aux variables dans le cadre de l’utilisation d’un environnement Cloud Composer en “multi-projets”, et proposerons une approche pour les surmonter.

A) Gestion des accès par projet

Le rôle Op, attribué par défaut aux utilisateurs Airflow, permet d’accéder à l’ensemble des DAGs de l’environnement Cloud Composer. Les développeurs peuvent visualiser les dernières exécutions d’un DAG, son code, mais aussi désactiver un DAG, le déclencher voire même le supprimer.

Pour s’assurer que les développeurs d’un projet ne puissent pas compromettre un autre projet, il convient donc de ne leur donner accès qu’aux DAGs de leur projet. Pour cela, nous verrons d’abord comment modifier la configuration par défaut pour qu’un nouvel utilisateur n’ait accès à aucun DAG. Puis, nous verrons comment donner l’accès uniquement aux DAGs d’un projet. Enfin, nous proposerons une méthode pour automatiser la gestion des droits.

Limitation des droits par défaut

Afin qu’un utilisateur qui se connecte pour la première fois à Airflow n’ait pas accès à l’ensemble des DAGs, on peut faire en sorte que le rôle Airflow par défaut ne soit plus Op mais UserNoDags.

Pour cela, il faut modifier la valeur de rbac_user_registration_role depuis la section Airflow Configuration Overrides :

[webserver] rbac_user_registration_role = “UserNoDags”

Aperçu de l’interface graphique Airflow avec le rôle UserNoDags

Création de rôles pour accéder aux DAGs d’un projet

Les fichiers python permettant de définir les DAGs sont stockés dans le dossier dags/ du bucket lié à l’environnement Cloud Composer. L’adressage de ces fichiers n’importe à priori pas, puisqu’ils apparaissent tous de la même manière dans Airflow.

Cependant, si l’on met en place une structure de dossiers par projet au sein du dossier dags/, il est possible de créer automatiquement des rôles Airflow donnant accès uniquement aux DAGs d’un projet.

Dans notre cas, on stockera donc les DAGs liés au projet projectA dans le dossier dags/projectA/ et les DAGs liés au projet projectB dans le dossier dags/projectB/.

Il suffit ensuite de spécifier dans la configuration d’Airflow :

[webserver] rbac_autoregister_per_folder_roles = true

Pour chaque sous-dossier du dossier dags/, un rôle est automatiquement créé.
Le nom du rôle correspond au nom du dossier, et ce rôle permet d’accéder à l’ensemble des DAGs stockés dans le dossier, en lecture et en écriture.

Nous disposons alors de deux nouveaux rôles : projectA et projectB

Combiner les rôles UserNoDags et projectA permet par exemple aux développeurs du projet projectA de visualiser les DAGs de leur projet, mais aussi de les exécuter et d’accéder aux logs correspondants.

Aperçu de l’interface graphique Airflow avec le rôle projectA

Attribution manuelle des rôles

Maintenant que l’on a créé des rôles Airflow pour chaque projet, il faut attribuer les bons rôles aux bons développeurs.

Cela peut être fait par un utilisateur Airflow qui dispose du rôle d’administrateur (Admin), via l’onglet Security accessible depuis l’interface graphique.

Pour donner le rôle d’administrateur à un utilisateur Airflow, on exécute la commande suivante, qui nécessite le rôle GCP Composer Worker (roles/composer.worker).

gcloud composer environments run ENVIRONMENT_NAME --location LOCATION users add-role -- -e USER_EMAIL -r Admin

Si l’environnement Cloud Composer a été déployé sur un réseau VPC privé, il faut exécuter la commande depuis une machine virtuelle (VM) située sur ce VPC.

On notera qu’on peut attribuer le rôle d’administrateur Airflow à tout utilisateur via cette commande. On veillera donc à faire en sorte que celle-ci ne puisse pas être exécutée par un utilisateur malveillant. Dans le cadre d’un réseau privé, on peut limiter les droits de créer une VM sur le VPC où l’environnement a été créé.

Automatisation de la gestion des accès

Bien qu’il puisse être utile de faire des modifications manuelles de droits, il serait fastidieux d’attribuer manuellement les droits à l’ensemble des utilisateurs Airflow. En effet, on devrait alors faire une action manuelle dès que quelqu’un rejoint un projet.

On propose donc une approche permettant d’automatiser ce processus. Celle-ci consiste à utiliser un DAG qui va attribuer aux utilisateurs Airflow les bons droits en regardant dans quel groupe IAM ils se trouvent.

DAG permettant de mettre à jour les utilisateurs

On propose le DAG ci-dessus, décomposé en cinq tâches :

  • get_project_parameter : Récupère le paramètre indiquant le projet dont on veut mettre à jour les droits
  • list_airflow_users : Récupère la liste des utilisateurs Airflow existants et des rôles qui leurs sont attribués
  • list_google_groups_memberships_transitive : Récupère la liste les utilisateurs présents dans le groupe IAM du projet, y compris les utilisateurs présents dans des groupes imbriqués
  • compare_members_users : Vérifie que les utilisateurs Airflow correspondent aux membres des groupes IAM et qu’ils ont les bons rôles
  • update_users: Crée et supprime des utilisateurs si besoin, et met à jour leur rôle si besoin

Pour que ce DAG fonctionne, il est nécessaire d’ajouter dans le groupe IAM un compte de service, auquel on donnera le rôle GCP Composer User (roles/composer.user), puis de créer un utilisateur Airflow correspondant à ce compte de service et ayant le rôle Admin.

L’ajout du compte de service au groupe permet de lister les utilisateurs. En effet, tout utilisateur d’un groupe IAM a le droit de lister les membres de ce groupe. On notera que s’il y a des groupes imbriqués, le compte de service doit aussi être ajouté à ceux-ci.

Le rôle Composer User (roles/composer.user) est nécessaire pour exécuter l’appel API permettant de récupérer la liste des utilisateurs Airflow.

Le rôle d’Admin Airflow est requis pour effectuer des opérations sur les utilisateurs Airflow.

Afin de créer un utilisateur Airflow lié à un compte de service GCP, on peut opérer de la façon suivante.

Dans l’onglet Security, accéder à “List Users” puis “Add a new record”. On remplit ensuite les champs de la manière suivante :

Afin de récupérer le google_account_number, on peut par exemple lister les membres d’un groupe auquel appartient le compte de service avec la commande :

gcloud identity groups memberships list --group-email=GROUP_EMAIL

Pour chaque membre, on aura accès à l’attribut name, écrit sous la forme suivante : “groups/{group_id}/memberships/{google_account_number}”

B) Gestion des variables et des secrets

Les scripts Python définissant les DAGs Airflow étant variabilisés, il est nécessaire de définir l’ensemble des variables nécessaires à leur exécution dans Airflow.

Dans le cadre d’une utilisation en multi-projets, cela présente quelques challenges que nous expliciterons dans un premier temps, avant de proposer une approche pour gérer les variables, ce qui inclut les variables sensibles, dites “secrets”.

Challenges relatifs à la gestion des variables en multi-projets

Il est possible de définir les variables Airflow depuis l’interface graphique Airflow, via la section Variables de l’onglet Admin.

En procédant ainsi, les développeurs auront cependant accès à l’ensemble des variables Airflow et pas uniquement à celles de leur projet. Ils pourraient donc par erreur supprimer une variable d’un autre projet, et ainsi compromettre la bonne exécution des workflows de ce projet.

Ce n’est par ailleurs pas une bonne pratique car l’ajout manuel des variables peut occasionner des erreurs.

Une autre approche que nous privilégions consiste à définir l’ensemble des variables nécessaires à un projet dans un fichier json, puis d’importer ce fichier.

En multi-projets, il faut cependant veiller à ne pas avoir deux variables de même nom parmi tous les projets, car on risquerait alors d’écraser la valeur de la variable de l’autre projet.

Gestion des variables “non sensibles”

Afin d’éviter que deux projets différents utilisent des variables du même nom, on propose de préfixer les variables par un trigramme unique à chaque projet. Par exemple, PRA pour projectA, PRB pour projectB.

Pour importer les variables, on propose d’utiliser un fichier XXX_variables.json — XXX correspondant au trigramme du projet — que l’on stockera dans un sous-dossier data/{project_name}/ du bucket GCS via un pipeline de CI/CD.

On exécutera ensuite un DAG afin d’initialiser les variables, de les mettre à jour ou de les supprimer.

En procédant ainsi, on évite de devoir donner aux utilisateurs l’accès aux variables d’Airflow.

DAG permettant de mettre à jour les variables Airflow

On propose le DAG ci-dessus, décomposé en cinq tâches :

  • get_project_parameter : récupère le paramètre indiquant le projet dont on veut mettre à jour les variables
  • get_expected_variables_from_project_json : récupère le fichier json de variables dans le bucket lié au composer
  • get_existing_variables_project : liste les variables actuellement définies dans Airflow
  • compare_expected_existing_variables : compare les variables actuelles et les variables attendues, et retourne trois listes : les variables à supprimer, les variables à modifier et les variables à créer
  • update_variables_project : Crée, supprime et modifie les variables Airflow afin que les variables du projet correspondent à ce qui est indiqué dans le fichier json

Gestion des secrets

Si les valeurs des variables sont sensibles — on parle de secretsil est possible d’utiliser le gestionnaire de secrets GCP : Secret Manager.

On pourrait récupérer les valeurs des secrets dans les DAGs via la bibliothèque python Secret Manager, mais il est possible de les récupérer comme tout autre variable Airflow, via la fonction Variable.get({variable_name}) du package airflow.models.variables.

Pour cela, il faut “override” la configuration Airflow :

[secrets] backend = airflow.providers.google.cloud.secrets.secret_manager.CloudSecretManagerBackend

On s’assurera également que le compte de service de votre environnement Cloud Composer a les permissions nécessaires pour accéder aux secrets (rôle roles/secretmanager.secretAccessor).

Pour que cela fonctionne, les secrets doivent également être préfixés airflow-variables-. En pratique, un secret utilisé pour un projet dont le trigramme est XXX sera préfixé airflow-variables-XXX-.

De façon analogue, on pourra y stocker des “connexions” Airflow, correspondant à des “credentials” permettant de se connecter à des services, en les préfixant airflow-connections-.

Afin de déployer les secrets dans Secret Manager, on peut définir un script via notre outil de CI/CD. On stocke les secrets en tant que variables masquées, puis on les récupère comme des variables d’environnement dans un script bash avant de les écrire dans le Secret Manager via gcloud.

L’appel des secrets stockés sur le secret Manager ayant un coût, on utilisera ce dernier seulement pour les variables sensibles.

Conclusion

Afin de réduire les coûts mensuels de l’infrastructure déployée sur GCP, il est possible d’utiliser un unique environnement Cloud Composer pour orchestrer les workflows de plusieurs projets indépendants, sans faire de concessions sur la sécurité.

Pour cela, il est important d’assurer que l’accès aux DAGs et aux variables soit géré par projet.

Pour faire en sorte que les utilisateurs d’un projet aient accès uniquement aux DAGs du projet, on peut attribuer des rôles Airflow de manière automatique, en choisissant la bonne structure de dossier dans le bucket GCS. On peut ensuite développer un DAG pour synchroniser les utilisateurs GCP avec Airflow.

Afin de gérer l’accès aux variables “non sensibles”, on peut les définir via un fichier de configuration pour chaque projet, et mettre en place des préfixes par projet pour éviter tout écrasement de variables. Un DAG peut ensuite permettre l’initialisation ou la mise à jour des variables.

Pour les variables sensibles, il convient d’utiliser Secret Manager, que l’on peut lier à notre environnement Cloud Composer afin de faciliter l’accès aux secrets dans les DAGs.

--

--