Arquitetura do programa de alta disponibilidade do AWS Design - implantação e desenvolvimento do Glue (ETL)

Dependência: este artigo requer uma compreensão dos fundamentos do design da arquitetura da AWS

O AWS Glue é um serviço ETL (extrair, transformar e carregar) totalmente gerenciado que permite classificar, limpar e enriquecer dados de maneira fácil e econômica, além de mover dados de maneira confiável entre vários armazenamentos e fluxos de dados. O AWS Glue consiste em um repositório de metadados central chamado Catálogo de dados do AWS Glue, um mecanismo ETL que gera automaticamente código Python ou Scala e um agendador flexível que lida com a resolução de dependências, monitoramento de trabalhos e novas tentativas. O AWS Glue é sem servidor, portanto, não há infraestrutura para configurar ou gerenciar.

O AWS Glue foi projetado para funcionar com dados semiestruturados. Ele apresenta um componente chamado Dynamic Frames que você pode usar em seus scripts ETL. Um quadro dinâmico é semelhante a um Apache Spark DataFrame, que é uma abstração de dados para organizar dados em linhas e colunas, exceto que cada registro é autodescritivo, portanto, nenhum esquema é necessário para começar. Com Dynamic Frames, você obtém flexibilidade arquitetônica e um conjunto de transformações avançadas projetadas especificamente para Dynamic Frames. Você pode converter entre Dynamic Frames e Spark DataFrames para aproveitar as transformações AWS Glue e Spark para realizar a análise necessária.

Você pode usar o console do AWS Glue para descobrir dados, transformá-los e disponibilizá-los para pesquisa e consulta. O console chama os serviços subjacentes para coordenar o trabalho necessário para transformar os dados. Você também pode usar operações de API do AWS Glue para interagir com os serviços do AWS Glue. Use um ambiente de desenvolvimento familiar para editar, depurar e testar seu código Python ou Scala Apache Spark ETL.

1. Distribuir Cola

Implante cola usando cloudformation, incluindo bancos de dados, conexões, rastreadores, trabalhos, gatilhos.

Criar uma função IAM

estratégia adicional

AmazonS3FullAccess
AmazonSNSFullAccess
AWSGlueServiceRole
AmazonRDSFullAccess
SecretsManagerReadWrite
AWSLambdaRole

relação de confiança

{
    
    
    "Version": "2012-10-17",
    "Statement": [
        {
    
    
            "Effect": "Allow",
            "Principal": {
    
    
                "Service": "glue.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

Criar Cola

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
    Environment:
        Type: String
        Default: DEV
    EnvironmentName:
        Type: String
        Default: d
    CustomerName:
        Description: The name of the customer
        Type: String
        #TODO:
        Default: your-company-name
    ProjectName:
        Description: The name of the project
        Type: String
        #TODO:
        Default: your-project-name
    CrawlerRoleARN:
        Type: String
        #TODO:
        Default: XXXXXXXXXXXXX
    ScriptLocation:
        Type: String
        #TODO: a empty file
        Default: s3://XXXXXX-s3/aws-glue-scripts
    SSLCertificateLocation:
        Type: String
        #TODO:a pem file
        Default: s3://XXXXXX-s3/aws-glue-scripts/xxxxxxx.pem
    ConnAvailabilityZone:
        Description:
            The name of the AvailabilityZone,Currently the field must be populated, but it will be
            deprecated in the future
        Type: String
        #TODO:
        Default: cn-northwest-xxx
    ConnSecurityGroups:
        Description: The name of the Secret
        Type: List<AWS::EC2::SecurityGroup::Id>
        #TODO:
        Default: sg-xxxxxxxxx, sg-xxxxxxxxx
    ConnSubnetId:
        Description: The name of the Secret
        Type: String
        #TODO:
        Default: subnet-xxxxxxxxx
    OriginSecretid:
        Description: The name of the Secret
        Type: String
        #TODO:
        Default: xxxxxxxxxxxxxxxxx
    OriginJDBCString:
        Type: String
        #TODO: jdbc:postgresql://{database ARN}:{port}/{databasename}
        Default: jdbc:postgresql://xxxx:xxx/xxxx
    OriginJDBCPath:
        Type: String
        #TODO: Database/Schema/%
        Default: xxxx/xxxx/%
Resources:
    #Create Origin to contain tables created by the crawler
    OriginDatabase:
        Type: AWS::Glue::Database
        Properties:
            CatalogId: !Ref AWS::AccountId
            DatabaseInput:
                Name: !Sub ${
    
    CustomerName}-${
    
    ProjectName}-origin-${
    
    EnvironmentName}-gluedatabase
                Description: 'AWS Glue container to hold metadata tables for the Origin crawler'
    #Create Origin Connection
    OriginConnectionPostgreSQL:
        Type: AWS::Glue::Connection
        Properties:
            CatalogId: !Ref AWS::AccountId
            ConnectionInput:
                Description: 'Connect to Origin PostgreSQL database.'
                ConnectionType: 'JDBC'
                PhysicalConnectionRequirements:
                    AvailabilityZone: !Ref ConnAvailabilityZone
                    SecurityGroupIdList: !Ref ConnSecurityGroups
                    SubnetId: !Ref ConnSubnetId
                ConnectionProperties:
                    {
    
    
                        'JDBC_CONNECTION_URL': !Ref OriginJDBCString,
                        # If use ssl
                        'JDBC_ENFORCE_SSL': true,
                        'CUSTOM_JDBC_CERT': !Ref SSLCertificateLocation,
                        'SKIP_CUSTOM_JDBC_CERT_VALIDATION': true,
                        'USERNAME': !Join [ '', [ '{
    
    {resolve:secretsmanager:', !Ref OriginSecretid, ':SecretString:username}}' ] ],
                        'PASSWORD': !Join [ '', [ '{
    
    {resolve:secretsmanager:', !Ref OriginSecretid, ':SecretString:password}}' ] ]
                    }
                Name: !Sub ${
    
    CustomerName}-${
    
    ProjectName}-origin-${
    
    EnvironmentName}-glueconn
    #Create Target to contain tables created by the crawler
    TargetDatabase:
        Type: AWS::Glue::Database
        Properties:
            CatalogId: !Ref AWS::AccountId
            DatabaseInput:
                Name: !Sub ${
    
    CustomerName}-${
    
    ProjectName}-target-${
    
    EnvironmentName}-gluedatabase
                Description: 'AWS Glue container to hold metadata tables for the Target crawler'
    #Create Target Connection
    TargetConnectionPostgreSQL:
        Type: AWS::Glue::Connection
        Properties:
            CatalogId: !Ref AWS::AccountId
            ConnectionInput:
                Description: 'Connect to Target PostgreSQL database.'
                ConnectionType: 'JDBC'
                PhysicalConnectionRequirements:
                    AvailabilityZone: !Ref ConnAvailabilityZone
                    SecurityGroupIdList: !Ref ConnSecurityGroups
                    SubnetId: !Ref ConnSubnetId
                ConnectionProperties:
                    {
    
    
                        'JDBC_CONNECTION_URL': !Ref TargetJDBCString,
                        # If use ssl
                        'JDBC_ENFORCE_SSL': true,
                        'CUSTOM_JDBC_CERT': !Ref SSLCertificateLocation,
                        'SKIP_CUSTOM_JDBC_CERT_VALIDATION': true,
                        'USERNAME': !Join [  '', [ '{
    
    {resolve:secretsmanager:', !Ref TargetSecretid, ':SecretString:username}}' ] ],
                        'PASSWORD': !Join [ '', [ '{
    
    {resolve:secretsmanager:', !Ref TargetSecretid,  ':SecretString:password}}' ] ]
                    }
                Name: !Sub ${
    
    CustomerName}-${
    
    ProjectName}-target-${
    
    EnvironmentName}-glueconn
    #Create a crawler to crawl the Origin data in PostgreSQL database
    OriginCrawler:
        Type: AWS::Glue::Crawler
        Properties:
            Name: !Sub ${
    
    CustomerName}-${
    
    ProjectName}-origin-${
    
    EnvironmentName}-gluecrawler
            Role: !Sub arn:aws-cn:iam::${
    
    AWS::AccountId}:role/${
    
    CrawlerRoleARN}
            Description: AWS Glue crawler to crawl Origin data
            DatabaseName: !Ref OriginDatabase
            Targets:
                JdbcTargets:
                    - ConnectionName: !Ref OriginConnectionPostgreSQL
                      Path: !Ref OriginJDBCPath
            TablePrefix: !Sub ${
    
    ProjectName}_${
    
    EnvironmentName}_
            SchemaChangePolicy:
                UpdateBehavior: 'UPDATE_IN_DATABASE'
                DeleteBehavior: 'LOG'
            Tags:
                ApplName:  your-app-name
    #Create a crawler to crawl the Target data in PostgreSQL database
    TargetCrawler:
        Type: AWS::Glue::Crawler
        Properties:
            Name: !Sub ${
    
    CustomerName}-${
    
    ProjectName}-target-${
    
    EnvironmentName}-gluecrawler
            Role: !Sub arn:aws-cn:iam::${
    
    AWS::AccountId}:role/${
    
    CrawlerRoleARN}
            Description: AWS Glue crawler to crawl Target data
            DatabaseName: !Ref TargetDatabase
            Targets:
                JdbcTargets:
                    - ConnectionName: !Ref TargetConnectionPostgreSQL
                      Path: !Ref TargetJDBCPath
            TablePrefix: !Sub ${
    
    ProjectName}_${
    
    EnvironmentName}_
            SchemaChangePolicy:
                UpdateBehavior: 'UPDATE_IN_DATABASE'
                DeleteBehavior: 'LOG'
            Tags:
                ApplName: your-app-name
    #Job  sync from Origin to Target
    JobDataSync:
        Type: AWS::Glue::Job
        Properties:
            Name: !Sub ${
    
    CustomerName}-${
    
    ProjectName}-data-sync-${
    
    EnvironmentName}-gluejob
            Role: !Ref CrawlerRoleARN
            DefaultArguments: {
    
    '--job-language': 'python','--enable-continuous-cloudwatch-log': 'true','--enable-continuous-log-filter': 'true'}
            # If script written in Scala, then set DefaultArguments={'--job-language'; 'scala', '--class': 'your scala class'}
            Connections:
                Connections:
                    - !Ref OriginConnectionPostgreSQL
                    - !Ref TargetConnectionPostgreSQL
            Description: AWS Glue job for Data sync from Origin to Target
            GlueVersion: 2.0
            Command:
                Name: glueetl
                PythonVersion: 3
                ScriptLocation:
                    !Sub ${
    
    ScriptLocation}/${
    
    CustomerName}-${
    
    ProjectName}-data-sync-gluejob.py
            Timeout: 60
            WorkerType: Standard
            NumberOfWorkers: 2
            ExecutionProperty:
                MaxConcurrentRuns: 1
            Tags:
                ApplName: your-app-name
    #Trigger
    TriggerDataSync:
        Type: AWS::Glue::Trigger
        Properties:
            Name: !Sub ${
    
    CustomerName}-${
    
    ProjectName}-data-sync-${
    
    EnvironmentName}-gluetrigger
            Description: AWS Glue trigger for Data sync from Origin to Target
            Type: SCHEDULED
            Actions:
                - JobName: !Ref JobDataSync
            Schedule: cron(0 12 * * ? *)
            StartOnCreation: true
            Tags:
                ApplName: your-app-name

2. Implantação automatizada de cola (CD)

name: build-and-deploy

# Controls when the action will run. Triggers the workflow on push 
# but only for the master branch.
on:
  push:
    branches: [ master ]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains two jobs called "build" and "deploy"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v2
        
      # Set up Python
      - name: Set up Python 3.8
        uses: actions/setup-python@v2
        with:
          python-version: '3.8'
          
      # Install nbconvert to convert notebook file to python script
      - name: Install nbconvert
        run: |
          python -m pip install --upgrade pip
          pip install nbconvert

      # Convert notebook file to python
      - name: Convert notebook
        run: jupyter nbconvert --to python traffic.ipynb

      # Persist python script for use between jobs
      - name: Upload python script
        uses: actions/upload-artifact@v2
        with:
          name: traffic.py
          path: traffic.py
  
  # Upload python script to S3 and update Glue job
  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Download python script from build
        uses: actions/download-artifact@v2
        with:
          name: traffic.py
          
      # Install the AWS CLI
      - name: Install AWS CLI
        run: |
          curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
          unzip awscliv2.zip
          sudo ./aws/install
          
      # Set up credentials used by AWS CLI
      - name: Set up AWS credentials
        shell: bash
        env:
          AWS_ACCESS_KEY_ID: ${
    
    {
    
     secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${
    
    {
    
     secrets.AWS_SECRET_ACCESS_KEY }}
        run: |
          mkdir -p ~/.aws
          touch ~/.aws/credentials
          echo "[default]
          aws_access_key_id = $AWS_ACCESS_KEY_ID
          aws_secret_access_key = $AWS_SECRET_ACCESS_KEY" > ~/.aws/credentials
          
      # Copy the file to the S3 bucket
      - name: Upload to S3
        run: aws s3 cp traffic.py s3://${
    
    {
    
    secrets.S3_BUCKET}}/traffic_${
    
    GITHUB_SHA}.py --region us-east-1
      
      # Update the Glue job to use the new script
      - name: Update Glue job
        run: |
          aws glue update-job --job-name "Traffic ETL" --job-update \
            "Role=AWSGlueServiceRole-TrafficCrawler,Command={Name=glueetl,ScriptLocation=s3://${
    
    {secrets.S3_BUCKET}}/traffic_${GITHUB_SHA}.py},Connections={Connections=redshift}" \
            --region us-east-1
      
      # Remove stored credentials file
      - name: Cleanup
        run: rm -rf ~/.aws
    

3. Desenvolvimento de cola de baixo código (recomendado)

O AWS Glue Studio é uma nova interface gráfica que facilita a criação, execução e monitoramento de trabalhos de extração, transformação e carregamento (ETL) no AWS Glue. Você pode criar fluxos de trabalho de transformação de dados visualmente e executá-los sem problemas no mecanismo ETL sem servidor no estilo Apache Spark do AWS Glue. Você pode examinar a descrição da estrutura e os resultados do perfil em cada etapa da tarefa.

Amazon Glue Studio

insira a descrição da imagem aqui

4. Desenvolvimento Python

Informações básicas python:

import sys
from awsglue.transforms import *
from awsglue.utils import getResolvedOptions
from pyspark.context import SparkContext
from awsglue.context import GlueContext
from awsglue.job import Job

args = getResolvedOptions(sys.argv, ["JOB_NAME"])
sc = SparkContext()
glueContext = GlueContext(sc)
spark = glueContext.spark_session
job = Job(glueContext)
job.init(args["JOB_NAME"], args)

4.1 Importar fonte de dados

PostgreSQLtable_node1 = glueContext.create_dynamic_frame.from_catalog(
    database="[您创建的Glue连接源数据库名称]",
    table_name="[通过爬网程序生成的表名]",
    additional_options = {
    
    "jobBookmarkKeys":["[tablename表的书签字段,不能为空]"],"jobBookmarkKeysSortOrder":"[asc/desc选一个]"},
    transformation_ctx="PostgreSQLtable_node1",
)

transform_ctx é o nome do marcador, e o marcador é o marcador onde os dados são processados, assim como ler um livro, isso é muito útil na sincronização incremental.

Para que o marcador entre em vigor, o seguinte deve ser atendido:

​ 1) "Configurações avançadas" -> "Ativar favoritos" -> "Ativar" no trabalho do Glue;

2) O item Added_options está ativado para entrar em vigor.

4.2 Apresentar o mapeamento de campo

# Script generated for node ApplyMapping
ApplyMapping_node2 = ApplyMapping.apply(
    frame=PostgreSQLtable_node1,
    mappings=[
        ("id", "decimal(19,0)", "id", "decimal(19,0)"),
        ("updatetime", "timestamp", "updatetime", "timestamp"),
        ("value", "decimal(19,0)", "value", "decimal(19,0)"),
    ],
    transformation_ctx="ApplyMapping_node2",
)

O mapeamento do tipo no campo requer tentativas constantes, por exemplo, ao definir diretamente decimal com mais de 8 caracteres, haverá problemas na exportação dos dados, o que requer certa experiência e experimentação.

4.3 Inserir dados de forma incremental

# Script generated for node PostgreSQL table
 PostgreSQLtable_node3 = glueContext.write_dynamic_frame.from_catalog(
     frame=ApplyMapping_node2,
     database="[您创建的Glue目标数据库连接名称]",
     table_name="[通过爬网程序生成的表名]",
     transformation_ctx="PostgreSQLtable_node3",
 )

transform_ctx é o nome do marcador, e o marcador é o marcador onde os dados são processados, assim como ler um livro, isso é muito útil na sincronização incremental.

4.4 Inserir dados completos (com tabela vazia)

df = ApplyMapping_node2.toDF()
df.write.format("jdbc").mode('overwrite') \
  .option("url", "jdbc:postgresql://[host主机]:5432/[数据库名称]") \
  .option("user", "[账号]") \
  .option("password", "[密码]") \
  .option("dbtable", "[dbo.表名]") \
  .option("truncate", "true") \
  .save()

Se você deseja limpar a tabela e executar a operação de gravação antes de inserir os dados, execute as ações acima.

4.5 Usar parâmetros de configuração e executar SQL personalizado

import boto3
import psycopg2

data_frame = ApplyMapping_node2.toDF()
glue = boto3.client('glue')
connection = glue.get_connection(Name="[您创建的Glue目标数据库连接名称]")
pg_url = connection['Connection']['ConnectionProperties']['JDBC_CONNECTION_URL']
pg_url = pg_url.split('/')[2].split(':')[0]
pg_user = connection['Connection']['ConnectionProperties']['USERNAME']
pg_password = connection['Connection']['ConnectionProperties']['PASSWORD']
magento = data_frame.collect()

#以下代码中使用配置参数
db = psycopg2.connect(host = pg_url, user = pg_user, password = pg_password, database = "[数据库名]")
cursor = db.cursor()
for r in magento:
    insertQry=""" INSERT INTO dbo.gluetest(id, updatetime, value) VALUES(%s, %s, %s) ;"""
    cursor.execute(insertQry, (r.id, r.updatetime, r.value))
    #可以考虑分页提交
    db.commit()
cursor.close()

O uso deste método requer a introdução do pacote psycopg2 (equivalente ao pacote pré-instalado pelo docker antes da execução)

"Configuração de Segurança, Biblioteca de Scripts e Parâmetros do Trabalho (Opcional)" -> "Parâmetros do Trabalho" no Trabalho do Glue;

versão cola chave valor
2.0 –additional-python-modules psycopg2-binary==2.8.6
3.0 –additional-python-modules psycopg2-binary==2.9.0

4.6 Upsert (Inserir e atualizar)

Atualize dados de forma incremental, use updatetime como um marcador (não vazio), novos dados são inseridos e dados antigos são atualizados.

from py4j.java_gateway import java_import
sc = SparkContext()
java_import(sc._gateway.jvm,"java.sql.Connection")
java_import(sc._gateway.jvm,"java.sql.DatabaseMetaData")
java_import(sc._gateway.jvm,"java.sql.DriverManager")
java_import(sc._gateway.jvm,"java.sql.SQLException")

data_frame = PostgreSQLtable_node1.toDF()
magento = data_frame.collect()
source_jdbc_conf = glueContext.extract_jdbc_conf('[您创建的Glue目标数据库连接名称]')
page = 0
try:
    conn = sc._gateway.jvm.DriverManager.getConnection(source_jdbc_conf.get('url') + '/[数据库名]',source_jdbc_conf.get('user'),source_jdbc_conf.get('password'))
    insertQry="""INSERT INTO dbo.[表名](id, updatetime, value) VALUES(?, ?, ?) ON CONFLICT (id) DO UPDATE 
            SET updatetime = excluded.updatetime, value = excluded.value 
            WHERE dbo.gluetest.updatetime is distinct from excluded.updatetime;"""
    stmt = conn.prepareStatement(insertQry)
    conn.setAutoCommit(False)
    for r in magento:
        stmt.setBigDecimal(1, r.id)
        stmt.setTimestamp(2, r.updatetime)
        stmt.setBigDecimal(3, r.value)
        stmt.addBatch()
        page += 1
        if page % 1000 ==0:
            stmt.executeBatch()
            conn.commit()
            page = 0
    if page > 0:
        stmt.executeBatch()
        conn.commit()
finally:
    if conn:
        conn.close()
job.commit()

Pontos principais:

O acima é o método de processamento do postgreSQL, o oracle usa Marge e o sqlserver usa uma sintaxe semelhante para inserir na atualização.

O pacote Java nativo do Spark usado pode ser usado como uma alternativa ao "psycopg2" sem importar novos pacotes.

A desvantagem do "psycopg2" é que leva cerca de 1 minuto para instalar o pacote. Para operações urgentes, é recomendável usar o pacote nativo.

5. Depuração do Local Glue (auxiliar)

Desenvolva e teste scripts de tarefas do AWS Glue

Configurar o contêiner para usar o Visual Studio Code

pré-requisitos:

  1. Instale o Visual Studio Code.

  2. Instale o Python .

  3. Instalar Visual Studio Code Remote - Contêiner

  4. Abra a pasta do espaço de trabalho no Visual Studio Code.

  5. Selecione Configurações .

  6. Por favor, selecione Área de trabalho .

  7. Selecione Abrir configurações (JSON) .

  8. Cole o seguinte JSON e salve-o.

    {
          
          
        "python.defaultInterpreterPath": "/usr/bin/python3",
        "python.analysis.extraPaths": [
            "/home/glue_user/aws-glue-libs/PyGlue.zip:/home/glue_user/spark/python/lib/py4j-0.10.9-src.zip:/home/glue_user/spark/python/",
        ]
    }
    

etapa:

  1. Execute o contêiner do Docker.
docker run -it -v D:/Projects/AWS/Projects/Glue/.aws:/home/glue_user/.aws -v D:/Projects/AWS/Projects/Glue:/home/glue_user/workspace/ -e AWS_PROFILE=default -e DISABLE_SSL=true --rm -p 4040:4040 -p 18080:18080 --name glue_pyspark amazon/aws-glue-libs:glue_libs_3.0.0_image_01 pyspark
  1. Inicie o Visual Studio Code.

  2. Selecione Remote Explorer no menu à esquerda e selecione amazon/aws-glue-libs:glue_libs_3.0.0_image_01.

  3. Clique com o botão direito do mouse e selecione Anexar ao contêiner . Se uma caixa de diálogo aparecer, selecione Entendi .

  4. abra /home/glue_user/workspace/.

  5. Execute o seguinte comando primeiro no VSCode:

    export AWS_REGION=cn-northwest-x
    
  6. Crie o script Glue PySpark e escolha Executar .

    Você verá o script executado com sucesso.

Acho que você gosta

Origin blog.csdn.net/black0707/article/details/124987653
Recomendado
Clasificación