Architecture du programme de haute disponibilité AWS Design - Déploiement et développement Glue (ETL)

Dépendance : cet article nécessite une compréhension des bases de la conception de l'architecture AWS

AWS Glue est un service ETL (extraction, transformation et chargement) entièrement géré qui vous permet de classer, nettoyer et enrichir facilement et à moindre coût les données, et de déplacer les données de manière fiable entre divers magasins de données et flux de données . AWS Glue se compose d'un référentiel central de métadonnées appelé Catalogue de données AWS Glue, d'un moteur ETL qui génère automatiquement du code Python ou Scala, et d'un planificateur flexible qui gère la résolution des dépendances, la surveillance des tâches et les nouvelles tentatives. AWS Glue est sans serveur, il n'y a donc aucune infrastructure à configurer ou à gérer.

AWS Glue est conçu pour fonctionner avec des données semi-structurées. Il introduit un composant appelé Dynamic Frames que vous pouvez utiliser dans vos scripts ETL. Un cadre dynamique est similaire à un Apache Spark DataFrame, qui est une abstraction de données pour organiser les données en lignes et en colonnes, sauf que chaque enregistrement est auto-descriptif, donc aucun schéma n'est requis pour commencer. Avec Dynamic Frames, vous bénéficiez d'une flexibilité architecturale et d'un ensemble de transformations avancées conçues spécifiquement pour Dynamic Frames. Vous pouvez effectuer une conversion entre Dynamic Frames et Spark DataFrames pour tirer parti des transformations AWS Glue et Spark afin d'effectuer l'analyse requise.

Vous pouvez utiliser la console AWS Glue pour découvrir des données, les transformer et les rendre disponibles pour la recherche et l'interrogation. La console appelle les services sous-jacents pour coordonner le travail nécessaire à la transformation des données. Vous pouvez également utiliser les opérations d'API AWS Glue pour interagir avec les services AWS Glue. Utilisez un environnement de développement familier pour modifier, déboguer et tester votre code ETL Python ou Scala Apache Spark.

1. Déployer la colle

Déployez de la colle à l'aide de cloudformation, y compris des bases de données, des connexions, des robots d'exploration, des tâches et des déclencheurs.

Créer un rôle IAM

stratégie supplémentaire

AmazonS3FullAccess
AmazonSNSFullAccess
AWSGlueServiceRole
AmazonRDSFullAccess
SecretsManagerReadWrite
AWSLambdaRole

relation de confiance

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

Créer de la colle

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. Collez le déploiement automatisé (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. Développement low-code Glue (recommandé)

AWS Glue Studio est une nouvelle interface graphique qui facilite la création, l'exécution et la surveillance des tâches d'extraction, de transformation et de chargement (ETL) dans AWS Glue. Vous pouvez créer visuellement des workflows de transformation de données et les exécuter en douceur sur le moteur ETL sans serveur de style Apache Spark d'AWS Glue. Vous pouvez examiner la description de la structure et les résultats du profil à chaque étape de la tâche.

Studio de colle Amazon

insérez la description de l'image ici

4. Développement Python

Informations de base sur 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 Importer la source de données

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

transformation_ctx est le nom du signet, et le signet est la marque où les données sont traitées, tout comme la lecture d'un livre ; ceci est très utile dans la synchronisation incrémentielle.

Pour que le signet prenne effet, les conditions suivantes doivent être remplies :

​ 1) "Paramètres avancés" -> "Activer les signets" -> "Activer" dans le travail de Glue ;

2) L'élément additional_options est activé pour prendre effet.

4.2 Introduire le mappage des champs

# 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",
)

Le type dans le mappage de champ nécessite des tentatives constantes. Par exemple, lors de la définition directe d'un décimal avec plus de 8 caractères, il y aura des problèmes lors de l'exportation des données, ce qui nécessite une certaine expérience et expérimentation.

4.3 Insérer des données de manière incrémentielle

# 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",
 )

transformation_ctx est le nom du signet, et le signet est la marque où les données sont traitées, tout comme la lecture d'un livre ; ceci est très utile dans la synchronisation incrémentielle.

4.4 Insérer les données en entier (avec table vide)

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()

Si vous souhaitez effacer la table et exécuter l'opération d'écriture avant d'insérer des données, veuillez effectuer les actions ci-dessus.

4.5 Utiliser les paramètres de configuration et exécuter du SQL personnalisé

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()

L'utilisation de cette méthode nécessite l'introduction du package psycopg2 (équivalent au package pré-installé par docker avant l'exécution)

"Configuration de la sécurité, bibliothèque de scripts et paramètres du travail (facultatif)" -> "Paramètres du travail" dans le travail de Glue ;

Version colle clé valeur
2.0 –modules-python-supplémentaires psycopg2-binaire==2.8.6
3.0 –modules-python-supplémentaires psycopg2-binaire==2.9.0

4.6 Upsert (Insérer et mettre à jour)

Mettez à jour les données de manière incrémentielle, utilisez updatetime comme signet (non vide), de nouvelles données sont insérées et les anciennes données sont mises à jour.

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()

Points principaux:

Ce qui précède est la méthode de traitement de postgreSQL, oracle utilise Marge et sqlserver utilise une syntaxe similaire à insert into update.

Le package Java natif spark utilisé peut être utilisé comme alternative à "psycopg2" sans importer de nouveaux packages.

L'inconvénient de "psycopg2" est qu'il faut environ 1 minute pour installer le package. Pour les opérations urgentes, il est recommandé d'utiliser le package natif.

5. Débogage Local Glue (auxiliaire)

Développer et tester des scripts de tâche AWS Glue

Configurer le conteneur pour utiliser Visual Studio Code

conditions préalables:

  1. Installez Visual Studio Code.

  2. Installez Python .

  3. Installer Visual Studio Code Remote - Conteneur

  4. Ouvrez le dossier de l'espace de travail dans Visual Studio Code.

  5. Sélectionnez Paramètres .

  6. Veuillez sélectionner Espace de travail .

  7. Veuillez sélectionner Ouvrir les paramètres (JSON) .

  8. Collez le JSON suivant et enregistrez-le.

    {
          
          
        "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/",
        ]
    }
    

marcher:

  1. Exécutez le conteneur 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. Démarrez Visual Studio Code.

  2. Sélectionnez Explorateur distant dans le menu de gauche , puis sélectionnez amazon/aws-glue-libs:glue_libs_3.0.0_image_01.

  3. Cliquez avec le bouton droit et sélectionnez Attacher au conteneur . Si une boîte de dialogue s'affiche, sélectionnez Compris .

  4. ouvert /home/glue_user/workspace/.

  5. Exécutez d'abord la commande suivante dans VSCode :

    export AWS_REGION=cn-northwest-x
    
  6. Créez le script Glue PySpark, puis choisissez Exécuter .

    Vous verrez le script s'exécuter avec succès.

Je suppose que tu aimes

Origine blog.csdn.net/black0707/article/details/124987653
conseillé
Classement