Ontwikkel Multicloud Ready Microservices met DAPR

We weten allemaal hoe snel je een cloud native webapplicatie kunt schrijven en deployen naar elke public cloud. Met een paar klikken heb je zo een leeg .NET Core project gemaakt, dependency injectie geïmplementeerd, een aantal APIs ontsloten en gedeployed naar je favoriete compute service. Maar wat als je over de Microservice route wilt gaan? Wel dan moet je eerst je Kubernetes cluster opzetten en je Service Bus of Message queue configuratie voor de service-to-service communicatie en dan heb je nog geen regel code geschreven. Heb je wel eens nagedacht over de afhankelijkheid van je software architectuur op je cloud infrastructuur? In de meeste gevallen is je container setup specifiek voor de cloud provider die je op dit moment gebruikt en hebben je microservices een tight coupling met cloud provider specifieke endpoints of met andere PaaS- diensten van deze cloud provider. Wat als je dan wilt switchen naar een andere cloud provider? Wat als je een nieuwe service nodig hebt die enkel wordt aangeboden door de concurrent? Dan heb je een software architectuur nodig welke klaar is voor multicloud!

Het multicloud ready maken van jouw architectuur kan nu vereenvoudigd worden door DAPR te gebruiken. Een middleware laag en applicatie runtime welke is ontworpen om jouw microserviceontwikkeling en deployment cloud agnostisch te maken. Zonder tight coupling tussen je applicatie code en jouw cloud infrastructuur.

Geschiedenis van DAPR

Als gezegd DAPR is een middleware laag en applicatie runtime, daarom staat de afkorting ook voor “Distributed Application Runtime”. Distributed betekent in deze dat DAPR decentraal draait en perfect past binnen een microservices architectuur. De key functionaliteit van DAPR is om ondersteuning te bieden voor cloud native en serverless cloud diensten in een microservices architectuur.

Au contraire wat de meeste mensen denken is DAPR niet ontwikkeld of onderhouden door Microsoft. Het is een volledig open-source project gelanceerd onder de MIT-licentie. Microsoft heeft echter wel redelijk wat publiciteit vergaard met DAPR en draagt ook financieel bij aan het project. Verder worden er ook bijdragen geleverd aan het project door developers van Microsoft en developers die in het verleden voor Microsoft hebben gewerkt. Deze inmenging van Microsoft heeft ertoe geleid dat er binnen het .NET ecosysteem er goede ondersteuning is voor DAPR vanaf eigenlijk begin af aan. Er is een specifieke SDK voor .NET Core, volledige support in Visual Studio en goede documentatie volgens de Microsoft standaarden. Maar DAPR is niet enkel Microsoft specifiek. De runtime zelf is geschreven in Go en is platform agnostisch.

Dit geldt voor zowel de infrastructuur laag waar DAPR mee praat als de applicatie laag die je bovenop DAPR draait. Naast .NET Core is er ook ondersteuning voor Node.js, Java, Pyhton, C++ en Go zelf. Op de infrastructuur laag is er out-of-the-box support voor natuurlijk Microsoft Azure maar ook voor AWS, Google Cloud Platform en zelfs nieuwkomers in de markt zoals Alibaba Cloud en Camunda Cloud.

DAPR Bouwstenen

DAPR acteert als een interface tussen je applicatie en je container infrastructuur. Het gebruik van containers is overigens verplicht als je DAPR wil gebruiken. De onderliggende orchestrator staat vrij. Dit kan zowel Kubernetes zijn maar ook Docker Swarm en de cloud provider die de infrastructuur levert is ook niet van belang. Het belangrijkste voordeel van DAPR uit oogpunt van de developer is dat het zorgt voor een single-contract interface om the interacteren met elke cloud infrastructuur, elke andere microservice in je cluster en zelfs interacties met externe applicaties of API’s op het internet, buiten je cluster. Je hoeft niet meer bang te zijn voor breaking changes in je infrastructuur laag. Zolang je jouw contract met de DAPR runtime respecteert is er niets aan de hand.

De DAPR runtine is modulair ontwikkeld. Dit betekent dat je zelf kunt kiezen welke services in jouw software life cycle door DAPR geregeld moeten worden en welke je zelf regelt. Op deze manier blijft DAPR lichtgewicht en heeft het geen impact op de snelheid van jouw microservices. Ook neemt DAPR zo minder resources in binnen je Kubernetes cluster.

Er zijn onder andere DAPR modules voor: State Management, Service-to-Service Invocation, Pub-Sub, Resource Bindings, Observables en Secrets. Vanwege de open-source licentie van DAPR kun je ook een van de velen modules gebruiken die ontwikkeld zijn en onderhouden worden door de community.

Sidecar Architectuur

DAPR gebruikt het sidecar pattern om zichzelf beschikbaar te maken voor jouw microservice. Dit is hetzelfde pattern dat Service Meshes zoals Istio gebruiken binnen het Java Ecosysteem. Alhoewel DAPR is geen Service Mesh omdat DAPR geen diensten of features aanbiedt op de netwerk laag, het is puur en alleen een applicatie runtime.

Wanneer je een DAPR resource injecteert in je microservice wordt er een sidecar opgespint in een aparte pod of container in jouw cluster. Deze pod wordt van verbonden met de pod welke je microservice draait op eenzelfde manier als meerdere karretjes samenwerken in een trein van een achtbaan. Afhankelijk van de hoeveelheid DAPR diensten die je gebruikt in je microservices kunnen er één of meerdere karretjes aangehaakt worden bij je service pod. De karretjes onderling communiceren over mTLS.

Black-box Interface

Het grote voordeel van DAPR is de enkele interface die je gebruikt vanuit het perspectief van je microservicecode. Je kunt met DAPR communiceren over HTTP(S) of gPRC afhankelijk van jouw voorkeur en de stack die je gebruikt. Elke DAPR service die je gebruikt wordt gedefinieerd als een component in je source code. Componenten zijn beschreven in YAML en kunnen gebruikt worden om te interacteren met elk van de DAPR modules. Voor elke module is er een lijst met ondersteunde componenten van elke cloud provider.

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: demosecrets
  namespace: default
spec:
  type: secretstores.local.file
  metadata:
    - name: secretsFile
      value: secrets.json

Code Sample 1. Voorbeeld van een secret component die een secret leest uit het local filesystem.

 Het DAPR component dat hierboven staat bevat relevante metadata over het type component en de infrastructuur achter het component. In dit geval hebben we het component demosecrets van het type secretstores dat leest uit een lokaal bestand dus een bestandsnaam en pad zijn verplicht.

```C
public static IHostBuilder CreateHostBuilder(string[] args)
{
    // Create DAPR Client
    var client = new DaprClientBuilder()
        .Build();

    return Host.CreateDefaultBuilder(args)
        .ConfigureServices((services) =>
        {
            // Add the DAPR Client to Host.
            services.AddSingleton<DaprClient>(client);
        })
        .ConfigureAppConfiguration((configBuilder) =>
        {
            // Add the secret store Configuration Provider to the configuration builder.
            configBuilder.AddDaprSecretStore("demosecrets", client);
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });
}

Code Sample 2. DAPR Client toevoegen aan je ASP.NET Core webapplicatie.

De DAPR client wordt geïnjecteerd als een singleton en daarna voegen we een SecretStore toe aan de client. De DAPR client weet alleen dat hij praat tegen de demosecrets secret store en weet niets over de fysieke locatie van de store of de specifieke implementatie. De client weet alleen dat het secrets kan ophalen van dit specifieke DARP endpoint wat in dit geval http://localhost:<daprPort>/v1.0/secrets/demosecrets/ zou zijn. Dit betekent dat je de inhoud van het DAPR component op elk moment kunt veranderen zonder het contract met de interface van je applicatie te breken. Dus stel ik wil mijn secrets verplaatsen van een lokaal bestand naar zeg Azure Key Vault, dan kan ik dat doen door enkel mijn DAPR component te wijzigen.

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: demosecrets
  namespace: default
spec:
  type: secretstores.azure.keyvault
  version: v1
  metadata:
  - name: vaultName
    value: david07-e-kv
  - name: spnClientId
    value: b49cdb50-a668-4c57-913d-21b45bf89e4

Code Sample 3. DAPR-component wijzingen naar Azure Key Vault met MSI-authenticatie.

Mijn applicatie en DAPR-client praten nog steeds tegen de demosecrets secret store. De client weet niet dat de secrets niet langer in het lokale bestandsysteem leven en verplaatst zijn naar Azure Key Vault. Mijn applicatie weet ook niet dat hij nu secrets moet ophalen over het publieke internet en zich eerst tegen Azure moet authentiseren, de DAPR runtime regelt dit allemaal voor mijn microservice. Dit betekent dat je resources kunt verplaatsen of kunt wisselen tussen publieke cloud providers zonder dat je applicatie hiervan weet en zonder dat je de configuratie van je applicatie hoeft aan te passen of code changes hoeft door te voeren in je microservice.

Componenten voor elke Cloud

DAPR heeft een heleboel off-the-shelf componenten voor alle ondersteunden cloud platformen. Vorr een beter inzicht in deze componenten zie de graph hieronder welke de verschillende componenten van elk cloud platform laat zien. Sommige componenten kunnen gebruikt worden op elk cloud platform zolang je maar het gestandaardiseerde wire protocol gebruikt voor deze componenten.

Natuurlijk kun je gebruik maken van Azure native services zoals Cosmos DB, SignalR of Event Grid maar er is ook support voor vergelijkbare diensten in AWS en GCP. Ook kun je kiezen voor diensten die beschikbaar zijn op elke cloud zoals Cassandra, Kafka of RabbitMQ. Mocht het component dat jij wilt gebruiken toch nog niet bestaan dan kun je deze zelf maken en makkelijk toevoegen aan de DAPR Components Contrib.

Cloud Agnostisch betekent Multicloud Ready

Omdat DAPR zelf cloud agnostisch is, i.e. voor elke module is er een component voor bijna alle Publieke Clouds (Azure, AWS, GCP, Alibaba Cloud, Camunda Cloud) en het ervoor zorgt dat jouw microservice niet weet tegen welke cloud hij praat is DAPR ideaal om te gebruiken in een multicloud omgeving of zelfs een must in een multicloud architectuur. Met DAPR kun je jouw infrastructuur wijzigen onafhankelijk van je applicatie code en makkelijker switchen naar een andere cloud provider of verschillende cloud providers door elkaar heen gebruiken zonder dat jouw microservice dit hoeft te weten. Verder kan je zelfs een hybride cloud oplossing maken met resources die ook nog steeds on-premises leven.

Voor puur op PaaS gebaseerde applicaties draaiend op Azure App Service of AWS Lambda was dit al tijden mogelijk met de SDKs die er beschikbaar zijn voor deze platformen binnen .NET Core. Maar voor microservices architecturen op de .NET Stack vult DAPR een gat om multicloud ook makkelijk te maken voor deze architectuur. Voor andere talen zoals Java kan DAPR ook een goede keus zijn maar binnen het Java Ecosysteem bestaan ook al andere bewezen oplossingen om service-code te scheiden van de cloud infrastructuur met Service Meshes zoals Istio maar ook met nieuwe ontwikkelingen binnen het Spring framework met Spring Cloud Skipper en ook de Spring Cloud native implementaties op Azre, AWS, GCP en Alibaba cloud zorgen ervoor dat er veel meer concurrentie is voor DAPR binnen de Java stack. Ik voorzie dus een grotere shift in interesse en ondersteuning voor DAPR op het Microsoft platform en de .NET Core community zo dat DAPR de go to middleware layer wordt om op .NET Core gebaseerde microservices te draaien op elke cloud.

Waarom Multicloud belangrijk is

Het public cloud landschap is veranderlijk en als afnemer wil je geen last hebben van vendor lock-in. Op IT-management niveau committeren aan één enkele leverancier voor cloud infrastructuur kan best stressvol zijn. Niet alleen verlies je flexibiliteit maar je moet ook een commitment op de lange termijn aangaan met een specifieke leverancier, helemaal als je software architectuur slechts rekening houdt met één enkel cloud platform. Op deze manier heb je een harde afhankelijkheid op deze leverancier, misschien ben je wel minder blij met de support die deze leverancier biedt, misschien is er wel een service die je wilt gebruiken die deze leverancier nog niet heeft of misschien verhoogd deze leverancier simpelweg haar prijzen omdat je er toch al aan vast zit.

In deze gevallen wil je de mogelijkheid hebben om (deels) te veranderen van cloud provider zonder je business continuïteit van je applicaties op het spel te zetten. Het hebben van een multicloud architectuur maakt dit mogelijk omdat je jouw applicatie ontkoppeld van de cloud infrastructuur. Dit zorgt ervoor dat je niet langer overgeleverd bent aan de grillen van die éne cloud provider waarmee je in zee bent gegaan en maakt het mogelijk om nieuwe diensten van andere leveranciers uit te proberen. Het zorgt er ook voor dat jouw CTO niet langer slapeloze nachten krijgt over dat megacontract met die éne cloud provider welke anders een single point of failure zou worden binnen je IT-strategie. Als je overweegt om te migreren richting public cloud, dan moet je ook overwegen om je software architectuur multicloud ready te maken. Doe dit voor het te laat is en je vastzit in de gouden kooi van de cloud provider waarmee je niet meer mee kunt leven, maar ook niet langer zonder kan.

Bio:

David de Hoop is Special Agent (thought leader) bij Team Rockstars IT. Als solution architect adviseert hij organisaties op het gebied van Public Cloud, DevOps transformatie en Microsoft Azure. David is een fervent Agile aanhanger en DevOps evangelist. Daarnaast haalt hij veel energie uit het geven van workshops en spreken op conferenties, het liefst fysiek op een podium al is dit de laatste twee jaar wat lastig.