Comment automatiser avec Jenkins la création et le roulement d'artefacts d'artefacts de modÚle de métadonnées pour les tables du référentiel

Tout a commencé avec le fait que nous étions confrontés à la nécessité de former rapidement et correctement les structures EDWEX, JSON, DDL, puis de les déployer sur différents contours de bases de données relationnelles. Par contours, je veux dire des abréviations que tout le monde connaßt - DEV, TST, UAT, PRD.



À cette Ă©poque, nous faisions presque tout manuellement: nous avons gĂ©nĂ©rĂ© DDL et collectĂ© edwex et json en fonction des mĂ©tadonnĂ©es de la base de donnĂ©es Oracle. Il existe de nombreux paramĂštres d'entrĂ©e. Si vous en manquez, vous formerez une entitĂ© incorrectement. Et comme tout le processus de formation Ă©tait cohĂ©rent et continu, l'erreur ne sera dĂ©tectĂ©e qu'Ă  la toute fin. À propos de la façon dont nous avons tous automatisĂ© et surmontĂ© les erreurs, lisez sous la coupe.

Un peu sur l'infrastructure


Avant de remplir les données dans les tableaux des bases de données relationnelles, nous devons les accepter de la source - dans n'importe quel format, par exemple, dans Excel. Les données de la source utilisant le mécanisme interne sont transportées vers Hadoop (Data Lake). Hive a installé le module complémentaire Hive - avec son aide, nous pouvons afficher le contenu des fichiers en utilisant une syntaxe de type SQL.

Pour transfĂ©rer les donnĂ©es dans Data Lake sous forme de tableaux, nous avons besoin de json, qui sont formĂ©s sur la base de mĂ©tadonnĂ©es. Pour analyser les donnĂ©es au format Excel, nous devons crĂ©er des fichiers EDWEX. Les fichiers EDWEX (EDW_EXTRACTOR) sont un ensemble d'artefacts qui contiennent des ensembles de tables avec le nom, ainsi que des ensembles de champs pour chacune de ces tables, qui sont formĂ©s sur la base de mĂ©tadonnĂ©es. En fonction de la version du modĂšle et de l'ID source, l'ensemble de champs varie. La formation de DDL est nĂ©cessaire pour crĂ©er les tables elles-mĂȘmes dans la base de donnĂ©es Hive au niveau des donnĂ©es opĂ©rationnelles et dans la base de donnĂ©es Greenplum au niveau des donnĂ©es dĂ©taillĂ©es et agrĂ©gĂ©es. Autrement dit, les donnĂ©es sont principalement transfĂ©rĂ©es vers Hive, si nĂ©cessaire, filtrĂ©es et transfĂ©rĂ©es vers Greenplum pour une manipulation ultĂ©rieure des donnĂ©es et la crĂ©ation de vitrines basĂ©es sur celle-ci.

Exemple d'artefact Edwex
pack - contient un ensemble de tables
données - contient un ensemble de champs

pack.edwex:

1 Table_1 User Table_1 bid between to_char($fromdt,'yyyymm') and to_char($actualdt,'yyyymm') 2 Table_2 User Table_2 curbid between to_char($fromdt,'yyyymm') and to_char($actualdt,'yyyymm') 3 Table_3 User Table_3 bid between to_char($fromdt,'yyyymm') and to_char($actualdt,'yyyymm') 


data.edwex:

 1 1 CHARGE_ID NUMBER 38 0 1 2 SVC_ID NUMBER 38 0 1 3 VND_ID NUMBER 38 0 1 4 PRICE NUMBER 38 5 1 5 QUANTITY NUMBER 38 5 1 6 BASE NUMBER 38 5 1 7 TAX NUMBER 38 5 1 8 TOTAL NUMBER 38 5 1 9 TAX_RATE NUMBER 38 5 1 10 TAX_IN VARCHAR 1 1 11 CHARGE_KIND VARCHAR 3 1 12 PRIVILEGE_ID NUMBER 38 0 1 13 CHARGE_REF_ID NUMBER 38 0 1 14 EBID NUMBER 38 0 1 15 INVOICE_ID NUMBER 38 0 1 16 ZERO_STATE_ID NUMBER 38 0 1 17 USER_ID NUMBER 38 0 1 18 BID NUMBER 38 0 1 19 QUANTITY_REAL NUMBER 38 5 2 1 CURBID NUMBER 38 0 2 2 USER_ID NUMBER 38 0 2 3 VND_ID NUMBER 38 0 2 4 APPBID NUMBER 38 0 2 5 SVC_ID NUMBER 38 0 2 6 DEBT NUMBER 38 5 2 7 INSTDEBT NUMBER 38 5 3 1 INVOICE_ID NUMBER 38 0 3 2 INVOICE_DATE DATE 3 3 INVOICE_NUM VARCHAR 64 3 4 INVOICE_NUM_N NUMBER 38 5 3 5 BASE NUMBER 38 5 3 6 TAX NUMBER 38 5 3 7 TOTAL NUMBER 38 5 3 8 PREPAID VARCHAR 1 3 9 EXPLICIT VARCHAR 1 3 10 VND_ID NUMBER 38 0 3 11 ADV_PAYMENT_ID NUMBER 38 0 3 12 MDBID NUMBER 38 0 3 13 BID NUMBER 38 0 3 14 USER_ID NUMBER 38 0 3 15 ZERO_STATE_ID NUMBER 38 0 3 16 ACTIVE_SUM NUMBER 38 5 3 17 SPLIT_VND NUMBER 38 5 3 18 PRECREATED VARCHAR 1 


Exemple d'artefact Json
 Table.json: { "metadata": [ { "colOrder":"1", "name":"charge_id", "dataType":"DECIMAL", "precision":"0", "requied":"true", "keyFile":"" }, { "colOrder":"2", "name":"svc_id", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"3", "name":"vnd_id", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"4", "name":"price", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"5", "name":"quantity", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"6", "name":"base", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"7", "name":"tax", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"8", "name":"total", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"9", "name":"tax_rate", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"10", "name":"tax_in", "dataType":"STRING", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"11", "name":"charge_kind", "dataType":"STRING", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"12", "name":"privilege_id", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"13", "name":"charge_ref_id", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"14", "name":"ebid", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"15", "name":"invoice_id", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"16", "name":"zero_state_id", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"17", "name":"user_id", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"18", "name":"bid", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" }, { "colOrder":"19", "name":"quantity_real", "dataType":"DECIMAL", "precision":"0", "requied":"false", "keyFile":"" } ], "ctlPath":"  ctl", "errPath":"    ", "inPath":"    hdfs", "outSchema":" ", "outTable":" ", "isPartitioned":" ", "sourceId":"id  " } 


Exemples d'artefacts DDL
modifier le schéma de table.GP_000001_TABLE renommer en Z_GP_000001_TABLE_20180807;
créer un schéma de table.GP_000001_TABLE
(

`charge_id` DECIMAL (38.0),
`svc_id` DECIMAL (38.0),
`vnd_id` DECIMAL (38.0),
`prix` DÉCIMAL (38,5),
`quantitĂ©` DÉCIMALE (38,5),
`base` DECIMAL (38,5),
`tax` DECIMAL (38.5),
«total» DÉCIMAL (38,5),
`tax_rate` DECIMAL (38.5),
`tax_in` STRING,
`charge_kind` STRING,
`privilege_id` DECIMAL (38.0),
`charge_ref_id` DECIMAL (38.0),
`ebid` DECIMAL (38.0),
`facture_id` DECIMAL (38.0),
`zero_state_id` DECIMAL (38,0),
`user_id` DECIMAL (38.0),
`bid` DECIMAL (38.0),
`quantity_real` DECIMAL (38,5),
TIMESTAMP `load_dttm`,
`src_id` SMALLINT,
`package_id` BIGINT,
`wf_run_id` BIGINT,
`md5` STRING
)
CHAMPS DÉLIMITÉS AU FORMAT DE LIGNE TERMINÉS PAR '\ t'
ENREGISTRÉ COMME INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
EMPLACEMENT 'moyen de se former en hdfs'
TBLPROPERTIES ('auto.purge' = 'true', 'transient_lastDlastDdlTime' = '1489060201');
insérer dans le schéma.GP_000001_TABLE (`charge_id`,
`svc_id`,
`vnd_id`,
`prix`,
`quantité`,
`base`,
`taxe`,
`total`,
`tax_rate`,
`tax_in`,
`charge_kind`,
`privilege_id`,
`charge_ref_id`,
`ebid`,
`facture_id`,
`zero_state_id`,
`user_id`,
enchérir
`quantity_real`,
`load_dttm`,
`src_id`,
package_id
`wf_run_id`,
`md5`)

sélectionnez `charge_id`,
`svc_id`,
`vnd_id`,
`prix`,
`quantité`,
`base`,
`taxe`,
`total`,
`tax_rate`,
`tax_in`,
`charge_kind`,
`privilege_id`,
`charge_ref_id`,
`ebid`,
`facture_id`,
`zero_state_id`,
`user_id`,
enchérir
`quantity_real`,
load_dttm,
src_id
package_id,
wf_run_id,
md5 du schéma.Z_GP_000001_TABLE_20180807;

modifier le schéma de table.GP_000001_TABLE renommer en Z_GP_000001_TABLE_20180807;
créer un schéma de table.GP_000001_TABLE
(

`charge_id` DECIMAL (38.0),
`svc_id` DECIMAL (38.0),
`vnd_id` DECIMAL (38.0),
`prix` DÉCIMAL (38,5),
`quantitĂ©` DÉCIMALE (38,5),
`base` DECIMAL (38,5),
`tax` DECIMAL (38.5),
«total» DÉCIMAL (38,5),
`tax_rate` DECIMAL (38.5),
`tax_in` STRING,
`charge_kind` STRING,
`privilege_id` DECIMAL (38.0),
`charge_ref_id` DECIMAL (38.0),
`ebid` DECIMAL (38.0),
`facture_id` DECIMAL (38.0),
`zero_state_id` DECIMAL (38,0),
`user_id` DECIMAL (38.0),
`bid` DECIMAL (38.0),
`quantity_real` DECIMAL (38,5),
TIMESTAMP `load_dttm`,
`src_id` SMALLINT,
`package_id` BIGINT,
`wf_run_id` BIGINT,
`md5` STRING
)
CHAMPS DÉLIMITÉS AU FORMAT DE LIGNE TERMINÉS PAR '\ t'
ENREGISTRÉ COMME INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
EMPLACEMENT 'moyen de se former en hdfs'
TBLPROPERTIES ('auto.purge' = 'true', 'transient_lastDlastDdlTime' = '1489060201');
insérer dans le schéma.GP_000001_CPA_CHARGE (`charge_id`,
`svc_id`,
`vnd_id`,
`prix`,
`quantité`,
`base`,
`taxe`,
`total`,
`tax_rate`,
`tax_in`,
`charge_kind`,
`privilege_id`,
`charge_ref_id`,
`ebid`,
`facture_id`,
`zero_state_id`,
`user_id`,
enchérir
`quantity_real`,
`load_dttm`,
`src_id`,
package_id
`wf_run_id`,
`md5`)

sélectionnez `charge_id`,
`svc_id`,
`vnd_id`,
`prix`,
`quantité`,
`base`,
`taxe`,
`total`,
`tax_rate`,
`tax_in`,
`charge_kind`,
`privilege_id`,
`charge_ref_id`,
`ebid`,
`facture_id`,
`zero_state_id`,
`user_id`,
enchérir
`quantity_real`,
load_dttm,
src_id
package_id,
wf_run_id,
md5 du schéma.Z_GP_000001_TABLE_20180807;

Comment automatiser


Pour résoudre le problÚme, nous avons utilisé:

  • Jenkins - en tant qu'orchestre et outil de mise en Ɠuvre du processus CI.
  • Python - il implĂ©mente des fonctionnalitĂ©s et des tests unitaires.
  • SQL - pour accĂ©der Ă  la base de donnĂ©es de mĂ©tadonnĂ©es.
  • Script shell - pour copier des artefacts entre des rĂ©pertoires et crĂ©er des scripts dans des projets multi-tĂąches exĂ©cutĂ©s sur le serveur Jenkins.

Pour commencer, nous travaillons initialement avec un grand nombre de sources.Par conséquent, en utilisant le script Shell comme paramÚtre de démarrage, nous transmettons les ID des sources aux fonctions SQL pour les identifier. Les fonctions SQL résultantes seront ensuite exécutées automatiquement dans la base de données de métadonnées. Sur la base des métadonnées disponibles, ces fonctions forment un fichier avec une liste de nouvelles fonctions SQL pour chacune des tables source afin que nous puissions les appeler ultérieurement dans le programme exécutable. Le résultat de l'exécution des fonctions générées est le transfert des valeurs DDL, JSON ou EDWEX au paramÚtre de sortie.

C'est là que Python se connecte, avec toutes les fonctionnalités exécutables et les tests unitaires écrits. Avant de démarrer l'un des modules de roulement des artefacts à l'aide de tests unitaires, le transfert de paramÚtres correct est vérifié pour exécuter des scripts python. Les tests vérifient non seulement l'exactitude des paramÚtres d'entrée, mais également leur présence dans le module de métadonnées, les tailles des fichiers créés et les dates de leur création. Les tests contrÎlent également le nombre de nouveaux artefacts créés et le nombre d'artefacts existants. Nous optimisons donc l'utilisation des ressources du serveur, car nous ne prenons que les nouveaux fichiers à déplacer et ne réinstallons pas les modÚles existants.

Et ce n'est qu'aprĂšs avoir rĂ©ussi toutes les vĂ©rifications qu'un programme python est exĂ©cutĂ© qui crĂ©e les artefacts nĂ©cessaires et dĂ©compose le rĂ©sultat dans les dossiers de projet nĂ©cessaires sur le serveur. Python dirige non seulement les fichiers json gĂ©nĂ©rĂ©s dans des rĂ©pertoires, mais forme Ă©galement des structures dans Data Lake afin que les donnĂ©es se chargent correctement. Lors de la gĂ©nĂ©ration d'artefacts DDL, ils sont non seulement enregistrĂ©s pour une analyse et une rĂ©utilisation ultĂ©rieures, mais peuvent Ă©galement ĂȘtre immĂ©diatement installĂ©s dans la base de donnĂ©es Ă  l'aide de nouveaux modĂšles et structures spĂ©cifiĂ©s dans le module de mĂ©tadonnĂ©es. Cela vous permet de crĂ©er des centaines de tables en peu de temps sans impliquer de travail manuel.

Et oĂč est Jenkins ici?


Jenkins entre lorsqu'il est nécessaire de gérer visuellement tous ces processus à l'aide d'une interface.

Cet outil a été sélectionné car il:

  • couvre entiĂšrement les besoins d'automatisation de l'assemblage et de l'installation
  • vous permet de concevoir un mĂ©canisme d'assemblage d'artefacts avec la mise en Ɠuvre du processus d'auto-test
  • vous permet de gĂ©rer facilement les lancements de travaux et de surveiller l'exĂ©cution Ă  une personne loin de la programmation
  • vous permet de configurer le mĂ©canisme de journalisation de maniĂšre Ă  ce que le rĂ©sultat de l'exĂ©cution soit comprĂ©hensible pour toute personne de l'Ă©quipe. Le problĂšme dans l'assembly sera explicitement indiquĂ© ou le processus se terminera avec succĂšs.

Pour résoudre les tùches, nous avons créé plusieurs projets multi-tùches. Ce type de projet a été utilisé, car il peut fonctionner en parallÚle avec d'autres travaux en un seul démarrage. Chacun des jobs est responsable de l'implémentation de son bloc de fonctionnalités. Nous avons donc remplacé le processus séquentiel d'obtention d'artefacts par des artefacts parallÚles autonomes. Tout commence séparément: la formation d'EDWEX, JSON, DDL, la formation de la structure dans HIVE, l'installation des structures de table dans la base de données. Nous analysons le résultat à différentes étapes de la formation des artefacts et procédons au lancement des actions suivantes en cas de succÚs.

La partie Jenkins est implĂ©mentĂ©e sans trop de trucs. Les paramĂštres de chaĂźne ou d' exĂ©cution sont envoyĂ©s Ă  l'entrĂ©e pour dĂ©marrer le code python. Le paramĂštre String est une fenĂȘtre permettant de saisir une valeur de type str avant de dĂ©marrer. Le paramĂštre run peut ĂȘtre transfĂ©rĂ© Ă  la volĂ©e vers un autre job pour exĂ©cution, il suffit pour cela d'indiquer de quel projet il est nĂ©cessaire de prendre la variable reçue. En outre, un nƓud est transmis en tant que paramĂštre distinct pour l'exĂ©cution. Ici, le partitionnement en runtimes sur DEV, TST, UAT, PRD vient d'ĂȘtre implĂ©mentĂ©. Un travail distinct a Ă©tĂ© utilisĂ© pour transfĂ©rer les fichiers EDWEX reçus vers SVN avec le numĂ©ro de rĂ©vision afin de pouvoir suivre les versions des structures modifiĂ©es.

Un exemple d'interface dans Jenkins:



Le rĂ©sultat de l'exĂ©cution du travail est la crĂ©ation et l'installation des artefacts nĂ©cessaires, leur transfert vers SVN et la formation d'un rapport HTML qui affiche le succĂšs des tests unitaires rĂ©ussis et les rĂ©sultats de l'assemblage et de l'installation des artefacts. Les travaux peuvent ĂȘtre exĂ©cutĂ©s individuellement avec vos mains ou en mode automatique, aprĂšs avoir prĂ©alablement construit une chaĂźne d'exĂ©cution.


Architecture du mécanisme d'assemblage et d'installation

Pour résumer


Beaucoup de travail a Ă©tĂ© fait pour automatiser la formation des artefacts. Auparavant, vous deviez monter manuellement sur le serveur, exĂ©cuter des scripts shell, puis Ă©tudier et modifier les donnĂ©es pendant longtemps avec vos mains. Maintenant, cliquez simplement sur le bouton DĂ©marrer, spĂ©cifiez l'ID du systĂšme source, le numĂ©ro de modĂšle et la boucle d'exĂ©cution. Avec l'aide de Jenkins, il a Ă©tĂ© possible de structurer et de dĂ©composer l'ensemble du mĂ©canisme d'assemblage et d'installation des artefacts en Ă©tapes indĂ©pendantes. Les vĂ©rifications nĂ©cessaires ont Ă©tĂ© ajoutĂ©es avant de commencer la formation des artefacts et leur intĂ©gration. Les artefacts reçus sont automatiquement transfĂ©rĂ©s vers SVN, ce qui simplifie le travail avec des Ă©quipes connexes d'analystes systĂšme et de modĂ©lisateurs de donnĂ©es. Des vĂ©rifications sont mises en Ɠuvre pour Ă©viter les lancements inactifs de la formation d'artefacts et confirmer leur exactitude.

En conséquence, nous avons réduit le processus fastidieux d'assemblage et d'installation des artefacts de modÚle de plusieurs heures à quelques minutes. Et surtout, ils ont éliminé les erreurs dues au facteur humain, qui se sont inévitablement produites dans un processus de routine complexe.

Source: https://habr.com/ru/post/fr428101/


All Articles