CI/CD voor microservicearchitecturen

Azure

Snellere releasecycli zijn een van de belangrijkste voordelen van microservicearchitecturen. Maar zonder een goed CI/CD-proces bereikt u niet de flexibiliteit die microservices beloven. In dit artikel worden de uitdagingen beschreven en worden enkele benaderingen van het probleem aanbevolen.

Wat is CI/CD?

Wanneer we het over CI/CD hebben, hebben we het in feite over verschillende gerelateerde processen: continue integratie, continue levering en continue implementatie.

  • Continue integratie. Codewijzigingen worden vaak samengevoegd met de hoofdbranch. Geautomatiseerde build- en testprocessen zorgen ervoor dat code in de hoofdvertakking altijd productiekwaliteit heeft.

  • Continue levering. Alle codewijzigingen die het CI-proces doorgeven, worden automatisch gepubliceerd naar een productie-achtige omgeving. Voor implementatie in de live productieomgeving is mogelijk handmatige goedkeuring vereist, maar anders wordt deze geautomatiseerd. Het doel is dat uw code altijd gereed is voor implementatie in productie.

  • Continue implementatie. Codewijzigingen die de vorige twee stappen doorstaan, worden automatisch geïmplementeerd in productie.

Hier volgen enkele doelstellingen van een robuust CI/CD-proces voor een microservicesarchitectuur:

  • Elk team kan de services bouwen en implementeren die onafhankelijk van elkaar zijn, zonder andere teams te beïnvloeden of te verstoren.

  • Voordat een nieuwe versie van een service naar productie wordt geïmplementeerd, wordt deze geïmplementeerd in ontwikkel-/test-/QA-omgevingen voor validatie. Kwaliteitspoorten worden in elke fase afgedwongen.

  • Een nieuwe versie van een service kan naast de vorige versie worden geïmplementeerd.

  • Er is voldoende beleid voor toegangsbeheer ingesteld.

  • Voor workloads in containers kunt u de containerinstallatiekopieën vertrouwen die voor productie worden geïmplementeerd.

Waarom een robuuste CI/CD-pijplijn belangrijk is

In een traditionele monolithische toepassing is er één build-pijplijn waarvan de uitvoer het uitvoerbare bestand van de toepassing is. Al het ontwikkelwerk wordt in deze pijplijn verwerkt. Als er een fout met hoge prioriteit wordt gevonden, moet er een oplossing worden geïntegreerd, getest en gepubliceerd, waardoor de release van nieuwe functies kan worden vertraagd. U kunt deze problemen oplossen door goedgefactoreerde modules te gebruiken en functievertakkingen te gebruiken om de impact van codewijzigingen te minimaliseren. Maar naarmate de toepassing complexer wordt en er meer functies worden toegevoegd, wordt het releaseproces voor een monoliet meestal brozer en wordt het waarschijnlijk verbroken.

Volgens de microservicesfilosofie mag er nooit een lange release-trein zijn waarbij elk team in de rij moet komen. Het team dat service A bouwt, kan op elk gewenst moment een update uitbrengen, zonder te wachten tot wijzigingen in service 'B' zijn samengevoegd, getest en geïmplementeerd.

Diagram van een CI/CD-monoliet

Als u een hoge releasesnelheid wilt bereiken, moet uw release-pijplijn geautomatiseerd en uiterst betrouwbaar zijn om risico's te minimaliseren. Als u een of meer keren per dag naar productie uitgeeft, moeten regressies of serviceonderbrekingen zeldzaam zijn. Als een ongeldige update wordt geïmplementeerd, moet u tegelijkertijd een betrouwbare manier hebben om snel terug te draaien of door te schakelen naar een eerdere versie van een service.

Uitdagingen

  • Veel kleine onafhankelijke codebases. Elk team is verantwoordelijk voor het bouwen van een eigen service, met een eigen build-pijplijn. In sommige organisaties kunnen teams afzonderlijke codeopslagplaatsen gebruiken. Afzonderlijke opslagplaatsen kunnen leiden tot een situatie waarin de kennis over het bouwen van het systeem verspreid is over teams en niemand in de organisatie weet hoe de hele toepassing moet worden geïmplementeerd. Wat gebeurt er bijvoorbeeld in een noodherstelscenario als u snel wilt implementeren in een nieuw cluster?

    Risicobeperking: Zorg voor een geïntegreerde en geautomatiseerde pijplijn voor het bouwen en implementeren van services, zodat deze kennis niet binnen elk team wordt 'verborgen'.

  • Meerdere talen en frameworks. Omdat elk team een eigen combinatie van technologieën gebruikt, kan het lastig zijn om één bouwproces te maken dat in de hele organisatie werkt. Het buildproces moet flexibel genoeg zijn zodat elk team het kan aanpassen aan de taal of het framework.

    Risicobeperking: het buildproces voor elke service in een container wijzigen. Op die manier hoeft het buildsysteem alleen de containers uit te voeren.

  • Integratie en belasting testen. Nu teams updates in hun eigen tempo uitbrengen, kan het lastig zijn om robuuste end-to-end-tests te ontwerpen, met name wanneer services afhankelijk zijn van andere services. Bovendien kan het uitvoeren van een volledig productiecluster duur zijn, dus het is onwaarschijnlijk dat elk team een eigen volledig cluster op productieschaal uitvoert, alleen voor tests.

  • Releasebeheer. Elk team moet een update voor productie kunnen implementeren. Dat betekent niet dat elk teamlid machtigingen heeft om dit te doen. Maar het hebben van een gecentraliseerde Release Manager-rol kan de snelheid van implementaties verminderen.

    Risicobeperking: hoe meer uw CI/CD-proces geautomatiseerd en betrouwbaar is, hoe minder er een centrale instantie nodig is. Dat gezegd hebbende, hebt u mogelijk verschillende beleidsregels voor het uitbrengen van belangrijke functie-updates versus kleine bugfixes. Gedecentraliseerd zijn betekent niet dat er geen governance is.

  • Service-updates. Wanneer u een service bijwerkt naar een nieuwe versie, mag deze niet worden onderbroken door andere services die ervan afhankelijk zijn.

    Risicobeperking: gebruik implementatietechnieken zoals blauw-groen of canary-release voor niet-brekende wijzigingen. Voor belangrijke API-wijzigingen implementeert u de nieuwe versie naast de vorige versie. Op die manier kunnen services die gebruikmaken van de vorige API worden bijgewerkt en getest voor de nieuwe API. Zie Services bijwerken hieronder.

Monorepo versus meerdere opslagplaatsen

Voordat u een CI/CD-werkstroom maakt, moet u weten hoe de codebasis wordt gestructureerd en beheerd.

  • Werken teams in afzonderlijke opslagplaatsen of in een monorepo (één opslagplaats)?
  • Wat is uw vertakkingsstrategie?
  • Wie kan code naar productie pushen? Is er een releasemanagerrol?

De monorepo-benadering wint aan populariteit, maar er zijn voor- en nadelen voor beide.

  Monorepo Meerdere opslagplaatsen
Voordelen Code delen
Eenvoudiger om code en hulpprogramma's te standaardiseren
Eenvoudiger om code te herstructureren
Detectie: één weergave van de code
Eigendom per team wissen
Mogelijk minder samenvoegingsconflicten
Helpt bij het afdwingen van ontkoppeling van microservices
Uitdagingen Wijzigingen in gedeelde code kunnen van invloed zijn op meerdere microservices
Groter potentieel voor samenvoegingsconflicten
Hulpprogramma's moeten worden geschaald naar een grote codebasis
Toegangsbeheer
Complexer implementatieproces
Moeilijker om code te delen
Moeilijker om coderingsstandaarden af te dwingen
Beheer van afhankelijkheden
Diffuse codebasis, slechte vindbaarheid
Gebrek aan gedeelde infrastructuur

Services bijwerken

Er zijn verschillende strategieën voor het bijwerken van een service die al in productie is. Hier bespreken we drie algemene opties: Rolling update, blauw-groene implementatie en canary release.

Doorlopende updates

In een doorlopende update implementeert u nieuwe exemplaren van een service en ontvangen de nieuwe exemplaren direct aanvragen. Naarmate de nieuwe exemplaren zich voordoen, worden de vorige exemplaren verwijderd.

Voorbeeld. In Kubernetes zijn rolling updates het standaardgedrag wanneer u de podspecificatie voor een implementatie bijwerkt. De implementatiecontroller maakt een nieuwe ReplicaSet voor de bijgewerkte pods. Vervolgens wordt de nieuwe ReplicaSet omhoog geschaald terwijl de oude wordt omlaag geschaald om het gewenste aantal replica's te behouden. Oude pods worden pas verwijderd als de nieuwe klaar zijn. Kubernetes houdt een geschiedenis van de update bij, zodat u een update indien nodig kunt terugdraaien.

Voorbeeld. Azure Service Fabric maakt standaard gebruik van de strategie voor rolling updates. Deze strategie is het meest geschikt voor het implementeren van een versie van een service met nieuwe functies zonder bestaande API's te wijzigen. Service Fabric start een upgrade-implementatie door het toepassingstype bij te werken naar een subset van de knooppunten of een updatedomein. Het wordt vervolgens doorgestuurd naar het volgende updatedomein totdat alle domeinen zijn bijgewerkt. Als een upgradedomein niet kan worden bijgewerkt, wordt het toepassingstype teruggezet naar de vorige versie in alle domeinen. Houd er rekening mee dat een toepassingstype met meerdere services (en als alle services worden bijgewerkt als onderdeel van één upgrade-implementatie) gevoelig is voor fouten. Als een service niet kan worden bijgewerkt, wordt de hele toepassing teruggezet naar de vorige versie en worden de andere services niet bijgewerkt.

Een uitdaging van rolling updates is dat tijdens het updateproces een combinatie van oude en nieuwe versies wordt uitgevoerd en verkeer ontvangt. Tijdens deze periode kan elke aanvraag worden doorgestuurd naar een van de twee versies.

Voor api-wijzigingen die fouten veroorzaken, is het raadzaam om beide versies naast elkaar te ondersteunen, totdat alle clients van de vorige versie zijn bijgewerkt. Zie API-versiebeheer.

Blauwgroene implementatie

In een blauw-groene implementatie implementeert u de nieuwe versie naast de vorige versie. Nadat u de nieuwe versie hebt gevalideerd, schakelt u al het verkeer in één keer over van de vorige versie naar de nieuwe versie. Na de overschakeling controleert u de toepassing op eventuele problemen. Als er iets misgaat, kunt u teruggaan naar de oude versie. Als er geen problemen zijn, kunt u de oude versie verwijderen.

Bij een meer traditionele monolithische of N-tier-toepassing betekende blauw-groene implementatie over het algemeen het inrichten van twee identieke omgevingen. U zou de nieuwe versie implementeren in een faseringsomgeving en vervolgens clientverkeer omleiden naar de faseringsomgeving, bijvoorbeeld door VIP-adressen te wisselen. In een microservicearchitectuur worden updates uitgevoerd op het niveau van de microservice. Daarom implementeert u de update doorgaans in dezelfde omgeving en gebruikt u een mechanisme voor servicedetectie om te wisselen.

Voorbeeld. In Kubernetes hoeft u geen afzonderlijk cluster in te richten om blauw-groene implementaties uit te voeren. In plaats daarvan kunt u gebruikmaken van selectoren. Maak een nieuwe implementatieresource met een nieuwe podspecificatie en een andere set labels. Maak deze implementatie zonder de vorige implementatie te verwijderen of de service te wijzigen die ernaar verwijst. Zodra de nieuwe pods worden uitgevoerd, kunt u de selector van de service bijwerken zodat deze overeenkomt met de nieuwe implementatie.

Een nadeel van blauwgroene implementatie is dat u tijdens de update twee keer zoveel pods uitvoert voor de service (huidige en volgende). Als de pods veel CPU- of geheugenresources vereisen, moet u het cluster mogelijk tijdelijk uitschalen om het resourceverbruik te verwerken.

Kanarietest

In een canary-release rolt u een bijgewerkte versie uit naar een klein aantal clients. Vervolgens bewaakt u het gedrag van de nieuwe service voordat u deze voor alle clients uitrolt. Hiermee kunt u een langzame implementatie op een gecontroleerde manier uitvoeren, echte gegevens observeren en problemen opsporen voordat alle klanten worden beïnvloed.

Een canary-release is complexer om te beheren dan blauw-groen of rolling update, omdat u aanvragen dynamisch naar verschillende versies van de service moet routeren.

Voorbeeld. In Kubernetes kunt u een service configureren voor twee replicasets (één voor elke versie) en het aantal replica's handmatig aanpassen. Deze benadering is echter nogal grof, vanwege de manier waarop Kubernetes-taken over pods worden verdeeld. Als u bijvoorbeeld in totaal 10 replica's hebt, kunt u verkeer slechts in stappen van 10% verplaatsen. Als u een service-mesh gebruikt, kunt u de routeringsregels voor service-mesh gebruiken om een geavanceerdere canary-releasestrategie te implementeren.

Volgende stappen