Azure

Retour d’expérience d’implémentation de projet Big Data sur Azure

Nov 23, 2019

Arnaud Voisin

Depuis plusieurs années j’ai la chance de pouvoir travailler sur la plateforme Azure et de pouvoir expérimenter des services Big Data comme SQL Data Warehouse Gen 1 et Gen 2, HD Insight, Data Lake Store Gen 1 et Data Factory.

Contexte du projet :

Note client, Believe, est le premier distributeur de musique indépendante international. 30% du catalogue distribué sur les plateformes digitales (Deezer, Spotify, Youtube, …) sont des tracks dont la distribution est assurée par Believe. Créé en 2005, l’entreprise compte 500 salariés dans 16 pays et diffuse le contenu de ses clients sur plus de 160 stores. Leader technologique, Believe mets à disposition de ses clients un backstage : offre complète de gestion de contenu (création d’album digital, monitoring de vente, demande de paiement).

Cet article sera articulé en deux parties, une première partie traitera du backstage s’appuyant sur SQL Data Warehouse et la deuxième d’un retour d’expérience sur les projets Big Data s’appuyant sur HDInsight.

Le premier chapitre est la suite de l’article publié dans le dernier numéro (Octobre) de Programmez

Bench de Azure SQL Datawarehouse Gen 1 et Gen 2 en mode interactif

Introduction

Notre front est connecté à une base Azure SQL Data Warehouse Gen 1 DW1200 depuis plus de 6 mois. Nous devons accueillir 10 fois plus d’utilisateurs. Nous devons passer d’un pool de 35 000 utilisateurs à un pool de 350 000 utilisateurs. 35 000 utilisateurs représentant environ 1700 utilisateurs actifs par jour, on peut estimer à 17 000 le nombre d’utilisateurs actifs par jour suite à l’arrivée de notre nouveau pool d’utilisateurs. Le marché de Believe étant world wide il y a des connexions réparties sur l’ensemble de la journée même si des pics peuvent être enregistrés particulièrement le matin (GMT). Pour accueillir cette nouvelle charge de données il était important pour nous de bencher une sélection de SLOs [i]de SQL Data Warehouse afin de vérifier une certaine linéarité des performances en fonction du prix payés et de vérifier la capacité de l’infrastructure à absorber la future charge.

Modélisation et volume de données :

Chaque jour nous ingérons les statistiques d’écoute des plateformes sur lesquelles Believe distribue de la musique à savoir Spotify, Deezer, Napster, ITunes, …

Ces données représentent un volume de 250 millions de lignes quotidiennes et l’arrivée de nouvelles plateformes comme par exemple Youtube et Youtube music devrait doubler le volume de données en entrée. La base est constituée d’une table de fait de 39 colonnes typées majoritairement en entier (bigint, int, smallint, tinyint) et avec des champs date et decimaux. Il existe des tables d’agrégats adaptées par famille de requête avec une granularité adaptée et une distribution optimisée pour répondre à certaines requêtes.

Toutes les tables sont stockées en Clustered ColumStore Index et elles sont partitionnées par producteur. Cette clé étant systématiquement présente sur chacune des requêtes jouées. Cela permet d’optimiser le temps d’accès à la donnée en réduisant le scope de data sans même lire l’index.

Présentation du protocole de Bench :

Nous avons répliqué 2 fois un snapshot d’un des Data Warehouse de production. Un a été laissé en Gen1 et l’autre upgradé en Gen 2 les 2 ont été entièrement Rebuild avec la classe de ressource la plus élevé sur un SLO de DW3000C pour le Gen 2 et un SLO de DW2000 pour le Gen 1.

A chaque début de bench nous avons appliqué un script personnalisé afin de monter en cache les données pour être ISO avec les conditions de production.

Ayant précédemment construit un entrepôt de données historisant toutes les requêtes jouées, nous avons construit un jeu de données de test en fonction de comportements utilisateurs réels sur le site pour stresser le service avec une charge aussi proche de la réalité que possible.

Ce jeu de donnée a été constitué avec les règles suivantes :

  • Toutes les 2 minutes on ajoute un utilisateur concurrent (producteur).
  • Les envois de requêtes sont faits aléatoirement sur des intervalles compris entre 2 et 8 secondes.
  • Choix d’un producteur parmi les producteurs les plus réguliers en consommation en statistique en respectant la proportion de type producteur en fonction de leur taille (volumétrie respective dans l’entrepôt – chaque producteur requêtant ses propres données).
  • Choix d’une date aléatoire parmi les dates de connexion du producteur
  • Catégorisation de toutes les séquences de navigation du producteur (requête contiguë dans un intervalle de 5 secondes)
  • Limitation à 5 requêtes maximum de cette séquence
  • Choix aléatoire parmi les différentes séquences et ajout de cette séquence au bench.

Un web job a été créé pour dépiler le jeu de donnée et lancer les requêtes à une heure déterminée en respectant la simultanéité d’envoi des requêtes afin de simuler de manière identique la montée en charge de chacun des SLOs.

Le bench dure 30min et il y a jusqu’à 16 utilisateurs concurrents qui p

euvent lancer jusqu’à 60 requêtes toutes les 2 secondes. L’intensité va en augmentant et la pression n’est jamais relâchée.

Ci-dessous un exemple du type de requêtes (procédures stockées) jouées lors de notre bench :

CREATE PROC report.getAudienceDailyUniqueListeners
 @idAlbum	   VARCHAR(MAX)
,@idLabel 	   VARCHAR(MAX)
,@idArtist 	   VARCHAR(MAX)
,@idSong   	   VARCHAR(MAX)
,@retailerId   VARCHAR(MAX)
,@isoCountry   VARCHAR(MAX)
,@idProducer   VARCHAR(MAX)
,@retailerStartDateMin VARCHAR(50)
,@retailerStartDateMax VARCHAR(50)
,@idSaleProductType VARCHAR(MAX)
,@undefinedClient VARCHAR(MAX)
,@IdReport VARCHAR(MAX)
,@TableName VARCHAR(MAX)

AS
BEGIN

DECLARE @TableNameDefault VARCHAR(255)

SET @TableNameDefault='[dwh].[AgrClientAge_HidClientIdentifier]'

DECLARE @SQL VARCHAR(MAX)

SET @SQL='
SELECT COUNT(DISTINCT(idClientIdentifier)) as clients, 
retailerStartDate as date,
retailerId, 
SUM(totalQuantity) as totalQuantity, 
SUM(undefinedQuantity) as totalUndefinedQuantity
FROM (
    SELECT idClientIdentifier, 
	retailerStartDate,
	retailerId,
    SUM( CASE WHEN idClientIdentifier IN ('+@undefinedClient+') THEN quantity ELSE 0 END ) as undefinedQuantity,
    SUM( quantity ) as totalQuantity
    FROM '+ISNULL(@TableName,@TableNameDefault)+' A WITH (NOLOCK)
    WHERE 
	idProducer IN (' + @idProducer + ')
	AND retailerStartDate BETWEEN ''' + @retailerStartDateMin + ''' AND '''+ @retailerStartDateMax + '''
	' + CASE WHEN @IdReport IS NULL THEN '' ELSE ' AND IdReport not IN (' + @IdReport + ')' END + '
	' + CASE WHEN @idAlbum IS NULL THEN '' ELSE ' AND idAlbum IN (' + @idAlbum + ')' END + '
	' + CASE WHEN @idLabel IS NULL THEN '' ELSE ' AND idLabel IN (' + @idLabel + ')' END + '
	' + CASE WHEN @idArtist IS NULL THEN '' ELSE ' AND idArtist IN (' + @idArtist + ')' END + '
	' + CASE WHEN @idSong IS NULL THEN '' ELSE ' AND idSong IN (' + @idSong + ')' END + '
	' + CASE WHEN @retailerId IS NULL THEN '' ELSE ' AND retailerId IN (' + @retailerId + ')' END + '
	' + CASE WHEN @isoCountry IS NULL THEN '' ELSE ' AND isoCountry IN (' + @isoCountry + ')' END + ' 
	' + CASE WHEN @idSaleProductType IS NULL THEN '' ELSE ' AND idSaleProductType IN (' + @idSaleProductType + ')' END + ' 
GROUP BY idClientIdentifier,retailerStartDate, retailerId ) as ZZ
GROUP BY retailerStartDate, retailerId
ORDER BY retailerStartDate ASC, clients DESC
OPTION (LABEL = ''report.getAudienceDailyUniqueListeners'');
'

--PRINT @SQL
EXEC(@SQL)
END

Les paramètres fournis aux procédures stockées ne sont jamais les mêmes et sont issus de workload réellement joués en production.

Choix des niveaux de service :

Nous avons choisi 6 niveaux de service (SLO) à bencher avec comme critères maximiser la concurrence et respecter un certain budget.

Revenons aux tableaux descriptifs de tous les niveaux de services disponibles en Gen 1 et Gen 2 dans Azure. J’attire votre attention sur le fait que les prix mentionnés sont ceux de la région Europe du Nord et que chaque région possède ses propres prix pour chacun des services.

  • En Gen 1 :
    • DW600 : Supporte 24 requêtes en simultanées. Nous avons volontairement retenu un niveau plus faible dans l’objectif de l’utiliser en complément des Data Warehouse existant ou bien dans une architecture scale-out.
    • DW1200 : C’est le niveau de service que nous utilisons actuellement, il supporte 32 requêtes simultanées. Il servira de baseline.
    • DW2000 : Supporte également 32 requêtes simultanées, mais avec plus de puissance et de slots de concurrence.

Tableau 1 Description des caractéristiques et les prix des différents SLOs de Gen 1

  • En Gen 2 :
    • DW1500C : Supporte 32 requêtes concurrentes, c’est le 2ème niveau en Gen2.
    • DW2000C : Premier niveau permettant de supporter 48 requêtes simultanées.
    • DW3000C : Premier niveau permettant de supporter 64 requêtes simultanées.

Tableau 2 Description des caractéristiques et les prix des différents SLOs de Gen 2

Résultats des benchs :

Grâce à un système de collecte des DMVs (Dynamic Management Views) notamment des vues sys.dm_pdw_exec_sessions, sys.dm_pdw_exec_requests, sys.dm_pdw_errors, nous avons pu construire le tableau de résultats suivants.

Tableau 3 Résultats des benchs

Interprétation des résultats :

Ces résultats nous permettent de tirer plusieurs enseignements :

  • D’une part avec une « faible » sollicitation, soit 33 requêtes/min, c’est-à-dire à la première minute du bench, nous voyons que le Gen 2 est très nettement plus performant. De l’ordre de x2. Si on compare le DW2000 coûtant 23,47€/H au DW1500C coûtant 17,61€/H, on voit que le temps médian d’exécution des requêtes est de 1203ms en DW2000 alors qu’il n’est que de 859ms en DW1500C.
  • On peut également constater une certaine linéarité entre coût du produit et performance notamment sur la Gen 2, ce qui est moins vrai sur la Gen 1.
  • Seul le DW2000C et le DW3000C peuvent encaisser les 300 requêtes/min à partir sur la base de notre bench
  • Le DW600 ne peut supporter ce workload sur une volumétrie proche des 100 milliards de lignes.

Conclusion du bench de SQL Data Warehouse :

Nous avons ici testé les différents SLOs et type d’entrepôts uniquement sur le cas d’usage de requêtage interactif. Et de manière assez logique la Gen 2, avec ses caches NVMe et sa mémoire vive très nettement augmentée remporte le match puisque plus de données peuvent monter en mémoire et moins d’IO sont requises pour satisfaire les requêtes. Cependant notre expérience a montré qu’il y a quand même des points sur lequel la Gen 1 remporte le match et pourrait donc être retenue pour des projets. Tout d’abord la Gen 1 démarre, s’éteint et se met à l’échelle plus rapidement. De plus pour des chargements en mode batch sans préchauffage, la Gen 1 peut se montrer aussi rapide. Sans compter le fait que la Gen 1 démarre à un peu plus de 1€ de l’heure pour la DW100 contre presque 12€/H pour la DW1000C. Lorsque l’objectif est une utilisation en mode Batch pour la Gen 1 peut être le bon candidat. Il faut également noter qu’il n’y a malheureusement pas la souplesse de passer d’une génération à l’autre car tout upgrade de base de Gen 1 à Gen 2 est définitif. L’utilisation de l’une des générations doit donc se décider au cas par cas en fonction des contraintes du projet.

Mise en place d’HDInsight : avantages / inconvénients.

Nous avons retenu HDInsight et plus particulièrement un cluster de type Interactive Query via HDInsight pour répondre à un besoin d’analyse Ad-Hoc et servir les équipes de data science et un outil décisionnel basé sur Power BI.

La définition de la Big Data étant bien souvent galvaudé, Il me semble important de la définir avant d’aller plus loin. Vous avez sans doute entendu parler des 3V, de 4V, voire même de 5V (Volume, Variété, Vélocité, Valeur, Véracité).

La notion de volume étant très subjective. A partir de quel volume de données parle-t-on de Big Data ? Il s’agit de Big Data à partir du moment où les systèmes traditionnels, les SGBDR en l’occurrence ne sont plus capables de traiter les volumes pour l’usage attendu. En fonction des usages, analytique ou data science cela pourra osciller du simple TeraOctet à la dizaine de TeraOctet. Rien de bien précis donc.

La notion de variété est également une dimension importante. Puisqu’un système traitant des volumes modérés pourrait être qualifié de système Big Data à partir du moment où les types de données sont suffisamment hétérogènes et complexes pour ne pas pouvoir être traités par des ETL classiques. Car oui la Big Data ce n’est pas simplement du stockage mais tout un écosystème permettant de gérer toute la chaine de son ingestion à son exploitation en passant par son stockage.

La vélocité signifie que le temps de mise à disposition de la donnée est très rapide. De son ingestion à son exploitation le temps est raccourci à son minimum allant jusqu’au temps réel et que le temps d’interrogation des données est d’une faible latence.

Une cohabitation compliquée de LLAP avec d’autres services

Comme je le disais nous avons fait le choix de mettre en place Hive LLAP (Interactive Query), dont l’objectif est de diminuer le temps de réponse d’un cluster Hadoop classique fonctionnant sur le moteur d’exécution Tez. Hadoop est fait pour un usage batch, c’est-à-dire pour agréger ou calculer de la donnée sur des volumes importants de données avec un temps de réponse ne permettant pas de faire du reporting. Avec LLAP la promesse est différente, Chaque nœud possède son daemon LLAP et chaque deamon LLAP son propre cache sur lequel les différents executor vont pouvoir aller piocher plutôt que de récupérer la donnée dans la table stockée sur le disque.

Le problème de Hive Interactive Server par rapport à Hive Server c’est qu’il est difficilement compatible voire même pas du tout compatible avec d’autres services Apache.

En effet, YARN qui est l’orchestrateur de la gestion de ressource en allouant et désallouant des containers avec un montant de RAM et de CPU déterminé ne joue pas le même rôle que lorsqu’il est utilisé pour Hadoop ou Spark.

Sur LLAP les containers sont initialisés dès le démarrage du service et les ressources ne sont jamais rendues au système sauf en cas de redémarrage du service. L’allocation dynamique n’est donc pas assurée. Et globalement LLAP préempte les ressources du cluster et ne laisse peu de place à d’autres services comme YARN, Hive Server, Pig ou encore des jobs map reduce de compaction.

C’est un des défauts majeurs que nous avons rencontrés dans notre projet, qui est peut-être plus imputable à Apache qu’à Hortonworks ou Azure (groupe produit de HDInsight).

Pour en savoir plus sur la configuration initiale de LLAP vous pouvez consulter ce lien : https://blog.dcube.fr/index.php/2018/09/14/configurer-hive-llap-sur-hdinsight/

Une mise en œuvre complexe :

Dans l’idée initiale d’utiliser HDInsight, un service managé de création de cluster Hadoop, il y avait la promesse de réduire le temps de mise en œuvre, et de configuration de l’infrastructure, pour se concentrer sur l’essentiel à savoir notre projet. La promesse était belle et la désillusion pour le client d’autant plus grande. Après de longs acharnements techniques, nous nous sommes rendus compte que HDInsight était plus proche du IAAS préconfiguré plutôt que du PAAS.

Ce qui a complexifié l’expérience, c’est d’arriver avant la version HDI 4.0 embarquant le tout dernier HDP 3.0 (Hortonworks Data Platform) avec la toute dernière version de Hive LLAP rendant ACID ON par défaut et rendant également la mise en cache possible des données issues de tables stockées en ORC (format columnar).

Nous avons fait nos armes sur HDI3.6 embarquant HDP 2.6 beaucoup moins aboutit sur la partie LLAP moteur le plus récent et avec une grosse marche de progression, heureusement HDP 3.0 corrige bon nombre de faiblesse, il est sorti depuis juin 2018.

A l’heure actuelle nous restons cependant sur la version HDP2.6 moins stable et moins performante car à ce stade Interactive Query 2.1.0 (HDI3.6) peut se connecter à Azure Data Lake Store Gen 1 alors que Interactive Query 3.1.0 (HDI 4.0) ne peut se connecter qu’à du Blob Storage et possiblement a du Azure Data Lake Store Gen 2 si l’on fait partie du programme de test (la Gen 2 étant en Preview) ce qui n’est pas notre cas.

Nous préférons rester sur du Data Lake Store car c’est un stockage dont le rapport Prix/Performance est favorable par rapport à Blob Storage. Data Lake Store est optimisé pour les charges de travail volumineuses et s’adapte parfaitement à la mise à l’échelle d’un Cluster HDInsight.

Des cases d’usages limités :

A date les connecteurs Interactive Query sont très rares puisque le seul connecteurs existant est celui de Power BI ce qui permet de faire de l’analyse Ad-Hoc ou de réaliser d’alimenter des rapports dans un portail PowerBI.com mais cela ne permet pas d’ajouter une surcouche sémantique comme Azure Analysis Services connecté en direct query et bénifciant de la puissance de calcul de LLAP et qui pourrait ajouter des caches intermédiaires et accélérer les performances de certaines requêtes après un warm-up.

Nous avons commencé à explorer de passer par Polybase sur un SQL Server IAAS en tentant de le connecter à Hiver Interactive Server, mais sans succès.

Des performances pas si interactives.

Les performances sont interactives si le partitionnement est bien fait et si les clauses where sont suffisamment filtrantes pour que le montant de données à scanner ne soit pas trop important.

Mais une requête de type FULL SCAN sur 100 milliards de lignes peut excéder les 3min voire plus avec notre configuration de 8 worker node de D14V2 (112Go de RAM et 16CPU).

Une gestion de la concurrence pas si aisées :

La configuration de queue utilisée par LLAP qui va permettre à YARN de gérer la concurrence entre les différentes requêtes. Il faut bien paramétrer cette queue notamment en configurant la propriété suivante yarn.scheduler.capacity.root.llap.minimum-user-limit-percent à 20% par exemple pour répartir équitablement les ressources sur 5 requêtes concurrentes et aussi en en choisissant une policy (yarn.scheduler.capacity.root.llap.ordering-policy) FAIR plutôt que FIFO dans la gestion de la queue.

Sauf que dans les faits sur des requêtes de type FULL SCAN sur 100 milliards de ligne en parallèle, il y a toujours des requêtes qui prennent le dessus par rapport à d’autres certaines requêtes voient leurs tâches killées au profit de celle qui a démarré avant.

Les forces du HDInsight :

Bien que précédemment nous ayons mis en lumière un certain nombre de faiblesses du service notre projet a révélé certaines forces du produit.

  1. Il est possible de tuer (supprimer) le cluster, c’est-à-dire toutes les machines qui le composent, les 2 heads nodes, les 3 zookeepers nodes et les X headnodes, 8 dans notre cas. Ce qui rends tout cela possible est le fait de pouvoir stocker les méta données Hive et Oozie dans une base SQL Azure et d’avoir décorrélé le stockage du compute en stockant toutes nos données sur un Azure Data Lake Store Gen 1. Bien sûr mettre en place un système automatique pour gérer la création n’est pas dénué de tout effort puisqu’il faut templatiser le cluster grâce à ARM (ou bien utiliser le SDK), il faut pouvoir stocker les settings personnalisés notamment ceux qui varient en fonction du nombre de nœud, et aussi il faut pouvoir stocker quelque part les informations de sécurité d’Apache Zeppelin ou celle d’Ambari si Apache Ranger n’est pas mis en œuvre.
  2. Dans la mesure où toutes les métadata sont stockées dans une base SQL Azure, il est possible de changer le type de cluster assez facilement et de passer de Interactive Query à Hadoop ou à un cluster de type Spark qui est sans doute le plus complet puisqu’il vient par défaut avec Spark (of course), Hive, Pig et Scoop. Spark étant difficilement compatible avec le cluster de type Interactive, il est préférable de partir sur la version préconfigurée plutôt que d’essayer de tordre l’outil.
  3. L’avantage d’avoir un service managé permet de pouvoir adapter le nombre de nœud à la volumétrie de données à analyser qui peut changer au fil du temps. Cela évite d’avoir à investir dans une infrastructure onéreuse sans la sous-utilisant ou de devoir attendre de commander et configurer de nouvelles machines pour ajouter des nœuds à notre cluster. Avec HDInsight, la mise à l’échelle se fait facilement. Il suffit de faire une demande d’augmentation de quota à Microsoft pour disposer de suffisamment de cœur pour demander la puissance requise et grâce aux APIs HDInsight il est possible de demander l’ajout ou la suppression de worker node et grâce à l’API d’Ambari il est possible d’adapter la configuration du cluster pour l’aligner sur la configuration physique du cluster. Exemple : si on passe de 16 worker nodes à 8 worker nodes il est nécessaire de demander à Ambari d’utiliser les 16 au lieu de n’en utiliser que 8. Sinon cela ferait payer 8 nœuds sans qu’ils ne soient utilisés.

Pour plus de détail sur cette partie vous pouvez consulter un article écrit par Nicolas Bailly :

Conclusion sur l’implémentation de HDInsight :

Notre retour d’expérience sur ce service managé qu’est HDInsight est que pour bénéficier des promesses de réduction des coûts promises par un service managé, cela nécessite un temps non négligeable de développement d’une Web API par exemple pour manager la création/suppression, mise à l’échelle, de développement d’un ordonnanceur maison ou plus simplement via Logic App.

Cela nécessite également pas mal de tuning de configuration avant d’arriver à des résultats optimaux.

Mais une fois le projet mis en place les coûts deviennent maitrisées. Pour terminer je vous mettrais un exemple de coût pour un cluster avec 8 worker nodes soit 1029Go de RAM et 156 cœurs.

La tarification utilisée est celle d’Europe du Nord. Pour une utilisation de 220H/mois ce fait un budget de 2700€ avec 1To de données et sans l’implémentation d’Apache Ranger afin d’utiliser des comptes AD pour se connecter au cluster. Avec Ranger cela couterait plus proche de 3400€/mois. Par comparaison le même cluster allumé en permanence couterait 7800€/mois sans ranger et 10150€/mois avec Oozie.

Le fait de s’outiller pour adapter le temps d’allumage du cluster à ses besoins n’est pas neutre budgétairement. Sachant qu’on peut encore optimiser les coûts en adaptant le nombre de nœuds en fonction du temps et donc aller chercher encore des économies. Mais attention la mise à l’échelle provoque une coupure de service, il faut donc le prendre en considération.

Conclusion générale sur la mise en place de projet Big Data sur Azure :

Les projets que nous traitons pour notre client Believe Digital sont résolument tournés vers la Big Data, la vraie Big Data avec déjà largement plus de 100 milliards de lignes à l’heure où nous parlons dans la table principale et très prochainement le double avec l’arrivée de gros flux comme You Tube et Youtube Music.

Pour le moment nous avons un retour d’expérience très positif sur Azure SQL Data Warehouse dont les performances permettent vraiment de rendre un service satisfaisant les exigences de notre client.

Hive LLAP serait un ton en dessous en termes de performance mais permettrait plus de cas d’usage. Notamment grâce à la possibilité de pouvoir manipuler de la donnée qui n’est pas structuré (semi ou non).

Nous attendons avec impatience l’accès à HDP3.0 pour toutes ses améliorations et notamment pour utiliser Apache Superset qui arrive en remplacement de Hive View.

Autre point à ne pas négliger et je terminerais là-dessus. Il y a des points de vigilance à ne pas oublier sur Azure pour tout projet et y compris des projets Big Data. Il est important d’avoir une stratégie réseau et sécurité cohérente. Car attention les données sortantes du réseau sont facturées relativement cher et en Big Data il peut y avoir de gros volumes de données en transit. Donc attention si vous ne voulez pas voir votre note exploser.


[i] Niveau de service

0 commentaires

Soumettre un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Découvrez nos autres articles

Aller au contenu principal