Microservices opzetten
Best practices voor het overschakelen naar een microservices-architectuur
Sten Pittet
Productmanager
Stel dat je applicatie gebouwd is op één code en vrij groot en monolithisch is. En dat werkte tot voor kort prima, maar nu niet meer. Je wilt graag dat je applicatie evolueert en veerkrachtiger, schaalbaarder en onafhankelijk inzetbaar wordt. Als je dit wilt bereiken, dan moet je de structuur van de applicatie opnieuw vormgeven op een granulair microserviceniveau.
Microservices zijn steeds populairder geworden naarmate applicaties meer verspreid en complexer worden. Het leidende principe van microservices is om een applicatie te bouwen door de componenten ervan op te splitsen in kleine services die onafhankelijk van elkaar kunnen worden geïmplementeerd en gebruikt. De splitsing van deze onderdelen in verschillende diensten wordt ook wel 'servicegrenzen' genoemd.
Servicegrenzen zijn nauw verbonden met de eisen van het bedrijf en de grenzen van de organisatiehiërarchie. Individuele services kunnen worden gekoppeld aan afzonderlijke teams, budgetten en routekaarten. Servicegrenzen kunnen bijvoorbeeld betalingsverwerkings- en gebruikersverificatieservices zijn. Microservices zijn niet hetzelfde als legacy softwareontwikkelingspraktijken waarin alle componenten zijn gebundeld.
Dit document gebruikt een denkbeeldige pizza-startup 'Pizzup' om de toepassing van microservices in een modern softwarebedrijf te illustreren.
Compass gratis uitproberen
Verbeter je ontwikkelaarservaring, catalogiseer alle services en verbeter de gezondheid van je software.
Microservices opzetten
Stap 1: Begin met een monoliet
De eerste best practice voor microservices is dat je ze waarschijnlijk niet nodig hebt. Als je geen gebruikers hebt voor je toepassing, is de kans groot dat de bedrijfsvereisten snel zullen veranderen terwijl jij je MVP (Minimum Viable Product of minimale levensvatbare product) bouwt. Dit is gewoon inherent aan softwareontwikkeling en de feedbackcyclus die plaatsvindt terwijl je de belangrijkste zakelijke mogelijkheden in kaart brengt die je systeem moet bieden. Microservices zorgen voor exponentieel meer overhead en complexer beheer. Daarom kun je nieuwe projecten om alle code en logica binnen één codebase te houden. Dit zorgt voor veel minder overhead en hierdoor kun je de grenzen van de verschillende applicatiemodules veel makkelijker verplaatsen.
Met Pizzup beginnen we bijvoorbeeld met een eenvoudig probleem dat we willen oplossen: we willen dat klanten online pizza kunnen bestellen.
gerelateerd materiaal
Microservices vs. monolithische architectuur
Oplossing bekijken
Verbeter je DevEx met Compass
Als we nadenken over het pizzabestelprobleem, brengen we de verschillende functies in kaart die onze applicatie moet hebben om aan die behoefte te voldoen. We moeten een lijst beheren met de verschillende pizza's die we kunnen maken, klanten moeten een of meer pizza's kunnen kiezen, we moeten de betaling verwerken en de bezorging plannen, enzovoort. We kunnen ervoor kiezen dat klanten een account moeten aanmaken, zodat ze sneller opnieuw zullen bestellen bij Pizzup. Nadat we met de eerste gebruikers hebben gesproken, horen we misschien dat het live volgen van de bezorger en de mobiele support ons onderscheidt van de concurrent.
Wat in het begin een simpele vraag was, veranderde al snel in een lijst met nieuwe functies.
Microservices werken goed wanneer je een goed inzicht hebt in de verschillende services die je systeem nodig heeft. Microservices zijn echter veel ingewikkelder als de kernvereisten van een applicatie niet goed zijn gedefinieerd. Het is vrij duur om service-interacties, API's en datastructuren in microservices opnieuw te definiëren, omdat er doorgaans veel meer bewegende onderdelen zijn die moeten worden gecoördineerd. Daarom raden we aan om het eenvoudig te houden totdat je voldoende feedback van gebruikers hebt verzameld om zeker te weten dat je de basisbehoeften van je klanten begrijpt en hierop kunt inspelen.
Let wel op: het bouwen van een monoliet kan snel leiden tot ingewikkelde code die niet eenvoudig in kleinere stukken kan worden opgesplitst. Het is het beste om duidelijke modules te maken, zodat je ze later uit de monoliet kunt halen. Je kunt ook beginnen door de logica los te koppelen van de webgebruikersinterface en ervoor te zorgen dat deze via een RESTful API over HTTP communiceert met je backend. Dit maakt de overstap naar microservices eenvoudiger wanneer je API-bronnen naar verschillende services verplaatst.
Stap 2: Organiseer je teams op de juiste manier
Tot nu toe lijkt het misschien of het opzetten van microservices vooral een technische aangelegenheid is. Je moet een codebase splitsen in meerdere services, de juiste patronen implementeren om netwerkproblemen snel te herstellen, omgaan met gegevensconsistentie, de servicelast controleren, etc. Je krijgt te maken met een groot aantal nieuwe concepten. Maar misschien wel het op één na belangrijkste punt is dat je de organisatie van je teams opnieuw moet vormgeven.
De wet van Conway is een feit en is terug te vinden in alle soorten teams. Als een softwareteam een backend-team, een frontendteam en een onafhankelijk operations-team heeft, dan zal het afzonderlijke frontend- en backend-monolieten leveren die over de schutting naar het operations-team worden gegooid om in productie te zetten. Dit type teamstructuur past niet goed bij microservices, omdat elke service als een apart product moet worden gezien dat onafhankelijk van de andere moet worden geleverd.
Zet in plaats daarvan liever kleinere DevOps-teams op die over alle competenties beschikken die nodig zijn om de services waarvoor ze verantwoordelijk zijn te ontwikkelen en te onderhouden. Er zitten veel voordelen aan om je teams op deze manier te organiseren. Ten eerste hebben je ontwikkelaars een beter begrip van de impact van hun code op de productie, waardoor ze betere releases kunnen leveren en je minder risico loopt dat klanten nog problemen tegenkomen in je product. Ten tweede worden implementaties voor elk team een tweede natuur omdat ze samenwerken aan verbeteringen in de code en aan de automatisering van de implementatie-pipeline.
Stap 3: Splits de monoliet op om een microservices-architectuur op te zetten
Wanneer je de grenzen van je services in kaart hebt gebracht en je hebt ontdekt hoe je je teams kunt herstructureren, kun je beginnen met het opsplitsen van je monoliet om microservices op te zetten. Hieronder volgen de belangrijkste punten om over na te denken.
Houd de communicatie tussen services eenvoudig met een RESTful API
Als je nog geen RESTful API gebruikt, dan is dit een goed moment om dat te gaan doen. Zoals Martin Fowler zegt: je wilt 'slimme eindpunten en domme pipes' hebben". Dit betekent dat het communicatieprotocol tussen je services zo eenvoudig mogelijk moet zijn en alleen verantwoordelijk moet zijn voor het verzenden van gegevens zonder deze te transformeren. Het echte werk gebeurt in de eindpunten zelf: ze ontvangen een verzoek, verwerken het en geven een reactie terug.
Microservice-architecturen streven ernaar om alles zo eenvoudig mogelijk te houden en de strakke koppeling van componenten te voorkomen. In sommige gevallen gebruik je dan een gebeurtenisgestuurde architectuur met asynchrone berichtengebaseerde communicatie. Maar nogmaals, kijk vooral naar basisberichtenwachtrijdiensten zoals RabbitMQ en maak de berichten die via het netwerk worden verzonden niet complexer dan strikt noodzakelijk.
Verdeel gegevens in begrensde contexten of datadomeinen
Monoliete-applicaties gebruiken één database voor alle zakelijke functies van de applicatie. Wanneer een monoliet wordt opgesplitst in microservices, dan is deze unieke database misschien niet zo nuttig meer. Een centrale database kan juist een knelpunt worden voor het schalen van verkeer. Als een bepaalde zware service gebruikmaakt van de database, kan de toegang tot de database van andere services worden beperkt. Bovendien kan één database een knelpunt vormen wanneer meerdere teams samenwerken en tegelijkertijd proberen het schema aan te passen. De database moet daarom wordt opgesplitst of er moeten extra tools voor gegevensopslag worden toegevoegd om te voldoen aan de gegevensbehoeften van microservice.
Het opnieuw inrichten van een monolithisch databaseschema kan een lastige klus zijn. Het is belangrijk om duidelijk te bepalen welke datasets elke service nodig heeft en waar dit eventueel overlapt. Deze schemaplanning kan worden uitgevoerd door begrensde contexten te gebruiken, die een patroon zijn van Domain Driven Design. Een begrensde context vormt een op zichzelf staand systeem, en omvat wat er binnenkomt in en vertrekt uit dat systeem.
Wanneer een gebruiker een bestelling doet, kun jij in het systeem de klantinformatie bekijken in de tabel. Deze tabel kan vervolgens ook worden gebruikt om de factuur in te vullen die wordt beheerd door het factureringssysteem. Dit lijkt misschien logisch en eenvoudig, maar met microservices moeten de services van elkaar worden losgekoppeld zodat facturen altijd toegankelijk zijn, zelfs als het bestelsysteem niet beschikbaar is. Ook kun je hierdoor de factuurtabel onafhankelijk van andere functies optimaliseren of ontwikkelen. Elke service kan uiteindelijk een eigen datastore hebben om gebruik te maken van de benodigde gegevens.
Dit brengt nieuwe problemen met zich mee, omdat sommige gegevens in verschillende databases worden gedupliceerd. Begrensde contexten kunnen de beste strategie bepalen om gedeelde of dubbele gegevens te verwerken. Je kunt gebruikmaken van een gebeurtenisgestuurde architectuur om gegevens in meerdere services te synchroniseren. Je services voor facturering en het volgen van de bezorger kunnen bijvoorbeeld luisteren naar gebeurtenissen die door de accountservice worden verstuurd wanneer klanten hun persoonlijke gegevens bijwerken. Na het ontvangen van de gebeurtenis zullen deze diensten hun datastore dienovereenkomstig bijwerken. Met deze gebeurtenisgestuurde architectuur blijft de logica van de accountservice eenvoudig, omdat deze geen inzicht hoeft te hebben in de andere afhankelijke services. Het systeem wordt alleen verteld wat er is gedaan en de andere services luisteren en handelen ernaar.
Je kunt er ook voor kiezen om alle klantgegevens in de accountservice te bewaren en alleen een externe codereferentie bij te houden in je facturerings- en bezorgservice. Deze services werken vervolgens samen met de accountservice om relevante klantgegevens op te halen in plaats van bestaande gegevens te dupliceren. Omdat er geen universele oplossing is voor deze problemen, moet je elk specifiek geval zelf bekijken om de beste aanpak te bepalen.
Stel je microservices in op storingen
We hebben gezien hoe microservices grote voordelen kunnen bieden ten opzichte van een monolithische architectuur. Ze zijn kleiner en meer gespecialiseerd, waardoor ze gemakkelijk te begrijpen zijn. Ze zijn ontkoppeld, wat betekent dat je een service kunt refactoreren zonder bang te hoeven zijn dat de andere componenten van het systeem defect raken of de ontwikkeling van de andere teams wordt vertraagd. Ze bieden ook meer flexibiliteit aan je ontwikkelaars, omdat deze indien nodig verschillende technologieën kunnen kiezen zonder dat ze worden beperkt door de andere services.
Kortom, een microservicearchitectuur maakt het ontwikkelen en onderhouden van elke bedrijfscapaciteit eenvoudiger. Maar het wordt ingewikkelder als je naar alle diensten samen kijkt en bekijkt hoe ze moeten communiceren om acties te voltooien. Je systeem is nu verdeeld en bevat meerdere storingspunten. Daar moet je rekening mee houden. Je moet niet alleen voorbereid zijn op gevallen waarin een service niet reageert, maar je moet ook kunnen omgaan met trage netwerkreacties. Herstellen na een storing kan soms ook lastig zijn, omdat je ervoor moet zorgen dat services die weer online gaan niet direct worden overspoeld door berichten die staan te wachten.
Wanneer je begint met het opsplitsen van functies uit je monolithische systemen, moet je ervoor zorgen dat je ontwerpen vanaf het begin zijn voorbereid op storingen.
Benadruk monitoring om testen van microservices te vergemakkelijken
Testen is een ander nadeel van microservices ten opzichte van een monolithisch systeem. Een applicatie die is gebouwd als een enkele codebase heeft niet veel nodig voordat hij getest kan worden. Vaak hoef je alleen maar een back-endserver te starten die is gekoppeld aan een database om je testsuite te kunnen uitvoeren.
Bij microservices is het niet zo eenvoudig. Als het gaat om eenheidstests, dan is het nog steeds behoorlijk vergelijkbaar zijn met de monoliet. Op dat niveau is het nog niet zo ingewikkeld. Als het echter gaat om integratie- en systeemtesten, wordt het lastiger. Je moet verschillende services samen starten, verschillende datastores online zetten, en je installatie moet berichtwachtrijen bevatten die bij de monoliet niet nodig waren. In dat geval wordt het veel duurder om functionele tests uit te voeren en het aantal bewegende onderdelen maakt het erg moeilijk om de verschillende soorten storingen te voorspellen waar je mee te maken kunt krijgen.
Met monitoring kun je problemen op tijd identificeren en dienovereenkomstig reageren. Je moet weten wat de baseline van verschillende services is en niet alleen reageren wanneer ze offline gaan, maar ook wanneer ze niet werken zoals verwacht. Een voordeel van een microservicearchitectuur is dat je systeem beter bestand is tegen gedeeltelijke storingen. Als je dus iets onverwachts tegenkomt in de service voor het volgen van de bezorger in onze Pizzup-toepassing, dan is het niet zo'n groot probleem als bij een monolithisch systeem. Onze applicatie is zo ontworpen dat alle andere services goed reageren en onze klanten pizza's kunnen bestellen terwijl wij het live volgen herstellen.
Omarm continue levering om implementatieproblemen te verminderen
Het handmatig releasen van een monolithisch systeem naar de productie is een vervelende en risicovolle klus, maar het kan wel. Natuurlijk raden we deze aanpak niet aan; we moedigen elk softwareteam aan om continue levering voor alle soorten ontwikkeling te omarmen, maar aan het begin van een project kun je de eerste implementaties zelf uitvoeren via de opdrachtregel.
Deze aanpak houdt geen stand wanneer je steeds meer services hebt die meerdere keren per dag moeten worden geïmplementeerd. Als onderdeel van je overstap naar microservices is het dus van cruciaal belang dat je continue levering omarmt om het risico op releasefouten te verminderen en ervoor te zorgen dat je team gefocust blijft op het bouwen en uitvoeren van de applicatie, in plaats van op bij het implementeren ervan. Continue levering betekent ook dat je service acceptatietests heeft doorstaan voordat hij wordt doorgezet naar productie. Natuurlijk zul je te maken krijgen met bugs, maar na verloop van tijd heb je een robuuste testsuite die ervoor zorgt dat je team steeds meer vertrouwen krijgt in de kwaliteit van de releases.
Microservices uitvoeren is geen sprint
Microservices zijn een populaire en algemeen aanvaarde best practice in de sector. Bij complexe projecten bieden ze meer flexibiliteit voor het bouwen en implementeren van software. Ze helpen ook de bedrijfscomponenten van je systeem in kaart te brengen en te formaliseren, wat handig is wanneer er meerdere teams aan dezelfde toepassing werken. Maar er zitten ook een paar duidelijke nadelen aan het beheer van gedistribueerde systemen. Je kunt een monolithische architectuur alleen opsplitsen als je de servicegrenzen goed begrijpt.
Het opzetten van microservices moet worden gezien als een reis in plaats van het onmiddellijke doel voor een team. Begin klein om te begrijpen wat de technische vereisten van een gedistribueerd systeem zijn, hoe je omgaat met storingen en hoe je afzonderlijke componenten op kunt schalen. Vervolgens kun je geleidelijk meer functies loskoppelen naarmate je ervaring en kennis opdoet.
De migratie naar een microservice-architectuur hoeft niet in één holistische activiteit te worden uitgevoerd. Het is veiliger om kleinere componenten stuk voor stuk te migreren naar microservices op een iteratieve manier. Breng de meest goed gedefinieerde servicegrenzen binnen een gevestigde monoliet-toepassing in kaart en werk iteratief om ze los te koppelen en in te stellen als een aparte microservice.
Conclusie...
Kortom: microservices is een strategie die nuttig is voor zowel het ontwikkelingsproces van ruwe technische code als de algemene bedrijfsorganisatiestrategie. Met microservices kunnen teams zich organiseren in eenheden die zich richten op het ontwikkelen en beheren van specifieke bedrijfsfuncties. Deze granulaire focus zorgt voor een verbeterde algemene bedrijfscommunicatie en efficiëntie. Er zitten voordelen, maar ook nadelen aan de microservices. Het is belangrijk dat servicegrenzen duidelijk worden gedefinieerd voordat je migreert naar een microservice-architectuur.
Hoewel een microservice-architectuur veel voordelen heeft, maakt het de zaken ook ingewikkelder. Atlassian heeft Compass ontwikkeld om bedrijven te helpen de complexiteit van gedistribueerde architecturen te beheren terwijl ze schalen. Compass is een uitbreidbaar ontwikkelaarsplatform dat verspreide informatie over engineeringoutput en teamsamenwerking samenbrengt op één centrale, doorzoekbare locatie.
Deel dit artikel
Volgend onderwerp
Aanbevolen artikelen
Bookmark deze resources voor meer informatie over soorten DevOps-teams of voor voortdurende updates over DevOps bij Atlassian.