Unabhängig davon, ob man nun Apache Hadoop, eine Distribution oder eine Big Data Suite nutzt, so basiert Hadoop auf einer Vielzahl von unterschiedlichen Komponenten. Deshalb spricht man bei Hadoop auch gerne von einem Framework oder von dem Hadoop Ökosystem, also einer Reihe von frei wählbaren Bibliotheken, welche es durch ihre Kombination und Verzahnung erst erlauben, die Räder der Big-Data-Maschinerie zum Laufen zu bringen.
Dabei wird die Entwicklung von Hadoop durch eine Open-Source-Community vorangetrieben, welche aus einem gesunden Mix von Privatpersonen und Angestellten der Distributoren, Hersteller und Firmen rund um Hadoop und Big Data besteht. Die Entwicklung von Hadoop startete bereits 2002 mit den Arbeiten an der Suchmaschine Nutch durch Doug Cutting, bekam seine theoretischen Grundlagen durch die 2003 und 2004 von Google veröffentlichten Paper zu Google‘s Dateisystem und zu MapReduce und nahm dann richtig Fahrt auf, als Doug Cutting 2006 zu Yahoo wechselte, die das enorme Potential dahinter sahen und heutzutage Betreiber und Nutzer der weltweit größten Hadoop-Cluster sind. An der Entwicklung beteiligten sich auch andere Internet-Größen wie Facebook, Twitter, eBay und es wurden aus den Reihen von Yahoo heraus die beiden Distributoren Cloudera und Hortonworks als Spin-Offs gestartet, welche heute wichtige Treiber hinter der aktuellen Entwicklung sind. All dies hat dafür gesorgt, dass Hadoop heute grundsolide ist, das Ökosystem sehr umfangreich ist und immer weiter wächst und dass aktuell am nächsten Major-Release Hadoop 2.0 (oder auch YARN) gearbeitet wird, welches Ende des Jahres erwartet wird. Kurzum: Hadoop ist schon seit längerem bereit für die Primetime und treibt einige der weltweit größten Firmen und Geschäftsmodelle an.
Die wichtigsten Komponenten von Hadoop und ihre Einordnung im Ökosystem sind auf der folgenden Abbildung dargestellt.
Core Hadoop
Als den Kern von Hadoop bezeichnet man die beiden zentralen Komponenten Hadoop Distributed File System (HDFS) und MapReduce. Diese sind für die beiden Kernaufgaben zuständig – das Speichern und Verarbeiten von großen Datenmengen. Auch wenn diese beiden Komponenten eng miteinander verzahnt sind, sind es doch unabhängige Komponenten und so wäre es auch möglich, ein anderes Dateisystem außer HDFS mit MapReduce zu verwenden. In der Realität wird aber aktuell fast immer die Kombination dieser beiden zentralen Elemente verwendet (z.B. die Distribution MapR verwendet ein anderes, eigenes Dateisystem als HDFS).
Hadoop Distributed File System (HDFS)
HDFS ist ein Java-basiertes verteiltes Dateisystem, das die zuverlässige und persistente Speicherung sowie den schnellen Zugriff auf große Datenvolumina erlaubt. Dazu wird ein „Write once, read many“-Paradigma verwendet, d.h. es ist so ausgelegt, dass Daten idealerweise nur einmal nach HDFS geschrieben werden und von dort dann vielfach ausgelesen werden. Das Modifizieren von Daten nach dem Schreiben ist kaum möglich, es können an bestehende Dateien lediglich Daten angehängt werden, was in der Praxis aber ob der Ineffizienz der Operation kaum genutzt wird.
Um die Daten verteilt speichern zu können, werden sie in Blöcke (meistens 64/128 MB) aufgespalten und auf den Knoten des Hadoop-Clusters verteilt. Durch die Aufteilung auf Blöcke ist es auch logisch, dass HDFS auf die Verwendung mit großen Dateien ausgelegt ist und die Verwendung von vielen kleinen Dateien ein Antipattern ist. Für jeden dieser Blöcke werden dann in der Regel 3 Kopien auf verschiedenen Servern im Cluster angelegt, um Fehler- und Ausfallsicherheit zu garantieren. Das Management der Daten übernimmt HDFS dabei komplett automatisch, für den Benutzer gestaltet sich der Zugriff wie auf ein virtuelles Dateisystem. Auch die Befehle ähneln sehr stark den *nix-Pendants, z.B. hadoop fs -ls zum Anzeigen der Eigenschaften einer Datei oder hadoop fs -put zum Speichern einer Datei aus dem logischen Dateisystem (wie z.B. ext3/4) in HDFS. Ebenso gibt es eine Rechteverwaltung, die jedem Linux-Benutzer vertraut vorkommen wird, etwa hadoop fs -chmod 777 um Vollzugriff auf eine Datei zu erlauben.
Auf der Seite der Architektur gestaltet sich HDFS wie ein „klassisches“ Master-Slave-System mit den folgenden Bestandteilen:
- Der NameNode ist die zentrale Master-Komponente und verwaltet die Metadaten für alle gespeicherten Daten auf allen DataNodes. Dazu unterhält er einen Plan mit den aktuellen Speicherorten der Blöcke von Verzeichnissen und Dateien sowie ein Journal-File mit den aktuellen Dateioperationen. Auf Basis dieser beiden Dateien kann er Anfragen nach Dateien jederzeit beantworten, speichert aber selbst keine Daten.
- Die DataNodes sind die Slave-Komponenten der Architektur, welche lediglich für die Datenspeicherung zuständig sind. Sie sind somit die „einfach“ gestrickten Elemente und über sie wird die Skalierung des Clusters erreicht.
- Der Secondary NameNode dient nicht wie der Name suggeriert zum Backup des NameNode, sondern er ist lediglich ein Helferelement, welches in periodischen Abständen die Block Map und das Journal Log des NameNodes zusammenführt und dann wieder auf diesen überspielt. Dadurch wird der NameNode entlastet und der Cluster startet nach einem Ausfall schneller.
Wird nun eine Datei nach HDFS geschrieben, so fragt der Client beim NameNode zuerst an, ob er überhaupt berechtigt ist, die Datei zu schreiben. Erhält er das OK, schickt er Details zur Datei an den NameNode und erhält von ihm eine Liste mit DataNodes auf denen er die Datei speichern darf. Der Rest der Kommunikation findet dann direkt zwischen dem Client und den DataNodes statt und er schreibt die Datei blockweise auf die übermittelten DataNodes. Im Hintergrund sorgt dann HDFS für die automatische Replikation der Blöcke zur Ausfallsicherheit. Dabei ist der Standard-Replikationsgrad gleich 3 und der Algorithmus sieht vor, die zweite und dritte Kopie des Blocks auf zwei unterschiedlichen DataNodes in einem anderen Rack vorzunehmen.
Beim Lesen einer Datei aus HDFS findet erneut erst eine Kommunikation zwischen dem Client und dem NameNode statt, ob der Client berechtigt ist, die Datei zu lesen. Ist das der Fall erhält er vom NameNode die Liste mit den Blöcken und DataNodes und liest die Blöcke dann wieder direkt von den DataNodes. Auf diese Weise kann ebenso wie der Schreib- auch der Lesezugriff parallelisiert werden, was gerade bei großen Dateien die Geschwindigkeit des Auslesens um ein Vielfaches erhöht im Vergleich zum sequentiellen Auslesen
MapReduce
MapReduce ist ebenfalls Java-basiert und dient als Framework zur verteilten und parallelen Verarbeitung großer Mengen strukturierter und unstrukturierter Daten, welche bei Hadoop typischerweise in HDFS gespeichert sind. Es basiert auf einem Divide-and-Conquer-Ansatz und erlaubt so verteilte Berechnungen über große Rechnercluster in sehr zuverlässiger und fehlertoleranter Art und Weise.
Die Arbeitsweise und die verschiedenen Phasen lassen sich am besten am Einsteigerbeispiel des WordCount verdeutlichen – dem Hello World der MapReduce-Welt. Die Aufgabenstellung dabei ist aus einer Sammlung von Eingabetexten eine Liste mit den Häufigkeiten der vorkommenden Worte zu erstellen. Der verteilte Algorithmus zur Lösung dieses Problems geht im Überblick wie folgt vor:
Das verteilte Verfahren MapReduce basiert allgemein auf Key-Value-Paaren. Als Eingabemenge dienen für das Beispiel WordCount die Dokumente mit einem Schlüssel (wie etwa dem Dateinamen) und dem eigentlichen Text als Wert. Diese Key-Value-Paare werden dann auf einzelne Prozesse auf Cluster-Knoten verteilt und die folgenden Phasen finden verteilt und parallel statt:
- In der Map-Phase wird der Text in die einzelnen Worte zerlegt und jedes Vorkommen eines Wortes einzeln gezählt. Dazu wird für jedes Wort ein Key-Value-Paar mit dem Wort als Schlüssel und dem Wert 1 als Schlüssel emittiert.
- Die Combine-Phase ist optional und dient als eine Art lokaler Mini-Reducer. Hier werden Key-Value-Paare mit dem gleichen Schlüssel bereits lokal zu einem Key-Value-Paar zusammengefasst, wie das im Beispiel auf dem Mapper #2 passiert.
- Bisher haben wir nur lokal auf den Cluster-Knoten gezählt und es muss noch eine globale Sicht hergestellt werden. Daher findet zuerst eine Partitionierung der bisherigen Ergebnisse statt und so landen in diesem Beispiel etwa alle Key-Value-Paare mit dem Schlüssel a auf Reducer #1, alle mit b auf Reducer #2 und alle mit c auf Reducer #3.
- Dass die Key-Value-Paare auf dem richtigen Reducer landen wird in der Shuffle & Sort-Phase sichergestellt. Hier werden die Teilergebnisse über das Netzwerk auf den entsprechenden Reducer verteilt, welcher im Regelfall ein anderer Cluster-Knoten als der Mapper ist. Zudem wird dafür gesorgt, dass die Teilergebnisse pro Reducer lokal vorsortiert sind bevor die Reduce-Phase startet.
- In der Reduce-Phase sind nun alle Werte mit dem gleichen Schlüssel beim gleichen Reducer und dieser muss nun nur noch die Häufigkeit zusammenzählen. So kommen als a und b zweimal und c dreimal vor, was ebenfalls wieder als Key-Value-Paar erfasst wird. Jeder Reducer schreibt zum Schluss diese Ergebnisse in jeweils eine Datei im HDFS, so dass wir hier am Ende 3 Dateien hätten, die dann z.B. per cat zum gewünschten finalen Ergebnis zusammengefasst werden könnten.
Auf Seiten der Architektur haben wir ein Déjà-Vu, denn die Komponenten von MapReduce bilden ebenfalls ein Master-Slave-System mit vergleichbaren Elementen zu HDFS, weshalb wir uns die Komponenten direkt in der kombinierten Architektur ansehen:
- Der JobTracker verwaltet als Master-Komponente alle Jobs und Ressourcen im Hadoop-Cluster und ist damit für MapReduce das, was der NameNode für HDFS ist.
- Die TaskTracker sind die Slave-Komponenten, welche auf jedem Server im Cluster laufen und für die eigentliche Ausführung von MapReduce-Jobs inklusive der periodischen Lieferung von Statusmeldungen an den JobTracker verantwortlich sind.
- Tasks werden vom TaskTracker lokal pro Cluster-Knoten verwaltet und entsprechend im Wesentlichen einer Java Virtual Machine (JVM) in welcher als Prozess ein Mapper oder Reducer ausgeführt wird.
Wie startet man denn nun aber einen MapReduce-Job? Dazu programmiert man zuerst die Logik seines MapReduce-Jobs – typischerweise in Java. Über den Streaming-Support von Hadoop können aber auch andere Sprachen wie etwa Python, etc. verwendet werden. Diesen Quellcode paketiert man mit der job-spezifischen Konfiguration zu einer JAR-Datei und übergibt diese an den NameNode. Der Client erfragt und erhält von diesem ebenso den Speicherort der Daten – wir erinnern uns an das Prinzip der Datenlokalität. Mit diesen Informationen übermittelt der Client den Job an den JobTracker, welcher die Aufgabe an die betroffenen TaskTracker weiter delegiert. Die TaskTracker holen sich dann alle job-relevanten Informationen vom NameNode und starten die Tasks, welche dann den Programmcode ausführen und die „eigentliche“ Arbeit durchführen. Während der Ausführung schicken sie periodisch Zustandsinformationen an den JobTracker. Sollte ein Task fehlschlagen koordiniert der JobTracker die korrekte Ausführung des Jobs mit je nach der Phase unterschiedlichen Maßnahmen. Am Ende steht auf jeden Fall die garantierte Ausführung des Jobs und die Ergebnisse sind im HDFS gespeichert und können dann angesehen oder für die weitere Verarbeitung verwendet werden.
Weitere wichtige Hadoop Komponenten
Neben den beiden Kernkomponenten zum Speichern und Verarbeiten von Daten sind im Laufe der Entwicklung von Hadoop noch viele weitere Komponenten dazugekommen, welche die Flexibilität und Mächtigkeit des Hadoop Ökosystems erst ausmachen. Im Folgenden wollen wir einen kurzen Überblick über die wichtigsten dieser Projekte geben.
Apache Pig
MapReduce-Jobs in Java zu schreiben ist auf Dauer aufwändig und erfordert Programmierkenntnisse. Wäre es nicht schön, wenn es eine Abstraktionsschicht dazu gäbe? Genau das dachten sich auch die Leute bei Yahoo und das Ergebnis nennt sich Apache Pig. Dabei besteht Pig aus einer abstrakten Skriptsprache (Pig Latin), welche es erlaubt auf einer abstrakteren Ebene eher den Datenfluss eines MapReduce-Jobs zu beschreiben. Darauf basierend erzeugt der Pig-Compiler bereits optimierte MapReduce-Jobs, welche dann wieder in gewohnter Form wieder mittels MapReduce ausgeführt werden – aber eben ohne dass der Benutzer alle Details selbst implementieren muss. Damit gibt man zwar eine komplett feingranulare Kontrolle über die Jobs auf, aber in der Praxis braucht man diese sehr selten und entsprechend wird Pig sehr häufig eingesetzt.
Apache Hive
Apache Hive ist ebenfalls eine Abstraktionsschicht, welche auf dem MapReduce-Framework basiert und gerne als das als Data Warehouse System für Hadoop beschrieben wird. Hive bringt eine SQL-ähnliche Sprache (HiveQL) mit, welche es ermöglicht Aufgaben wie Aggregationen, Analysen und weitere Abfragen auf in HDFS (oder anderen Hadoop- kompatiblen File Systemen) gespeicherter Datenmengen durchzuführen. Am Ende wird aber auch HiveQL wieder in MapReduce-Jobs transformiert und so ausgeführt. Der große Vorteil ist aber auch hier die Abstraktion und die Tatsache, dass SQL-Kenntnisse weit verbreitet sind und somit auch keine reinen Techniker z.B. in Fachabteilungen von großen Firmen damit gut arbeiten können – ein wichtiger Faktor für die große Verbreitung im Hadoop-Umfeld.
Apache HCatalog
HCatalog verbindet Welten – in der Praxis vor allem Pig und Hive. Es stellt einen zentralen Tabellen- und Metadatenmanagementservice zur Beschreibung der Struktur von in Hadoop abgelegten Daten zur Verfügung. Einmal beschrieben können die Daten sowohl in Pig als auch in Hive bequemer verwendet werden und es lassen sich viel leichter ganze Ketten an MapReduce-Jobs aufbauen. So kann man etwa Pig für ETL-Prozesse nutzen, die Daten also importieren und bereinigen und sie dann mit Hive weiterverarbeiten – ein in der Praxis häufig angetroffener Prozess.
Apache Oozie
Prozessketten aufzubauen und sie automatisiert und zeitgesteuert auszuführen ist genau die Aufgabe der Workflow-Komponente Oozie. Sie füllt damit die Lücke, dass es über die Mechanismen in MapReduce nicht möglich ist, Abhängigkeiten zwischen Jobs zu erzeugen. So lässt sich mit Oozie etwa mittels einer einfachen Syntax im XML-Format angeben, dass automatisch zwei abhängige MapReduce-Jobs nach erfolgreicher Beendigung eines Vorläufers gestartet werden, dann mittels eines Synchronisationspunktes auf die erfolgreiche Durchführung beider Jobs gewartet wird, um dann den letzten Export-Job der gesammelten Ergebnisse zu starten. Sie erkennen das Potential dahinter…
Apache HBase
Wie wir gelernt haben ist Hadoop auf das einmalige Speichern und vielfache Lesen von Daten optimiert und arbeitet batch-orientiert. Während das für viele Anwendungsfälle im Bereich Big Data beste Voraussetzungen sind, gibt es aber auch genügend Anwendungsfälle, welche die Manipulation von Daten benötigen sowie sehr schreiblastig sind. Und hier kommt dann das NoSQL-Datenbanksystem HBase ins Spiel. Es basiert auf dem BigTable-Ansatz, d.h. es speichert die Daten spaltenorientiert ab. Dabei setzt HBase auf HDFS auf und bietet so fehlertolerante Speicherung von Daten kombiniert mit schnellem Zugriff auf die verteilte große Datenmengen an. Außerdem erweitert HBase das Hadoop-System durch das Transaktionsmanagement um benutzerdefinierte Updates sowie Insert- und Delete-Operationen.
Apache ZooKeeper
Die ganzen verteilten Prozesse eines Hadoop-Clusters müssen auch koordiniert werden und genau das ist die Aufgabe von ZooKeeper. Verteilte Anwendungen können ZooKeeper zur Speicherung, Verteilung und Aktualisierung wichtiger Konfigurationsinformationen verwenden. Dabei ist ZooKeeper sehr generisch gehalten und beschränkt sich nicht nur auf das Hadoop-Universum. So verwendet z.B. auch Twitter Storm ZooKeeper zum Aufbau einer Shared-Nothing-Architektur.
Apache Ambari
Eine der Herausforderungen bei Hadoop ist sicherlich die Installation, Administration und das Monitoring des Clusters, da dieser durchaus mal aus tausenden von Knoten bestehen kann. War dies lange Zeit eine Domäne der Cloudera Distribution mit ihrem kommerziellen Cloudera Manager, gibt es mittlerweile mit Apache Ambari ein quelloffenes und ausgereiftes System, welches genau diese Aufgaben mittels einer intuitiven Weboberfläche löst.
Apache Sqoop
Was nützt einem Hadoop, wenn man bestehende Daten nicht leicht integrieren kann? Und da bestehende Daten heutzutage bei den meisten Firmen in relationalen Datenbanken gespeichert sind, wurde Apache Sqoop ins Leben gerufen, welches genau für den effizienten Import und Export von großen Daten Mengen zwischen rationalen Datenbanken und Hadoop konzipiert worden ist.
Apache Flume
Neben relationalen Daten möchte man natürlich auch noch andere Daten in Hadoop verarbeiten. Und hier ist Apache Flume sehr hilfreich, welches für das verteilte und zuverlässige Sammeln, Aggregieren und Bewegen von Log-Daten konzipiert wurde. Es benötigt nicht zwingend Hadoop, bringt aber Konnektoren mit, um Daten aus verschiedenen Quellen z.B. nach HDFS zu schreiben. Dabei kommen generische Transportformate wie Thrift oder Avro zum Einsatz.
Apache Mahout
Mahout ist eine skalierbare, für Hadoop entwickelte und sehr mächtige Bibliothek für Machine Learning und Data Mining. Das Projekt beinhaltet die Implementierung diverser Machine Learning Algorithmen, unter anderem zur Klassifizierung, dem Clustering und dem Collaborative Filtering. Auf Basis von Mahout lassen sich so z.B. mächtige Recommendation Systems entwickeln, sie wissen schon, diese Subsysteme „Kunden, die das kauften, kaufen auch dies“ in Onlineshops mit denen etwa Amazon heutzutage einen beträchtlichen Teil seines Umsatzes macht.
Hadoop 2.0
Wie Sie gesehen haben, ist die Community rund um Hadoop eine sehr aktive und lebendige und natürlich stehen dort die Räder nicht still. Das betrifft zum einen die verschiedenen Projekte des Ökosystems, von denen Sie die gerade die wichtigsten kennengelernt haben und zum anderen aber auch die Kernfunktionalitäten. Denn mittlerweile ist auch die Architektur von Hadoop etwas in die Jahre gekommen und hat ihre Grenzen und Limits – kurzum es bedarf eines radikaleren Umbaus. So stößt Hadoop in der Version 1 etwa ab einer Cluster-Größe von ca. 4500 Knoten an seine Skalierbarkeit-Grenzen und der NameNode und die darüber laufende Kommunikation wird zum Bottleneck. Architektonisch gesehen ist der NameNode von HDFS auch ein Single Point of Failure, was in der Praxis nicht so schwer wiegt, da man sich hier gut, etwa mit Virtualisierungslösungen, behelfen kann. Trotzdem kennt man mittlerweile durchaus elegantere Lösungen.
Aufgrund dieser und anderer Überlegungen wird aktuell an einem ganzen Bündel von Neuerungen gearbeitet, um die nächste Generation des Hadoop Frameworks zu erstellen – zusammen bekannt als Hadoop 2.0. Während alle Neuerungen den Rahmen sprengen würden und vieles aktuell noch in Arbeit ist (mit einer Veröffentlichung wird etwa Ende 2013 gerechnet), wollen wir zumindest kurz die wichtigsten Features andeuten, damit Sie die zukünftige Entwicklung abschätzen können:
YARN (oder auch MapReduce 2)
Bisher lautet das Paradigma beim Verarbeiten von Daten in Hadoop MapReduce und dies ist aktuell auch die einzige Möglichkeit Daten zu verarbeiten. Hier möchte man gerne aber auch andere Möglichkeiten der Verarbeitung zulassen und die Lösung dafür heißt YARN (Yet Another Resource Negotiator). Hier geht es darum, die Ressourcenverwaltung von der eigentlichen Verarbeitung der Daten zu trennen, so dass MapReduce nur eines von vielen „Plugins“ für Hadoop werden wird. Dazu wird architektonisch der JobTracker in einen Ressource Manager und einen Application Master aufgespalten und über YARN können die verschiedenen Plugins dann Ressourcen im Cluster beantragen und YARN übernimmt deren automatische Verwaltung. Das erste Plugin bleibt natürlich MapReduce, welches aber auch an die neue Architektur angepasst werden muss und daher häufig als MapReduce 2 bezeichnet wird, aber die Daten weiterhin in altbekannter Weise verarbeitet. Daneben zeichnet sich aber schon in dieser frühen Phase eine breite Unterstützung von anderen Verarbeitungsparadigmen ab und so existiert z.B. schon eine YARN-Version der Near-Realtime-Bibliothek Twitter Storm und auch Apache Tez verspricht ein Near-Realtime-Framework für Big Data zu werden. Völlig neue Anwendungsfälle werden ebenso etwa die Graph-Bibliothek Apache Giraph oder Apache Drill für Adhoc-Abfragen mit niedriger Latenz ermöglichen. Sie sehen, hier ist aktuell noch viel im Fluss, aber wir sind uns sicher, dass sich basierend auf YARN viele spannende neue Möglichkeiten zur Verarbeitung von Daten ergeben werden – und damit auch völlig neue Geschäftsmodelle.
HDFS Federation
Unter HDFS Federation werden die Maßnahmen zur Verbesserung von HDFS zusammengefasst. Im Wesentlichen geht es hier darum, die angesprochenen Engpässe durch den NameNode aufzulösen und dadurch HDFS noch skalierbarer und robuster zu machen. Dazu werden Namespaces eingeführt, welche unabhängig voneinander sind und keine Koordination untereinander brauchen. Weiterhin wird ein Block Storage Service eingeführt, der aus zwei Komponenten besteht, dem Block Management auf Seiten des NameNodes und dem Storage Service auf Seiten der DataNodes. Die bisherige HDFS-Architektur erlaubte nur einen Namespace und durch die Umstellung sind nun mehrere NameNodes pro Cluster möglich, was die Skalierbarkeit und natürlich auch die Hochverfügbarkeit deutlich erhöht. So laufen bei Yahoo aktuell schon Cluster auf der neuen Version mit deutlich mehr als 10.000 Knoten und es sind noch keine Limitierungen zu erkennen.
Stinger Initiative
Die Stinger Initiative hat es sich zum Ziel gesetzt, Apache Hive um den Faktor 100 zu beschleunigen und zudem volle ANSI SQL-92-Kompabilität herzustellen. Um dieses hehre Ziel zu erreichen gibt es ein ganzes Bündel an Maßnahmen, angefangen bei der Einführung des ORC-Datenformats zum Speichern der Daten über die Integration mit YARN bis hin zu deutlich verbessertem Buffering und mehr Intelligenz bei der Erstellung der Ausführungspläne.
Weitere Beiträge
von Uwe Printz
Dein Job bei codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog-Autor*in
Uwe Printz
Standortleitung Frankfurt
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.