Nichts Passendes dabei? Stellen Sie ein, welche Inhalte Sie auf aleri.de sehen möchten:
Basierend auf den Vorgaben des API-Designs beschäftigt sich API-Development mit der Umsetzung bzw. Implementierung der vereinbarten Schnittstellen. Als stark technisch geprägtes Thema werden hier Entscheidungen zu den eingesetzten Techniken und den zu verwendenden Frameworks gefällt.
Die Entwicklung und die Übergabe in den Betrieb folgen dabei üblicherweise der Idee des “DevOps” (also der gemeinsamen Verantwortung für Entwicklung und Betrieb) und sind geprägt von häufigen Tests und Roll-Outs (Continuous Integration / Continuous Delivery). Aufgrund der heute üblichen Microservice-Architekturen besteht große Freiheit bei der Auswahl geeigneter Programmiersprachen (z. B. Java, ECMAScript oder Go) und Laufzeit-Umgebungen (z. B. NodeJS und Spring Boot).
Virtualisierung von Hardware und die Verwendung von Containern erlauben darüber hinaus den leichtgewichtigen Betrieb im eigenen Rechenzentrum oder bei einem Cloud- bzw. IaaS-Anbieter. Häufig kommen dabei Orchestrierungswerkzeuge (Docker Swarm oder Kubernetes) zum Einsatz.
Die tatsächliche Implementierung von APIs durch entsprechende Services unterscheidet sich nicht wesentlich von anderen Umsetzungen im Bereich IT-Architektur und Software-Entwicklung. Jedoch gibt es einige aktuelle Paradigmen und Tendenzen im Software-Development, welche den Aspekt API-Development beeinflussen.
Diese Art der “Full-Stack”-Entwicklung kann nur von heterogenen oder “cross-funktionalen” Teams durchgeführt werden, also Teams, in denen UI- und Datenbank-Designer mit Solution-Architekten, Entwicklern und Testern gemeinsam an Arbeitspaketen arbeiten. Die Arbeitspakete sollten außerdem in kurzen Zeiträumen umgesetzt werden und idealerweise aufeinander aufbauen, also inkrementellen Zuwachs an Funktionalität bewirken.
Beide Aspekte sind Indikatoren für die Anwendung agiler Methoden wie Scrum in der Software-Entwicklung:
Die Entwicklung und Bereitstellung von Microservices nach agilen Prinzipien hat allerdings nur Sinn, wenn die erzeugten Inkremente (oder Artefakte) auch in kurzer Zeit und ohne größere organisatorische Barrieren in den Betrieb überführt werden können. Dabei ist – neben einem hohen Anteil an automatisierten Tests – auch eine entsprechende IT-Umgebung notwendig, welche Continuous Delivery, also fortlaufende Installationen ermöglicht.
Idealerweise deckt die verfügbare Build-and-Deploy-Strecke dabei alle Phasen der Software-Erstellung und -Bereitstellung ab, wobei die einzelnen Stufen (Stages) ineinandergreifen:
APIs sollten sich auf Ressourcen konzentrieren und daher einen überschaubaren, fokussierten Funktionsumfang bieten. Dieser Anspruch wird von Microservices erfüllt, welche sich als autonome, oftmals geschlossene Einheiten mit Fokus auf Einfachheit, Parallelität und Skalierung darstellen.
Microservices werden in der Regel von kleinen Teams in Eigenregie entwickelt und sind nur wenigen Vorgaben bzgl. des Entwicklungsmodells und der eingesetzten Techniken unterworfen. Es gibt tatsächlich auch wenig logische Gründe für Einschränkungen, solange die entwickelten Services die API – die vereinbarte Schnittstelle – einhalten. Lediglich Restriktion bzgl. der Laufzeitumgebung können auferlegt sein, doch dafür gibt es inzwischen Lösungen.
Wo Einschränkungen bzgl. Software-Architektur, Programmiersprache und Laufzeitumgebung reduziert werden, ergibt sich zwangsläufig Raum für anwendungs-orientierte, effiziente Sprachen und Frameworks. Erwähnenswert sind hierbei sicherlich NodeJS und Go. Während es sich bei NodeJs eine mächtige Umgebung für server-seitiges JavaScript handelt, is Go eine relative neue, von Google entwickelte Programmiersprache, die auf Einfachheit, Skalierung und Parallelität setzt. Aber auch für etablierte “Enterprise”-Sprachen wie Java existieren interessante Initiativen und Frameworks. Während das interne Projekt “Jigsaw” Modularisierung in den Sprachkern integriert, setzen Spring Boot und jHipster auf Prototypen und Bootstrapping aktueller Java-Versionen in Hinblick auf Microservices.
Software-Entwicklung wird üblicherweise auf die Plattform ausgerichtet, auf welcher sie später betrieben wird. Schon länger beschreibt der Begriff “Plattform” dabei keine physische Komponente mehr, sondern eine virtuelle Maschine, welche in mehreren Instanzen auf reeller Hardware betrieben wird. Das Ziel sind eine bessere Auslastung vorhandener Hardware und eine einheitliche, durch Abstraktion erreichte Zielplattform.
Doch für Microservices stellt selbst eine virtuelle Machine (VM) einen gewaltigen Overhead dar, da hier eine vollständige Hardware-Umgebung inkl. ihrer (virtuellen) Bausteine und Schnittstellen simuliert wird. Zuviel für eine atomare Komponente wie einen Microservice, der in der Regel aus einem Prozess besteht und über ein Netzwerk angesteuert wird.
Dieses Problem adressiert Containerisierung. Während VMs Hardware virtualisieren, dienen Container der Virtualisierung von Prozessen. Sie isolieren also einen Prozess von anderen Prozessen der gleichen (physischen) Umgebung und gewähren dennoch kontrollierten Zugriff auf die physischen Schnittstellen der umgebenden Plattform. Um die daraus resultierenden Risiken unter Kontrolle zu halten, werden oftmals virtuelle Maschinen und Container-Umgebungen kombiniert.
Container stellen dabei Instanzen einer Vorlage, des sogenannten Images, dar. Ein Image seinerseits kann auf anderen Images basieren, welche in einem Mehrschichtenmodell sich ergänzen. So lässt sich die gewünschte Laufzeitumgebung für den späteren Prozess im Image granular und effizient definieren und über die Container-Instanz manifestieren. Werden nach der Erzeugung des Containers Änderungen am Image oder den Basis-Images vorgenommen, haben diese keine Auswirkungen mehr auf den Container.
Dieser Umstand und die daraus resultierende Anforderung der “Vergänglichkeit” für Container führt zu Herausforderungen bei der Speicherung von Daten (Persistenz), welche aber über Netzwerk-Speicher (NAS/SAN) und Cloud-Drives gelöst werden können. Die Prozesse, die in Containern laufen, konzentrieren sich daher auf die Verarbeitung und nutzen die Daten gemeinsam. Eine wichtige Voraussetzung für Skalierung und Redundanz.
Auch wenn wesentliche Techniken mittlerweile an die Open Container Initiative (OCI) übergeben wurden – Treiber des Themas Containerisierung ist unverändert die Firma Docker Inc. mit ihrem gleichnamigen Technologie-Stack. Rund um Docker sind weitere Frameworks entstanden, welche die Verwaltung mehrerer Container und deren Skalierung unterstützen, z. B. Google Kubernetes (k8s), RedHat OpenShift und Docker Swarm.
Container-Infrastrukturen können sowohl im eigenen Rechenzentrum (on-premise) als auch in Cloud-Zentren bei Amazon, Google oder Microsoft betrieben werden. Die großen Cloud-Anbieter haben inzwischen auch vorgefertigte Installationen im Angebot, z. B. Amazons Elastic Container Services für Kubernetes (Amazon EKS).
Die zunehmende Verbreitung und Nutzung von Containern sowie eine fortschreitende, fachliche Fokussierung von zustandslosen Microservices haben letztendlich zu Überlegungen geführt, ob der Betrieb einer (virtuellen) Hardware oder Container-Infrastruktur überhaupt noch im Bereich der Service-Implementierung stehen sollte oder ob man diese Leistung einkauft bzw. an höher qualifizierte Anbieter abgibt. Diesen Überlegungen folgend hat sich ein Markt für Function-as-a-Service- (FaaS) bzw. Lambda-Angebote entwickelt. Ein Lambda ist dabei ein Service, welcher genau eine Methode bereitstellt. Er ist gemäß des REST-Paradigmas zustandslos und kann daher mit minimalem Aufwand an beliebiger Stelle in beliebiger Breite (skaliert) installiert werden.
Function-as-a-Service bzw. Lambdas treiben also die Idee eines Microservice auf die Spitze, indem der Service auf genau eine Operation reduziert wird. Jeder dieser Operationen kann getrennt entwickelt, bereitgestellt und optimiert werden. Gleichzeitig können mit Mitteln des API-Managements nach Art einer Fassade mehrere Lambdas zu (thematischen) Gruppen zusammengeführt und orchestriert werden. Da der Lebenszyklus einer FaaS äußerst begrenzt ist und Ressourcen-Management von der Laufzeitumgebung übernommen wird, können sich Entwickler vollständig auf die fachliche Funktionalität konzentrieren. Durch die Nutzung eines umgebungs-agnostischen Frameworks wie OpenFaaS können dabei sogar die Deployment-Besonderheiten des konkreten Anbieters ignoriert werden.
Richtlinien und Hinweise zur Realisierung sicherer Services sind Ergebnisse konsequenten API-Designs. In der Disziplin API-Development gilt es diese umzusetzen bzw. auf die aktuellen Rahmenbedingungen und technischen Möglichkeiten abzubilden. Die Best Practices zur Fehlerermittlung und Schwachstellen-Analyse über technische Tools und Methoden wie Pair Programming finden im API-Development unverändert Anwendung. Ein konsequentes Monitoring der Software-Qualität über Tools wie SonarQube darf als selbstverständlich betrachtet werden.
Von besonderer Bedeutung ist dabei, dass eine Service-Implementierung in sich sicher ist. Sie darf sich nicht auf eventuell vorgeschaltete Infrastruktur-Komponenten wie eine Firewall oder ein API-Gateway verlassen. Der Grund hierfür ist der Anspruch der jederzeitigen Verschiebbarkeit eines Service. Durch Werkzeuge wie Kubernetes und Docker Swarm kann eine Service-Instanz jederzeit und sogar über Cluster-Grenzen hinweg verschoben werden. Dabei kann sie auch (unabsichtlich) außerhalb des aufgespannten “Schutzraums” betrieben werden. Mittels pro-aktiver Penetrationstests (“PenTest”) kann die Belastbarkeit der Sicherheitsaussage belegt werden.
API und Services fungieren als Schnittstellen, d.h. sie wurden geschaffen für die Kommunikation mit Consumern. Für die Sicherheit ist es unabdingbar, dass unberechtigte Aufrufe unterbunden werden oder anders formuliert, dass nur bekannte und berechtigte Kommunikationspartner zugelassen werden.
Die grundsätzliche Identifizierung der Partner kann über Zertifikate geschehen, welche von einer gemeinsamen (öffentlichen) Certificate Authority (CA) beglaubigt wurden. So kann allgemein sichergestellt werden, dass ein bekannter Partner die Gegenstelle der Kommunikation bildet. Alternativ dazu kann auch ein beiderseits bekanntes Geheimnis (Shared Secret) verwendet werden, sofern dies über ein sicheres Transportmedium ausgetauscht wird.
Weiterführend ist oftmals eine Authentifizierung des Nutzers notwendig, d.h. der Person (oder Fraktion), welche über eine Consumer-Anwendung Zugriff auf eine API nimmt. Auch hierfür kann ein Shared Secret zum Einsatz kommen, wozu im weiteren Sinne auch eine Benutzername- und Passwort-Kombination gehört. Stand der Technik (und Voraussetzung für komfortable Single Sign On-Lösungen) sind allerdings Token-basierte Verfahren wie OAuth oder OpenID Connect .
OAuth ist ein Authentifizierungsprotokoll, welches ab 2006 von einem Twitter-Mitarbeiter entwickelt und später an ein Standardisierungsgremium übergeben wurde. Das Protokoll regelt das Zusammenspiel von vier Aktoren, die miteinander in OAuth-Flows interagieren.
Der OAuth-Server stellt den Authentisierungsmechanismus durch die Abfrage der Credentials und fordert das Einverständnis des Benutzers für die Delegation (Consent Server). Der Server persistiert und verwaltet die Tokens.
Der Resource Server bietet geschützte Daten und Services für authentisierte und autorisierte Nutzer über APIs an. Im Kontext API-Management ist darauf zu achten, dass der Zugriff auf die OAuth-APIs frei von Zugangsbeschränkungen ist.
Der Resource Owner ist der Besitzer (User) der angestrebten Daten (Protected Resources). OAuth ermöglicht, falls gewünscht, die Delegation der Zugriffsrechte vom Resource Owner an den Client. Der Client (Third-Party) ist zu meist eine Anwendung wie z. B. eine Mobile App oder Webanwendung. Der Client strebt in diesem Zusammenhang die geschützten Daten des Resource Owner an.
OAuth 2.0 differenziert strikt zwischen dem Erlangen eines Tokens durch verschiedene Grant Types und die Verwendung dieser. Somit ist es möglich, dieselbe API mit verschiedenen Endgeräten und Geschäftsmodellen zu verwenden.
Der Authorization Code Flow basiert auf dem Grant Type Authorization Code und wird oft auch als “3-legged OAuth” bezeichnet, da die Identität von drei Aktoren geprüft werden.
Der stark browserbasierte Authorization Code Flow besitzt dabei im OAuth-Protokoll eine fundamentale Rolle, da alle anderen Flows nur eine Abänderung oder Minimierung des Flows darstellen. Jeder Flow ist auf bestimmt Anwendungsfälle optimiert.
Je nach Nutzungsszenarien können die OAuth-Aktoren in einem Flow verschieden miteinander interagieren. Dabei nutzen verschiedene Flows meist gleichnamige Grant Types:
OAuth lässt sich je nach Bedarf mit so genannten Profilen erweitern, diese sind vielfach schon standardisiert.
OAuth lässt sich je nach Bedarf mit so genannten Profilen erweitern, diese sind vielfach schon standardisiert. OpenID Connect kann als ein Aufsatz für OAuth2.0 betrachtet werden und ermöglicht den Clients, die Identität des Nutzers anhand der Authentifizierung durch einen Autorisierungsserver zu überprüfen. Informationen über den Resource Owner können in einer zur Weiterverarbeitung geeigneten Form überführt und ausgelesen werden. OpenID Connect eignet sich dabei für alle Arten von Clients (Cloud, Web oder Mobil) und wird allen notwendigen Anforderungen für ein Single Sign-On ‒ wie z. B. Verschlüsselung und Session Management ‒ gerecht.
Unser Technology Lead API & Services ist gerne für Sie da.