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 Edwexpack - 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 DDLmodifier 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'installationPour 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.