man  die anoniem blijft
SOFTWAREONTWIKKELINGDOCKERETLPIPELINEWISE
02/06/2021 • Jan Eerdekens

Simpele en flexibele op ETL gebaseerde anonimisatie instellen – deel 3

In de eerste twee afleveringen van deze serie zijn het onderzoek en het maken van een ETL-pipeline met behulp van Singer aan bod gekomen. In dit derde en laatste deel, richten we ons op hoe we de pijplijn meer cloud-native kunnen maken.

PipelineWise

De naam PipelineWise zijn we al tegengekomen tijdens ons onderzoek naar Singer taps en targets. We hebben tijdens die zoektocht de stap PipelineWise Transform Field en de target PipelineWise Target S3 CSV ontdekt, die we hebben gebruikt in onze op Singer gebaseerde pijplijnoplossing.

Op de website van PipelineWise staat het volgende:

PipelineWise is een Data Pipeline Framework dat de specificatie Singer.io gebruikt om gegevens op te nemen en repliceren van diverse bronnen naar diverse bestemmingen.

Gebouwd met ETL voor ogen: PipelineWise past in het ETL-landschap en is geen traditionele ETL-tool. Met PipelineWise wordt getracht om de gegevens van de bron te reproduceren naar de Analytics-Data-Store en om de oorspronkelijke indeling daarbij zo veel mogelijk te behouden. Er worden enkele ondergeschikte laadtijdtransformaties ondersteund, maar gecompliceerde toewijzingen en koppelingen moeten in de Analytics-Data-Store worden verricht om de betekenis te extraheren.

Met PipelineWise verandert Singer in feite van een EL-product in een ETL-product. De transformatiemogelijkheden zijn beperkt, maar meer dan toereikend voor onze use-case. Wat ook onmiddellijk de aandacht trekt in hun documentatie is de sectie Running in Docker.

docker is so hot right now

Als het ons lukt om onze op Singer gebaseerde pijplijn te repliceren in PipelineWise, kunnen we deze Docker-functionaliteit gebruiken om heel eenvoudig onze complete oplossing op een cloud-native manier uit te voeren. Het bouwen van één of meerdere Docker-containers rond onze bestaande tap-/transformatie-/target-oplossing is in dat geval niet meer nodig.

Onze Singer pipeline in PipelineWise

Docker was al geïnstalleerd op mijn systeem en dat zal tegenwoordig voor veel ontwikkelaars gelden. Mocht dit voor jou niet het geval zijn, dan kun je de installatie-instructies op hun site gebruiken om Docker te installeren. Na het installeren van Docker wordt het werken met PipelineWise net zo eenvoudig als het klonen van hun Github-opslagplaats en het bouwen van een Docker-afbeelding daarmee. Deze afbeelding bevat de benodigde taps en targets.

Om de Singer-pijplijn uit het vorige artikel te maken, hebben we dit nodig: tap-oracle, target-s3-csv en transform-field. Het eerste is in feite een PipelineWise-versie van de Singer tap-oracle. De documentatie over de configuratie is vreemd genoeg beter dan die van het origineel. Door deze tap tijdens het bouwen van de docker te specificeren als een PipelineWise-connector, wordt automatisch de vereiste Oracle Instant Client geïnstalleerd.

git clone https://github.com/transferwise/pipelinewise.git
cd ./pipelinewise
docker build --build-arg connectors=tap-oracle,target-s3-csv,transform-field -t pipelinewise:latest .
alias pipelinewise="$(pwd)/bin/pipelinewise-docker"

Dit kost enige tijd om te voltooien (circa 6 minuten op mijn systeem) en daarna kunnen we verder met het maken van de daadwerkelijke pijplijn. Je kunt de afbeelding ook een andere naam geven of een andere versie dan de laatste gebruiken. Verander in dat geval de waarden in het bestand bin/pipelinewise-docker. De alias zal zodoende jouw waarden gebruiken.

pipelinewise init --name anonimization-pipeline

Met bovenstaand commando wordt een nieuwe map gemaakt met de naam anonimization-pipeline in de pipelinewise-map die is gemaakt door de opslagplaats uit te checken. De map bevat diverse voorbeeldbestanden. Om onze Singer-pijplijn te repliceren, kunnen we ze allemaal verwijderen behalve tap_oracle.yml.sample en target_s3_csv.yml.sample. We moeten de extensie .sample verwijderen bij deze twee bestanden.

Pas het bestand tap_oracle.yml nu aan met onderstaande content:

---
 
id: "oracle"
name: "Oracle Import connector"
type: "tap-oracle"
db_conn:
  host: "localhost"
  port: 1521
  sid: "OraDoc"
  user: "etl"
  password: "admin123"
  filter_schemas: "ETL"
target: "s3"
schemas:
  - source_schema: "ETL"
    target_schema: "ETL"
    tables:
      - table_name: "TEST"
        replication_method: "FULL_TABLE"
        transformations:
         - column: "FIRST_NAME"
           type: "HASH"

Een PipelineWise-tap dient twee doeleinden tegelijkertijd. Het extractie-deel, maar ook het transformatie-gedeelte voor een tabel, het transformatie-deel van de pijplijn. Pas vervolgens het bestand target_s3_csv.yml aan met onderstaande content:

---
 
id: "s3" 
name: "S3 Target connector"
type: "target-s3-csv"
db_conn:
  aws_access_key_id: "your_own_aws_access_key_id_value"
  aws_secret_access_key: "your_own_aws_secret_access_key_value"
  s3_bucket: "anonymized-data-bucket"

Importeer ten slotte de pipeline:

developer@laptop pipelinewise % pipelinewise import --dir anonimization-pipeline
time=2021-05-18 11:55:02 logger_name=pipelinewise log_level=INFO message=Profiling mode not enabled
time=2021-05-18 11:55:03 logger_name=pipelinewise.cli.config log_level=INFO message=Searching YAML config files in /app/wrk
time=2021-05-18 11:55:03 logger_name=pipelinewise.cli.config log_level=INFO message=LOADING TARGET: target_s3_csv.yml
time=2021-05-18 11:55:03 logger_name=pipelinewise.cli.config log_level=INFO message=LOADING TAP: tap_oracle.yml
time=2021-05-18 11:55:03 logger_name=pipelinewise.cli.config log_level=INFO message=SAVING CONFIG
time=2021-05-18 11:55:03 logger_name=pipelinewise.cli.config log_level=INFO message=SAVING MAIN CONFIG JSON to /root/.pipelinewise/config.json
time=2021-05-18 11:55:03 logger_name=pipelinewise.cli.config log_level=INFO message=SAVING TARGET JSONS to /root/.pipelinewise/s3/config.json
time=2021-05-18 11:55:03 logger_name=pipelinewise.cli.config log_level=INFO message=SAVING TAP JSONS to /root/.pipelinewise/s3/oracle
time=2021-05-18 11:55:03 logger_name=pipelinewise.cli.pipelinewise log_level=INFO message=ACTIVATING TAP STREAM SELECTIONS...
[Parallel(n_jobs=-1)]: Using backend ThreadingBackend with 6 concurrent workers.
time=2021-05-18 11:55:03 logger_name=pipelinewise.cli.pipelinewise log_level=INFO message=Discovering oracle (tap-oracle) tap in s3 (target-s3-csv) target...
time=2021-05-18 11:55:04 logger_name=pipelinewise.cli.pipelinewise log_level=INFO message=Writing new properties file with changes into /root/.pipelinewise/s3/oracle/properties.json
[Parallel(n_jobs=-1)]: Done   1 tasks      | elapsed:    0.9s
[Parallel(n_jobs=-1)]: Done   1 out of   1 | elapsed:    0.9s finished
time=2021-05-18 11:55:04 logger_name=pipelinewise.cli.pipelinewise log_level=INFO message=
            -------------------------------------------------------
            IMPORTING YAML CONFIGS FINISHED
            -------------------------------------------------------
                Total targets to import        : 1
                Total taps to import           : 1
                Taps imported successfully     : 1
                Taps failed to import          : []
                Runtime                        : 0:00:01.007921
            -------------------------------------------------------

Hiermee wordt de pijplijn geïmporteerd. En ook de tap catalog-discovery wordt uitgevoerd, die we in het vorige artikel handmatig hebben afgehandeld. Als we het commando pipelinewise status uitvoeren, moet onze pijplijn correct worden geïmporteerd, geactiveerd en gereed zijn om te worden uitgevoerd.

developer@laptop pipelinewise % pipelinewise status
time=2021-05-18 11:57:40 logger_name=pipelinewise log_level=INFO message=Profiling mode not enabled
Tap ID    Tap Type    Target ID    Target Type    Enabled    Status    Last Sync            Last Sync Result
--------  ----------  -----------  -------------  ---------  --------  -------------------  ------------------
oracle    tap-oracle  s3           target-s3-csv  True       ready     2021-03-11T14:18:23  success
1 pipeline(s)

De pipeline uitvoeren

Het enige wat we nu nog moeten doen is het uitvoeren van de daadwerkelijke pijplijn. Gebruik hiervoor het commando pipelinewise run_tap:

developer@laptop pipelinewise % pipelinewise run_tap --tap oracle --target s3
time=2021-05-18 12:04:24 logger_name=pipelinewise log_level=INFO message=Profiling mode not enabled
time=2021-05-18 12:04:24 logger_name=pipelinewise.cli.pipelinewise log_level=INFO message=Running oracle tap in s3 target
time=2021-05-18 12:04:24 logger_name=pipelinewise.cli.pipelinewise log_level=INFO message=No table available that needs to be sync by fastsync
time=2021-05-18 12:04:24 logger_name=pipelinewise.cli.pipelinewise log_level=INFO message=Table(s) selected to sync by singer: ['ETL-TEST']
time=2021-05-18 12:04:24 logger_name=pipelinewise.cli.commands log_level=INFO message=Writing output into /root/.pipelinewise/s3/oracle/log/s3-oracle-20210518_120424.singer.log
time=2021-05-18 12:04:25 logger_name=pipelinewise.cli.pipelinewise log_level=INFO message=
-------------------------------------------------------
TAP RUN SUMMARY
-------------------------------------------------------
    Status  : SUCCESS
    Runtime : 0:00:01.545607
-------------------------------------------------------

Uit de output blijkt dat onze pijplijn succesvol werd uitgevoerd. Er is een nieuw CSV-bestand aan onze bucket toegevoegd:

developer@laptop pipelinewise % aws s3 ls s3://anonymized-data-bucket/ --profile your_aws_profile_name
2021-05-18 14:04:26        198 ETL-TEST-20210518T120425.csv
2021-04-13 22:38:50        186 TEST-20210413T223846.csv

Moreover, the file contains the correct information:

developer@laptop pipelinewise % aws s3 cp s3://anonymized-data-bucket/ETL-TEST-20210518T120425.csv --profile your_aws_profile_name - | cat
FIRST_NAME,LAST_NAME,PERSON_ID,PHONE
a8cfcd74832004951b4408cdb0a5dbcd8c7e52d43f7fe244bf720582e05241da,Doe,1,0499010203
5c5db120cb11bee138ff3143edcbedaead684de7a0ba140e12287d436c5dc487,Doe,2,0499040506

Externe configuratie

We hebben nu iets waarmee we de pijplijn uit het vorige artikel kunnen repliceren, maar dit keer met een op Docker gebaseerde aanpak. Het kan nu worden uitgevoerd in alle contexten die Docker-afbeeldingen accepteren. Dit varieert van een eenvoudige fysieke server of een rekenproces waarbij je Docker of Docker Compose zelf installeert en uitvoert, tot een container-as-a-service-configuratie zoals Amazon ECS tot een complete Kubernetes-cluster.

Het enige dat nog steeds ontbreekt aan ons cloud-native scenario is een externe configuratie voor onze pijplijn. We hebben dit nodig om alle waarden door te geven die verschillen tussen omgevingen, zodat we geen speciale configuratiebestanden hoeven te maken voor elke omgeving.

Dit wordt gelukkig ook ondersteund door PipelineWise. We gebruiken simpelweg de notatie env_var voor elke waarde die we willen plaatsen. Dit is een voorbeeld hiervan voor de S3-geheimen:

---
 
id: "s3" 
name: "S3 Target connector"
type: "target-s3-csv"
db_conn:
  aws_access_key_id: "{{ env_var['AWS_ACCESS_KEY_ID'] }}"
  aws_secret_access_key: "{{ env_var['AWS_SECRET_ACCESS_KEY'] }}"
  s3_bucket: "anonymized-data-bucket"

docker compose meme

Docker Compose is kant-en-klaar en werkt direct correct. Je kunt de variabelen voor de omgeving specificeren in het bestand docker-compose.yml of .env zoals te zien in Sample Project for Docker Development Environment. Om dit lokaal aan de gang te krijgen, moest ik echter 

het bestand bin/pipelinewise-docker enigszins aanpassen. Deze wijziging is nodig om omgevingsvariabelen zodanig door te geven aan het normale commando dat de docker die erin wordt uitgevoerd ze ook gebruikt:

...
 
while [[ $# -gt 0 ]]; do
  case $1 in
    --dir)
    HOST_DIR=$(cd "$(dirname "$2")"; pwd)/$(basename "$2")
    ARGS="$ARGS --dir $CONT_WORK_DIR"
    shift
    shift
    ;;
    -e)
    ENV+="$1 $2 "
    shift
    shift
    ;;
    *)
    ARGS="$ARGS $1"
    shift
    ;;
  esac
done
 
...
 
docker run \
  --rm \
  $ENV \
  -v ${HOST_CONFIG_DIR}:${CONT_CONFIG_DIR} \
  -v ${HOST_DIR}:${CONT_WORK_DIR} \
  ${IMAGE}:${VERSION} \
  ${ARGS}

De pijplijn-import wordt met bovenstaande wijzigingen vervolgens:

developer@laptop pipelinewise % pipelinewise import --dir anonimization-pipeline  -e "AWS_ACCESS_KEY_ID=your_own_aws_access_key_id_value" -e "your_own_aws_secret_access_key_value=OraDoc"

Conclusie

Uit dit artikel blijkt dat de conversie van Singer naar PipelineWise vrij eenvoudig is. Het levert een op Docker gebaseerd systeem op dat eenvoudig lokaal te gebruiken is of in de cloud met geëxternaliseerde configuratie. Planning maakt geen deel uit van PipelineWise zelf maar is wel eenvoudig te bewerkstelligen. Afhankelijk van de implementatie, kun je een simpele cron gebruiken of bijvoorbeeld zoiets als Apache Airflow.

Ik vond dit een interessante POC om te doen. Ik was aangenaam verrast over de diverse gebruiksvriendelijke tools die eenvoudig met elkaar zijn te combineren. Er waren enkele kleine probleempjes, maar niets onoverkomelijks.

De enige opmerking die ik hierbij wil plaatsen, is dat Singer en PipelineWise weliswaar perfect (blijven) werken, maar dat ze tegenwoordig voornamelijk lijken te worden gebruikt in andere (SaaS) producten. Dit kan betekenen dat de gratis en publiekelijk toegankelijke delen misschien niet langer worden doorontwikkeld, dus dat is iets om rekening mee te houden

Jan Eerdekens