Micro­softs Azure Data Fac­tory (ADF) ist ein Daten­in­te­gra­ti­ons­dienst, in dem man kom­plexe Arbeits­ab­läufe in Form von Pipe­lines erstel­len und aus­füh­ren kann. Seit Ende Juni 2018 gibt es ADF V2, in der man Pipe­lines bequem in einer gra­fi­schen Ober­flä­che im Brow­ser ent­wi­ckeln kann – ähn­lich wie mit SQL Ser­ver Inte­gra­tion Ser­vices. Dabei hat man für die ein­zel­nen Acti­vi­ties (den Bau­stei­nen der Pipe­line) viele Mög­lich­kei­ten, andere Dienste des Azure-Öko­sys­tems ein­zu­bin­den, und so Daten zu ver­ar­bei­ten und zu bewegen.

Was fehlt, ist eine ein­fa­che Mög­lich­keit ein ein­zel­nes Java-Pro­gramm aus­zu­füh­ren – wie saracus con­sul­tings Data-Ware­house-Auto­ma­ti­sie­rungs­soft­ware DWau­to­ma­tic. Zwar ist Java eine zuver­läs­sige, weit ver­brei­tete und mit vie­len Sys­te­men kom­pa­ti­ble Tech­no­lo­gie (man denke an das auf vie­len Smart­phones und Tablets ver­wen­dete Android), doch sie ist nicht direkt in Data Fac­tory integriert.

Den­noch gibt es Mög­lich­kei­ten, Java in einer ADF Pipe­line zu verwenden.

Java aus­füh­ren via Azure Databricks

Azure Dat­ab­ricks basiert auf Apa­che Spark, einer Ana­ly­tics Engine für Big Data. Als sol­che ist Spark vor allem dar­auf aus­ge­legt, ver­teilte Anwen­dun­gen in gro­ßen Clus­tern aus­zu­füh­ren. In Azure Dat­ab­ricks las­sen sich Arbeits­ab­läufe in Form von Note­books anle­gen. Außer die­sen Note­books kön­nen wir von ADF aus auch direkt ein geeig­ne­tes Java JAR oder Python Code aus­füh­ren lassen. 

Dazu ist etwas Vor­ar­beit nötig.

Einen Clus­ter erstellen

Die not­wen­dige Rechen­leis­tung kann in Azure Dat­ab­ricks über den Menü­punkt “Clus­ters” bereit­ge­stellt wer­den. Je nach Bedarf lässt sich hier sowohl die Aus­stat­tung der ein­zel­nen Clus­ter Nodes als auch deren Anzahl fest­le­gen. Wird die­ser Schritt nicht in Azure Dat­ab­ricks selbst durch­ge­führt, so kann spä­ter statt­des­sen aus ADF aus ein Job-spe­zi­fi­scher Clus­ter kon­fi­gu­riert werden.

Java verwenden in der Azure Data Factory Bild1
Abbil­dung 1 Azure Dat­ab­ricks Clus­ter Erstellung
Azure Dat­ab­ricks und Data Fac­tory verbinden

Damit der Azure-Dat­ab­ricks-Clus­ter aus einer Data Fac­tory-Pipe­line aus ver­wen­det wer­den kann, müs­sen die Dienste ver­bun­den wer­den. Hierzu dient der Punkt “Con­nec­tions” im Aut­hor-Bereich von ADF. Unter Com­pute befin­det sich unter ande­rem Azure Dat­ab­ricks. Für die Ver­bin­dung wird ein Access Token oder Azure Key Vault benö­tigt; der Token muss im Azure Dat­ab­ricks Inter­face erstellt wer­den. Wei­ter­hin kann hier aus­ge­wählt wer­den, ob ein neuer oder ein vor­her erstell­ter Clus­ter ver­wen­det wer­den soll.

Ein JAR mit Azure Dat­ab­ricks ausführen
Java verwenden in der Azure Data Factory Bild2
Abbil­dung 2 JAR in Azure Dat­ab­ricks ausführen

In der ADF Pipe­line kön­nen Sie nun die Dat­ab­ricks – Jar Acti­vity hin­zu­fü­gen und die Ver­bin­dung zu Azure Dat­ab­ricks und die Main Klasse kon­fi­gu­rie­ren. Der DBFS-Pfad zur Jar-Datei kann unter “Append Libra­ries” ange­ge­ben wer­den – dazu muss sie vor­her in Azure Dat­ab­ricks hoch­ge­la­den wor­den sein. Ein ers­ter Klick auf “Debug” führt die Pipe­line aus, auch wenn sie noch nicht published wurde …

… und dann heißt es war­ten, denn bis unsere Anwen­dung läuft kann es auf­grund des not­wen­di­gen Pro­vi­sio­nings der Spark-Umge­bung einige Minu­ten dauern.

Spark, und damit auch Azure Dat­ab­ricks, eig­net sich für Anwen­dun­gen, die stark par­al­le­li­sier­bar sind. Für ein Hel­lo­World oder auch unser rela­tiv klei­nes, effi­zi­en­tes Jar, das nicht viel Leis­tung benö­tigt, ist es ein zu gro­ßer Overhead.

Glück­li­cher­weise gibt es noch eine wei­tere Alternative.

Java aus­füh­ren in Azure Batch

Wenn man Tasks aus­füh­ren möchte, die kein Spark benö­ti­gen, ist der Dienst “Batch” eine Alter­na­tive. In Azure Batch kon­fi­gu­riert man Pools von Ser­vern, die ein­zelne Tasks aus­füh­ren kön­nen. Im Gegen­satz zu Azure Dat­ab­ricks läuft hier­bei jeweils jeder Task auf einem ein­zel­nen Node und wird nicht über den Pool ver­teilt. Der Over­head von Spark fällt weg. Die Anzahl der im Pool zur Ver­fü­gung ste­hen­den Ser­ver lässt sich frei kon­fi­gu­rie­ren und bei Bedarf ändern: Fest oder dyna­misch mit einem Skalierungsscript.

Azure Batch vorbereiten

Unter “Fea­tures” – “Pools” las­sen sich neue Pools anle­gen und vor­han­dene Pools ver­wal­ten. Die Pool-ID iden­ti­fi­ziert den Pool und wird bei der Kon­fi­gu­ra­tion der Ver­bin­dung zwi­schen ADF und Batch benö­tigt; die Eigen­schaf­ten der Nodes und deren Anzahl las­sen sich je nach Bedarf und Bud­get kon­fi­gu­rie­ren. Inter­es­sant ist hier­bei auch die Mög­lich­keit, Low-Prio­rity-Nodes aus Über­ka­pa­zi­tä­ten in Azure zu ver­wen­den – diese sind güns­ti­ger als ihre dezi­dier­ten Pendants.

Azure Batch und Data Fac­tory verbinden

Ähn­lich wie bei der Ver­wen­dung von Azure Data Bricks lässt sich eine Ver­bin­dung im Aut­hor-Bereich unter “Con­nec­tions” anle­gen. Die dazu not­wen­di­gen Daten fin­den sich im Batch-Bereich des Azure Por­tals; der Pool Name muss dem vor­her ange­ge­be­nen entsprechen.

Java verwenden in der Azure Data Factory Bild3
Abbil­dung 3 Azure Batch und Data Fac­tory verbinden

In ADF gibt es unter “Batch Ser­vice” nur einen Bau­stein, näm­lich “Cus­tom”. Wie der Name ver­mu­ten lässt ist die­ser aller­dings sehr fle­xi­bel, denn er ermög­licht es einen belie­bi­gen Befehl an den Batch-Dienst zur Aus­füh­rung zu schi­cken. Wenn Sie z.B. einen Pool mit Linux-Com­pu­tern in Batch haben, kön­nen Sie mit dem ein­zel­nen Befehl

/bin/bash -c 'java -version'

die aktu­ell instal­lierte Java-Ver­sion anzei­gen las­sen. Die Ver­wen­dung von /bin/bash ‑c ist nütz­lich, um bei Bedarf Zugriff auf Umge­bungs­va­ria­blen zu haben. 

Aus­ga­ben überprüfen

Sowohl stdout als auch stderr wer­den in eine Text­da­tei auf dem aus­füh­ren­den Node geschrie­ben. Diese Dateien fin­den Sie im Batch Por­tal über die Task­liste unter dem Punkt “Files on node”. In unse­rem Bei­spiel stel­len wir so fest, dass die instal­lierte Java-Version…

Java verwenden in der Azure Data Factory Bild4
Abbil­dung 4 Aus­gabe prüfen

… nicht vor­han­den ist. In den Stan­dar­di­mages für Batch-Com­pu­ter ist Java nicht installiert.

Ein eige­nes Image für die Pool-Com­pu­ter verwenden

Neben der Ver­wen­dung von Stan­dar­di­mages besteht die Mög­lich­keit, selbst erzeugte Images für die Pool-Com­pu­ter zu ver­wen­den. In einem sol­chen Image lässt sich Java sowie ggf. benö­tigte wei­tere Soft­ware und Dateien vor­in­stal­lie­ren. Micro­soft stellt hierzu eine Anlei­tung bereit. Die­ser Ansatz ist sehr fle­xi­bel, erfor­dert aber auch grö­ßere Vor­ar­beit und War­tung. Sie sind sel­ber ver­ant­wort­lich dafür, die­ses Image aktu­ell und sicher zu hal­ten, so dass mög­li­che Angrei­fer keine Chance haben, Ihre Abläufe zu stö­ren oder Daten zu stehlen.

Con­tai­ner verwenden

Azure Batch lässt auch die Aus­füh­rung von Con­tai­nern zu, wie sie z.B. von der belieb­ten Soft­ware Docker ver­wen­det wer­den. Dazu müs­sen Sie beim Anle­gen des Pools ein Betriebs­sys­tem­image wäh­len, das Con­tai­ner unter­stützt; der­zeit gibt es neben einer Win­dows­Ser­ver-Vari­ante noch Linux-Images, die unter dem Publisher “micro­soft-azure-batch” zur Ver­fü­gung gestellt wer­den. Ach­ten Sie dar­auf, dass unter Offer oder SKU expli­zit “Con­tai­ner” erwähnt wer­den! Es lohnt sich, in der Pool­kon­fi­gu­ra­tion die ver­wen­de­ten Con­tai­ner-Images direkt mit anzu­ge­ben, so dass diese direkt beim Star­ten der Pool-Com­pu­ter gela­den wer­den, und nicht erst wenn ein Task sie bereits benö­tigt. Für viele Ein­satz­sze­na­rien gibt es pas­sende Con­tai­ner, die man u.a. im Azure-Por­tal selbst suchen kann, so auch wel­che mit Java-Unterstützung.

Jeder Task, der in einem con­tai­ne­ri­sier­ten Pool aus­ge­führt wird, muss selbst eine Con­tai­ner­kon­fi­gu­ra­tion ent­hal­ten, in der ange­ge­ben wird, inner­halb wel­chen Con­tai­ners der Task lau­fen soll – schließ­lich kön­nen meh­rere Con­tai­ner-Images auf den Nodes vor­han­den sein. Legt man einen Task inner­halb der Batch-Ober­flä­che an, befin­det sich die­ser Punkt unter den Advan­ced Set­tings. Lei­der ist es der­zeit nicht mög­lich, dies beim Auf­ruf von Batch aus ADF zu kon­fi­gu­rie­ren. Der Con­tai­ner-Ansatz schei­det daher (noch) für die Aus­füh­rung von Java aus einer ADF Pipe­line aus!

Java in einem Stan­dar­di­mage installieren

Es ist durch­aus mög­lich, Soft­ware­instal­la­tio­nen durch einen Batch Task anzu­sto­ßen. Dies ist sinn­voll, wenn man Stan­dar­di­mages ver­wen­den möchte. Lau­fen die Maschi­nen im Pool mit CentOS, so instal­liert der Befehl

sudo yum -y install java-1.8.0-openjdk

eine Java-Umge­bung. Hier­bei eska­liert sudo die eige­nen Rechte inner­halb der Maschine, was in der Regel für Soft­ware­instal­la­tio­nen not­wen­dig ist; yum ist die Stan­dard-Paket­ver­wal­tung unter CentOS und ande­ren Linux-Dis­tri­bu­tio­nen. Unter Debian-basier­ten Linux­sys­te­men könnte man statt “yum” “apt-get” ver­wen­den. Damit mit sudo die not­wen­di­gen Rechte erlangt wer­den kön­nen, muss der Task inner­halb von Batch mit einem Admi­nis­tra­to­ru­ser aus­ge­führt werden.

Dies funk­tio­niert z.B. ein­ma­lig durch einen manu­ell im Batch-Por­tal hin­zu­ge­füg­ten Task. Wenn meh­rere Com­pu­ter im Pool exis­tie­ren, haben Sie jedoch keine Kon­trolle dar­über, auf wel­chem Com­pu­ter der Befehl aus­ge­führt wird; außer­dem sollte die Instal­la­tion sinn­vol­ler­weise auf jedem ver­füg­ba­ren Ser­ver statt­fin­den. Ein geeig­ne­tes Mit­tel, dies sicher­zu­stel­len, ist die Ein­rich­tung als Start Task. Der Start Task gehört zur Pool-Kon­fi­gu­ra­tion, und wird auf jedem neu gestar­te­ten Node aus­ge­führt. Wich­tige Kon­fi­gu­ra­ti­ons­ein­stel­lun­gen in die­sem Zusam­men­hang sind die User Iden­tity für den Task (Admin-Rechte sind für Soft­ware­instal­la­tio­nen sinn­voll), sowie “Wait for suc­cess”. Ein Set­zen die­ser Ein­stel­lung auf true bewirkt, dass dem Node erst nach erfolg­rei­cher Aus­füh­rung des Start Tasks andere Tasks zuge­wie­sen wer­den. Ansons­ten könnte ver­sucht wer­den, einen Task aus­zu­füh­ren, ohne dass die Instal­la­tion geglückt ist – in unse­rem Fall würde dies nur zu Feh­ler­mel­dun­gen führen.

Java verwenden in der Azure Data Factory Bild5
Abbil­dung 5 Feh­ler­mel­dung bei feh­len­der Java Installation

Eine sol­che Instal­la­tion, egal ob durch den Start Task oder manu­ell, über­lebt auch den Neu­start eines Ser­vers. Es ist also nicht not­wen­dig den Task danach erneut aus­zu­füh­ren. Ist die Instal­la­tion als Startup-Task ein­ge­rich­tet wor­den, so wird sie bei jedem Neu­start erneut ange­sto­ßen; dies stellt jedoch kein grö­ße­res Pro­blem dar, da yum erkennt, dass die Instal­la­tion bereits durch­ge­führt wurde.

Hin­zu­fü­gen eige­ner JARs in Batch

Ihre eige­nen Java-Pro­gramme müs­sen den Weg auf die Nodes im Batch-Pool fin­den. Eine Mög­lich­keit dazu besteht durch das Fea­ture “Appli­ca­ti­ons” im Batch-Bereich des Azure-Por­tals. Hier kön­nen Sie Ihre eige­nen Pro­gramme und zuge­hö­rige Dateien im ZIP-For­mat hoch­la­den. Hoch­ge­la­dene Pro­gramme wer­den bei der Pool­kon­fi­gu­ra­tion ange­ge­ben und wer­den dann bei jedem Start eines neuen Nodes auto­ma­tisch aus dem ver­bun­de­nen Sto­rage gela­den und ent­packt. Der Ort der ent­pack­ten Pro­gramme wird in einer Umge­bungs­va­ria­blen gespei­chert. Um eine Über­sicht der vor­han­de­nen Varia­blen zu bekom­men, kann z.B. der Befehl prin­tenv aus­ge­führt wer­den.
Ein Pro­gramm­auf­ruf für die Jar-Datei von MyApp Ver­sion 0.1 sieht dann z.B. so aus:

/bin/bash -c "java -cp \"${AZ_BATCH_APP_PACKAGE_myapp_0_1}/MyApp.jar\" MyStartClass"

Zusätz­li­che Libra­ries kön­nen wie gewohnt dem ange­ge­ben Clas­s­path hin­zu­ge­fügt wer­den, eben­falls las­sen sich der main-Methode der Start­klasse wei­tere Para­me­ter über­ge­ben, die ein­fach hin­ter dem Klas­sen­na­men auf­ge­lis­tet wer­den. Sol­len meh­rere Befehle aus­ge­führt wer­den, oder ist der Auf­ruf schlicht­weg lang und schwer les­bar, so bie­tet es sich an, diese in ein Script zu packen und nur die­ses direkt aus ADF aufzurufen.

Zugriff auf Azure SQL Datenbanken

Die Aus­füh­rung von Pipe­lines in Azure Data Fac­tory ist vor allem attrak­tiv, wenn auch die Daten in der Azure Cloud lie­gen. SQL Daten­ban­ken in Azure ver­hal­ten sich dabei ähn­lich (aber nicht gleich) wie lokal instal­lierte Instan­zen des Micro­soft SQL­Ser­ver. Um eine Java-Anwen­dung mit einer Azure SQL Daten­bank kom­mu­ni­zie­ren zu las­sen, bie­tet sich daher der JDBC-Trei­ber von Micro­soft an, mit dem auch On-Pre­mise SQL­Ser­ver ver­wen­det wer­den kön­nen. In eini­gen Fäl­len muss jedoch die Anwen­dung selbst für Azure SQL ange­passt wer­den. Dies betrifft z.B. das Wech­seln der Daten­bank mit dem USE-Befehl – statt­des­sen ist es not­wen­dig, sich direkt mit der gewünsch­ten Daten­bank zu verbinden.

Fazit

Java als Teil von Azure Data Fac­tory Pipe­lines aus­zu­füh­ren ist mög­lich, wenn die rich­ti­gen Dienste mit­ein­an­der kom­bi­niert wer­den. Einige Kom­bi­na­tio­nen funk­tio­nie­ren der­zeit noch nicht wie gewünscht. Da die Azure Cloud ste­tig wei­ter­ent­wi­ckelt wird, ist anzu­neh­men, dass in Zukunft wei­tere Mög­lich­kei­ten hin­zu­kom­men werden.