Todo comenzó con el hecho de que nos enfrentamos con la necesidad de formar estructuras EDWEX, JSON, DDL de manera rápida y correcta y luego rodarlas en diferentes contornos de bases de datos relacionales. Por contornos, quiero decir abreviaturas que todos conocen: DEV, TST, UAT, PRD.

En ese momento, hicimos casi todo manualmente: generamos DDL y recopilamos edwex y json basados en metadatos de la base de datos Oracle. Hay muchos parámetros de entrada. Si pierde uno, formará una entidad incorrectamente. Y dado que todo el proceso de formación fue constante y continuo, el error se detectará solo al final. Sobre cómo todos automatizamos y superamos los errores, lea debajo del corte.
Un poco sobre infraestructura
Antes de completar los datos en las tablas de bases de datos relacionales, debemos aceptarlos desde la fuente, en cualquier formato, por ejemplo, en Excel. Los datos de la fuente que utilizan el mecanismo interno se transportan a Hadoop (Data Lake). Hive tiene instalado el complemento Hive: con su ayuda podemos ver el contenido de los archivos utilizando una sintaxis similar a SQL.
Para transferir los datos en Data Lake en forma de tablas, necesitamos json, que se forman sobre la base de metadatos. Para analizar datos en formato Excel, necesitamos crear archivos EDWEX. Los archivos EDWEX (EDW_EXTRACTOR) son un conjunto de artefactos que contienen conjuntos de tablas con el nombre, así como conjuntos de campos para cada una de estas tablas, que se forman sobre la base de metadatos. Dependiendo de la versión del modelo y la ID de origen, el conjunto de campos varía. La formación de DDL es necesaria para crear las tablas en la base de datos de Hive a nivel de datos operativos y en la base de datos de Greenplum a nivel de datos detallados y agregados. Es decir, los datos se transfieren principalmente a Hive, si es necesario, se filtran y transfieren a Greenplum para la posterior manipulación de datos y la creación de escaparates basados en ellos.
Ejemplo de artefacto de Edwexpaquete: contiene un conjunto de tablas
datos: contiene un conjunto de campos
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
Ejemplo de artefacto 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 " }
Ejemplos de artefactos DDLalter table esquema. GP_000001_TABLE renombrar a Z_GP_000001_TABLE_20180807;
crear esquema de tabla. GP_000001_TABLE
(
`charge_id` DECIMAL (38.0),
`svc_id` DECIMAL (38.0),
`vnd_id` DECIMAL (38.0),
`precio` DECIMAL (38.5),
`cantidad` DECIMAL (38.5),
`base` DECIMAL (38.5),
`fiscal` DECIMAL (38.5),
`total` DECIMAL (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),
`invoice_id` DECIMAL (38.0),
`zero_state_id` DECIMAL (38,0),
`user_id` DECIMAL (38.0),
`bid` DECIMAL (38.0),
`cantidad_real` DECIMAL (38.5),
`load_dttm` TIMESTAMP,
`src_id` SMALLINT,
`package_id` BIGINT,
`wf_run_id` BIGINT,
`md5` STRING
)
FORMATO DE FILAS CAMPOS DELIMITADOS TERMINADOS POR '\ t'
ALMACENADO COMO INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
UBICACIÓN 'forma de formar en hdfs'
TBLPROPERTIES ('auto.purge' = 'true', 'transient_lastDlastDdlTime' = '1489060201');
insertar en el esquema. GP_000001_TABLE (`charge_id`,
`svc_id`,
`vnd_id`,
`precio`,
`cantidad`,
`base`,
"impuesto",
`total`,
`tax_rate`,
`tax_in`,
`charge_kind`,
`privilege_id`,
`charge_ref_id`,
`ebid`,
`invoice_id`,
`zero_state_id`,
`user_id`,
oferta
`cantidad_real`,
`load_dttm`,
`src_id`,
package_id
`wf_run_id`,
`md5`)
seleccione `charge_id`,
`svc_id`,
`vnd_id`,
`precio`,
`cantidad`,
`base`,
"impuesto",
`total`,
`tax_rate`,
`tax_in`,
`charge_kind`,
`privilege_id`,
`charge_ref_id`,
`ebid`,
`invoice_id`,
`zero_state_id`,
`user_id`,
oferta
`cantidad_real`,
load_dttm,
src_id
package_id,
wf_run_id,
md5 del esquema.Z_GP_000001_TABLE_20180807;
alter table esquema. GP_000001_TABLE renombrar a Z_GP_000001_TABLE_20180807;
crear esquema de tabla. GP_000001_TABLE
(
`charge_id` DECIMAL (38.0),
`svc_id` DECIMAL (38.0),
`vnd_id` DECIMAL (38.0),
`precio` DECIMAL (38.5),
`cantidad` DECIMAL (38.5),
`base` DECIMAL (38.5),
`fiscal` DECIMAL (38.5),
`total` DECIMAL (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),
`invoice_id` DECIMAL (38.0),
`zero_state_id` DECIMAL (38,0),
`user_id` DECIMAL (38.0),
`bid` DECIMAL (38.0),
`cantidad_real` DECIMAL (38.5),
`load_dttm` TIMESTAMP,
`src_id` SMALLINT,
`package_id` BIGINT,
`wf_run_id` BIGINT,
`md5` STRING
)
FORMATO DE FILAS CAMPOS DELIMITADOS TERMINADOS POR '\ t'
ALMACENADO COMO INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
UBICACIÓN 'forma de formar en hdfs'
TBLPROPERTIES ('auto.purge' = 'true', 'transient_lastDlastDdlTime' = '1489060201');
insertar en el esquema. GP_000001_CPA_CHARGE (`charge_id`,
`svc_id`,
`vnd_id`,
`precio`,
`cantidad`,
`base`,
"impuesto",
`total`,
`tax_rate`,
`tax_in`,
`charge_kind`,
`privilege_id`,
`charge_ref_id`,
`ebid`,
`invoice_id`,
`zero_state_id`,
`user_id`,
oferta
`cantidad_real`,
`load_dttm`,
`src_id`,
package_id
`wf_run_id`,
`md5`)
seleccione `charge_id`,
`svc_id`,
`vnd_id`,
`precio`,
`cantidad`,
`base`,
"impuesto",
`total`,
`tax_rate`,
`tax_in`,
`charge_kind`,
`privilege_id`,
`charge_ref_id`,
`ebid`,
`invoice_id`,
`zero_state_id`,
`user_id`,
oferta
`cantidad_real`,
load_dttm,
src_id
package_id,
wf_run_id,
md5 del esquema.Z_GP_000001_TABLE_20180807;
¿Cómo automatizar
Para resolver el problema, utilizamos:
- Jenkins: como orquesta y herramienta para implementar el proceso de CI.
- Python: implementa la funcionalidad y las pruebas unitarias.
- SQL: para acceder a la base de datos de metadatos.
- Script de Shell: para copiar artefactos entre directorios y crear scripts en proyectos de múltiples trabajos que se ejecutan en el servidor Jenkins.
Para empezar, inicialmente trabajamos con una gran cantidad de fuentes, por lo tanto, al usar el script de Shell como parámetro de inicio, pasamos las ID de las fuentes a las funciones de SQL para identificarlas. Las funciones SQL resultantes se ejecutarán automáticamente en la base de datos de metadatos. En función de los metadatos disponibles, estas funciones forman un archivo con una lista de nuevas funciones SQL para cada una de las tablas de origen para que posteriormente podamos llamarlas en el programa ejecutable. El resultado de la ejecución de las funciones generadas es la transferencia de los valores DDL, JSON o EDWEX al parámetro de salida.
Aquí es donde Python se conecta, con toda la funcionalidad ejecutable y las pruebas unitarias escritas. Antes de comenzar cualquiera de los módulos para rodar artefactos utilizando pruebas unitarias, se verifica la transferencia de parámetros correcta para ejecutar scripts de python. Las pruebas no solo verifican la corrección de los parámetros de entrada, sino también su presencia en el módulo de metadatos, los tamaños de los archivos creados y las fechas de su creación. Las pruebas también controlan la cantidad de artefactos nuevos creados y la cantidad de artefactos existentes. Por lo tanto, optimizamos el uso de los recursos del servidor, ya que solo tomamos nuevos archivos para rodar y no reinstalamos los modelos existentes nuevamente.
Y solo después de pasar con éxito todas las comprobaciones se ejecuta un programa de Python que crea los artefactos necesarios y descompone el resultado en las carpetas de proyecto necesarias en el servidor. Python no solo dirige los archivos json generados a directorios, sino que también forma estructuras en Data Lake para que los datos se carguen correctamente. Al generar artefactos DDL, no solo se guardan para su posterior análisis y reutilización, sino que también se pueden instalar inmediatamente en la base de datos utilizando nuevos modelos y estructuras especificadas en el módulo de metadatos. Esto le permite crear cientos de tablas en poco tiempo sin involucrar trabajo manual.
¿Y dónde está Jenkins aquí?
Jenkins ingresa cuando es necesario administrar todos estos procesos visualmente usando una interfaz.
Esta herramienta fue seleccionada porque:
- cubre completamente las necesidades de automatización del ensamblaje y la instalación
- le permite diseñar un mecanismo para ensamblar artefactos con la implementación del proceso de autocomprobación
- le permite administrar fácilmente los lanzamientos de trabajos y supervisar la ejecución a una persona que está lejos de programar
- le permite configurar el mecanismo de registro de tal manera que el resultado de la ejecución sea comprensible para cualquier persona del equipo. El problema en el ensamblado se indicará explícitamente o el proceso se completará con éxito.
Para resolver las tareas, hemos creado varios proyectos de trabajo múltiple. Este tipo de proyecto se ha utilizado, ya que puede funcionar en paralelo con otros trabajos en un solo inicio. Cada trabajo es responsable de la implementación de su bloque de funcionalidad. Así que reemplazamos el proceso secuencial de obtención de artefactos por otros paralelos autónomos. Todo comienza por separado: la formación de EDWEX, JSON, DDL, la formación de la estructura en HIVE, la instalación de estructuras de tabla en la base de datos. Analizamos el resultado en diferentes etapas de la formación de artefactos y procedemos a lanzar acciones posteriores si tienen éxito.
La parte de Jenkins se implementa sin muchos trucos.
Los parámetros de
cadena o
ejecución se envían a la entrada para iniciar el código de Python.
El parámetro
String es una ventana para ingresar un valor de tipo str antes de comenzar.
El parámetro de
ejecución se puede transferir sobre la marcha a otro trabajo para su ejecución, para esto basta con indicar de qué proyecto es necesario tomar la variable recibida. Además, se pasa un nodo como un parámetro separado para la ejecución. Aquí se acaba de implementar la partición en tiempos de ejecución en DEV, TST, UAT, PRD. Se usó un trabajo separado para transferir los archivos EDWEX recibidos a SVN con el número de revisión para poder rastrear las versiones de las estructuras modificadas.
Una interfaz de ejemplo en Jenkins:

El resultado de la ejecución del trabajo es la creación e instalación de los artefactos necesarios, su transferencia a SVN y la formación de un informe HTML que muestra el éxito de pasar las pruebas unitarias y los resultados del montaje e instalación de los artefactos. Los trabajos pueden ejecutarse individualmente con sus manos, o en modo automático, habiendo construido previamente una cadena de ejecución.
Arquitectura del mecanismo de montaje e instalaciónPara resumir
Se ha trabajado mucho para automatizar la formación de artefactos. Anteriormente, tenía que subir manualmente al servidor, ejecutar scripts de shell y luego estudiar y editar los datos durante mucho tiempo con las manos. Ahora solo haga clic en el botón de inicio, especifique la ID del sistema de origen, el número de modelo y el bucle de ejecución. Con la ayuda de Jenkins, fue posible estructurar y descomponer todo el mecanismo de montaje e instalación de artefactos en etapas independientes. Se agregaron los controles necesarios antes de comenzar la formación de artefactos y su integración. Los artefactos recibidos se transfieren automáticamente a SVN, lo que simplifica el trabajo con equipos relacionados de analistas de sistemas y modeladores de datos. Los controles se implementan para evitar lanzamientos inactivos de formación de artefactos y confirmar su corrección.
Como resultado, redujimos el largo proceso de ensamblaje e instalación de artefactos modelo de varias horas a un par de minutos. Y lo más importante, eliminaron la ocurrencia de errores debido al factor humano, que inevitablemente surgió en un proceso rutinario complejo.