Tudo começou com o fato de nos depararmos com a necessidade de formar rápida e corretamente estruturas EDWEX, JSON, DDL e depois rolar em diferentes contornos de bancos de dados relacionais. Por contornos, quero dizer abreviações que todo mundo conhece - DEV, TST, UAT, PRD.

Naquela época, fizemos quase tudo manualmente: geramos DDL e coletamos edwex e json com base em metadados do banco de dados Oracle. Existem muitos parâmetros de entrada. Se você perder um, formará uma entidade incorretamente. E como todo o processo de formação foi consistente e contínuo, o erro será detectado apenas no final. Sobre como todos automatizamos e superamos os erros, leia abaixo.
Um pouco sobre infraestrutura
Antes de preenchermos os dados nas tabelas dos bancos de dados relacionais, precisamos aceitá-los da fonte - em qualquer formato, por exemplo, no Excel. Os dados da fonte usando o mecanismo interno são transportados para o Hadoop (Data Lake). O Hive possui o complemento Hive instalado - com sua ajuda, podemos visualizar o conteúdo dos arquivos usando a sintaxe semelhante ao SQL.
Para transferir os dados no Data Lake na forma de tabelas, precisamos do json's, formado com base em metadados. Para analisar dados no formato Excel, precisamos criar arquivos EDWEX. Arquivos EDWEX (EDW_EXTRACTOR) são um determinado conjunto de artefatos que contêm conjuntos de tabelas com o nome, bem como conjuntos de campos para cada uma dessas tabelas, formados com base em metadados. Dependendo da versão do modelo e do ID de origem, o conjunto de campos varia. A formação de DDL é necessária para criar as próprias tabelas no banco de dados Hive no nível dos dados operacionais e no banco de dados Greenplum no nível dos dados detalhados e agregados. Ou seja, os dados são inicialmente transferidos para o Hive, se necessário, filtrados e transferidos para o Greenplum para manipulação de dados subsequente e criação de fachadas de lojas baseadas nele.
Exemplo de artefato Edwexpack - contém um conjunto de tabelas
data - contém um 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
Exemplo de artefato 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 " }
Artefatos DDL de Exemploaltere o esquema da tabela.GP_000001_TABLE renomeie para Z_GP_000001_TABLE_20180807;
criar esquema de tabela.GP_000001_TABLE
(
`charge_id` DECIMAL (38.0),
`svc_id` DECIMAL (38.0),
`vnd_id` DECIMAL (38.0),
DECIMAL (38.5),
«quantidade» DECIMAL (38.5),
DECIMAL (base) (38.5)
DECIMAL (38.5),
DECIMAL «total» (38,5)
«tax_rate» DECIMAL (38.5),
`tax_in` STRING,
`charge_kind` STRING,
`privilégio_id` DECIMAL (38.0),
`charge_ref_id` DECIMAL (38.0),
DECIMAL (38.0)
`invoice_id` DECIMAL (38.0),
`zero_state_id` DECIMAL (38,0),
`user_id` DECIMAL (38.0),
`bid` DECIMAL (38.0),
`Quantity_real` DECIMAL (38.5),
`load_dttm` TIMESTAMP,
`src_id` SMALLINT,
`package_id` BIGINT,
`wf_run_id` BIGINT,
`md5` STRING
)
CAMPOS DELIMITADOS DE FORMATO DE LINHA TERMINADOS POR '\ t'
ARMAZENADO COMO INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCAL 'maneira de formar em hdfs'
TBLPROPERTIES ('auto.purge' = 'true', 'transient_lastDlastDdlTime' = '1489060201');
inserir no esquema.GP_000001_TABLE (`charge_id`,
`svc_id`,
`vnd_id`,
`preço ',
`quantidade ',
`base`,
«imposto»,
«total»,
`tax_rate`,
`tax_in`,
`charge_kind`,
`privilege_id`,
`charge_ref_id`,
`ebid`,
`invoice_id`,
`zero_state_id`,
`user_id`,
licitar
`Quantity_real`,
`load_dttm`,
`src_id`,
package_id
`wf_run_id`,
`md5`)
selecione `charge_id`,
`svc_id`,
`vnd_id`,
`preço ',
`quantidade ',
`base`,
«imposto»,
«total»,
`tax_rate`,
`tax_in`,
`charge_kind`,
`privilege_id`,
`charge_ref_id`,
`ebid`,
`invoice_id`,
`zero_state_id`,
`user_id`,
licitar
`Quantity_real`,
load_dttm,
src_id
package_id,
wf_run_id,
md5 do esquema.Z_GP_000001_TABLE_20180807;
altere o esquema da tabela.GP_000001_TABLE renomeie para Z_GP_000001_TABLE_20180807;
criar esquema de tabela.GP_000001_TABLE
(
`charge_id` DECIMAL (38.0),
`svc_id` DECIMAL (38.0),
`vnd_id` DECIMAL (38.0),
DECIMAL (38.5),
«quantidade» DECIMAL (38.5),
DECIMAL (base) (38.5)
DECIMAL (38.5),
DECIMAL «total» (38,5)
«tax_rate» DECIMAL (38.5),
`tax_in` STRING,
`charge_kind` STRING,
`privilégio_id` DECIMAL (38.0),
`charge_ref_id` DECIMAL (38.0),
DECIMAL (38.0)
`invoice_id` DECIMAL (38.0),
`zero_state_id` DECIMAL (38,0),
`user_id` DECIMAL (38.0),
`bid` DECIMAL (38.0),
`Quantity_real` DECIMAL (38.5),
`load_dttm` TIMESTAMP,
`src_id` SMALLINT,
`package_id` BIGINT,
`wf_run_id` BIGINT,
`md5` STRING
)
CAMPOS DELIMITADOS DE FORMATO DE LINHA TERMINADOS POR '\ t'
ARMAZENADO COMO INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCAL 'maneira de formar em hdfs'
TBLPROPERTIES ('auto.purge' = 'true', 'transient_lastDlastDdlTime' = '1489060201');
inserir no esquema.GP_000001_CPA_CHARGE (`charge_id`,
`svc_id`,
`vnd_id`,
`preço ',
`quantidade ',
`base`,
«imposto»,
«total»,
`tax_rate`,
`tax_in`,
`charge_kind`,
`privilege_id`,
`charge_ref_id`,
`ebid`,
`invoice_id`,
`zero_state_id`,
`user_id`,
licitar
`Quantity_real`,
`load_dttm`,
`src_id`,
package_id
`wf_run_id`,
`md5`)
selecione `charge_id`,
`svc_id`,
`vnd_id`,
`preço ',
`quantidade ',
`base`,
«imposto»,
«total»,
`tax_rate`,
`tax_in`,
`charge_kind`,
`privilege_id`,
`charge_ref_id`,
`ebid`,
`invoice_id`,
`zero_state_id`,
`user_id`,
licitar
`Quantity_real`,
load_dttm,
src_id
package_id,
wf_run_id,
md5 do esquema.Z_GP_000001_TABLE_20180807;
Como automatizar
Para resolver o problema, usamos:
- Jenkins - como orquestra e ferramenta para implementar o processo de IC.
- Python - implementa testes de funcionalidade e unidade.
- SQL - para acessar o banco de dados de metadados.
- Script de shell - para copiar artefatos entre diretórios e criar scripts em projetos com vários trabalhos em execução no servidor Jenkins.
Para começar, trabalhamos inicialmente com um grande número de fontes; portanto, usando o script Shell como parâmetro de início, passamos os IDs das fontes para as funções SQL para identificá-las. As funções SQL resultantes serão posteriormente executadas automaticamente no banco de dados de metadados. Com base nos metadados disponíveis, essas funções formam um arquivo com uma lista de novas funções SQL para cada uma das tabelas de origem, para que possamos chamá-las posteriormente no programa executável. O resultado da execução das funções geradas é a transferência dos valores DDL, JSON ou EDWEX para o parâmetro de saída.
É aqui que o Python se conecta, com todas as funcionalidades executáveis e testes de unidade gravados. Antes de iniciar qualquer um dos módulos para rolar artefatos usando testes de unidade, a transferência correta de parâmetros é verificada para executar scripts python. Os testes não apenas verificam a correção dos parâmetros de entrada, mas também a presença deles no módulo de metadados, os tamanhos dos arquivos criados e as datas de sua criação. Os testes também monitoram o número de novos artefatos criados e o número de artefatos existentes. Portanto, otimizamos o uso dos recursos do servidor, porque pegamos apenas novos arquivos para rolagem e não reinstalamos os modelos existentes novamente.
E somente após a aprovação bem-sucedida de todas as verificações é executado um programa python que cria os artefatos necessários e decompõe o resultado nas pastas de projeto necessárias no servidor. O Python não apenas direciona os arquivos json gerados para os diretórios, mas também forma estruturas no Data Lake para que os dados sejam carregados corretamente. Ao gerar artefatos DDL, eles não são salvos apenas para análise e reutilização posteriores, mas também podem ser instalados imediatamente no banco de dados usando novos modelos e estruturas especificados no módulo de metadados. Isso permite criar centenas de tabelas em um curto espaço de tempo sem envolver trabalho manual.
E onde está Jenkins aqui?
Jenkins entra quando é necessário gerenciar todos esses processos visualmente usando uma interface.
Esta ferramenta foi selecionada porque:
- cobre totalmente as necessidades de automação da montagem e instalação
- permite projetar um mecanismo para montar artefatos com a implementação do processo de autoteste
- permite gerenciar facilmente lançamentos de empregos e monitorar a execução de uma pessoa que está longe de programar
- permite que você configure o mecanismo de log de maneira que o resultado da execução seja compreensível para qualquer pessoa da equipe. O problema na montagem será indicado explicitamente ou o processo será concluído com êxito.
Para resolver as tarefas, criamos vários projetos com vários empregos. Esse tipo de projeto foi usado, pois pode funcionar em paralelo com outros trabalhos em uma única inicialização. Cada tarefa é responsável pela implementação de seu bloco de funcionalidades. Então, substituímos o processo seqüencial de obter artefatos por paralelos autônomos. Tudo começa separadamente: a formação de EDWEX, JSON, DDL, a formação da estrutura no HIVE, a instalação de estruturas de tabela no banco de dados. Analisamos o resultado em diferentes estágios da formação de artefatos e procedemos ao lançamento de ações subsequentes, se bem-sucedidas.
A parte de Jenkins é implementada sem muitos truques.
Os parâmetros
String ou
Run são enviados à entrada para iniciar o código python.
O parâmetro
String é uma janela para inserir um valor do tipo str antes de iniciar.
O parâmetro de
execução pode ser transferido imediatamente para outro trabalho para execução, bastando apenas indicar de qual projeto é necessário obter a variável recebida. Além disso, um nó é passado como um parâmetro separado para execução. Aqui, o particionamento em tempos de execução no DEV, TST, UAT, PRD é apenas implementado. Um trabalho separado foi usado para transferir os arquivos EDWEX recebidos para o SVN com o número de revisão para poder rastrear versões das estruturas alteradas.
Um exemplo de interface no Jenkins:

O resultado da execução da tarefa é a criação e instalação dos artefatos necessários, sua transferência para o SVN e a formação de um relatório HTML que mostra o sucesso da aprovação nos testes de unidade e os resultados da montagem e instalação dos artefatos. Os trabalhos podem ser executados individualmente com as mãos, ou no modo automático, tendo construído anteriormente uma cadeia de execução.
Arquitetura de mecanismo de montagem e instalaçãoResumir
Muito trabalho foi feito para automatizar a formação de artefatos. Anteriormente, era necessário subir manualmente no servidor, executar scripts de shell e, em seguida, estudar e editar os dados por um longo tempo com as mãos. Agora basta clicar no botão Iniciar, especifique o ID do sistema de origem, o número do modelo e o loop de execução. Com a ajuda de Jenkins, foi possível estruturar e decompor todo o mecanismo de montagem e instalação de artefatos em estágios independentes. As verificações necessárias foram incluídas antes de iniciar a formação de artefatos e sua integração. Os artefatos recebidos são transferidos automaticamente para o SVN, o que simplifica o trabalho com equipes relacionadas de analistas de sistema e modeladores de dados. As verificações são implementadas para evitar lançamentos ociosos da formação de artefatos e confirmar sua correção.
Como resultado, reduzimos o processo demorado de montar e instalar artefatos do modelo de várias horas para alguns minutos. E o mais importante, eles eliminaram a ocorrência de erros devido ao fator humano, que inevitavelmente surgiu em um complexo processo de rotina.