CI/CD voor microservices

Snellere releasecycli zijn een groot voordeel van microservicesarchitecturen. Zonder een betrouwbaar CI/CD-proces (continue integratie en continue levering) verliest u de flexibiliteit die microservices bieden. In dit artikel vindt u een overzicht van veelvoorkomende CI/CD-uitdagingen in microservicearchitecturen en worden benaderingen aanbevolen voor het onafhankelijk bouwen, valideren, beveiligen en implementeren van services.

Wat is CI/CD?

CI/CD verwijst naar verschillende gerelateerde processen: continue integratie, continue levering en continue implementatie.

  • Continue integratie (CI): Codewijzigingen worden vaak samengevoegd in de hoofdbranch. Geautomatiseerde build- en testprocessen zorgen ervoor dat code in de hoofdbranch altijd productiekwaliteit is.

  • Continue levering (CD): Codewijzigingen die het CI-proces succesvol doorlopen, worden automatisch uitgebracht in een productie-achtige omgeving. Implementatie in de live productieomgeving vereist mogelijk handmatige goedkeuring, maar wordt anders geautomatiseerd. Het doel is dat uw code altijd klaar is om te implementeren in productie.

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

Houd rekening met de volgende doelstellingen van een robuust CI/CD-proces voor een microservicesarchitectuur:

  • Elk team kan de services die het bezit zelfstandig bouwen en implementeren, zonder invloed uit te oefenen op andere teams of hun werkzaamheden te verstoren.

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

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

  • Er zijn voldoende beleidsregels voor toegangsbeheer aanwezig. Pijplijnen authenticeren zich bij Azure met gefedereerde, kortstondige inloggegevens in plaats van langlevende geheimen.

  • Voor workloads in containers kunt u vertrouwen op de containerimages die zijn uitgerold in de productieomgeving. Dat vertrouwen wordt tot stand gebracht via ondertekende images, SBOM-attestaties (software bill of materials) en kwetsbaarheidsscans die binnen de pijplijn worden afgedwongen.

Waarom een robuuste CI/CD-pijplijn belangrijk is

In een traditionele monolithische toepassing genereert één buildpipeline het uitvoerbare bestand van de toepassing. Alle ontwikkelingswerkzaamheden worden in deze pijplijn gevoerd. Als het team een fout met hoge prioriteit vindt, moet de oplossing worden geïntegreerd, getest en gepubliceerd, waardoor de release van nieuwe functies kan worden vertraagd. U kunt deze problemen verminderen door goed gefactoreerde modules en functievertakkingen te gebruiken om het effect van codewijzigingen te beperken. Maar naarmate de toepassing complexer wordt en er meer functies worden toegevoegd, wordt het releaseproces voor een monolith meestal ingewikkelder en mislukt het waarschijnlijk.

Volgens de microservices-filosofie zou er nooit een lange releasetrein moeten zijn waar elk team in de lijn moet komen. Het team dat service A bouwt, kan een update vrijgeven wanneer deze kiest en hoeft niet te wachten op wijzigingen in service B om samen te voegen, te testen en te implementeren.

Diagram dat CI/CD vergelijkt voor monolith versus microservicesarchitecturen.

Om een hoge releasesnelheid te bereiken, moet uw release-pijplijn geautomatiseerd en zeer betrouwbaar zijn om het risico te minimaliseren. Als u een of meer keren per dag in productie gaat, moeten regressies of serviceonderbrekingen zeldzaam zijn. Tegelijkertijd moet u, als u een slechte update implementeert, een betrouwbare manier hebben om snel terug te draaien of terug te draaien naar een eerdere versie van een service.

Challenges

  • Veel kleine onafhankelijke codebasissen: 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 de kennis over het bouwen van het systeem in teams verspreiden. Hierdoor weet niemand in de organisatie hoe de hele toepassing moet worden geïmplementeerd.

    Mitigatie: Zorg voor een uniforme en geautomatiseerde pijplijn of ten minste een gemeenschappelijke pijplijninfrastructuur voor het bouwen en implementeren van services, zodat deze kennis niet binnen elk team wordt verborgen. Herbruikbare pijplijnsjablonen, zoals GitHub Actions herbruikbare werkstromen of Azure-pipelines-sjablonen, helpen bij het standaardiseren van de build-, test-, scan- en implementatiestappen voor elke service.

  • Meerdere talen en frameworks: Elk team maakt gebruik van een eigen mix van technologieën, zodat het lastig kan zijn om één buildproces te maken dat in de hele workload werkt. Het buildproces moet flexibel genoeg zijn zodat elk team het kan aanpassen voor de gekozen taal of het gekozen framework.

    Mitigatie: Containeriseer het buildproces voor elke service, zodat het buildsysteem alleen de containers hoeft uit te voeren. Platformen zoals GitHub Actions, Azure-pipelines en Azure Container Registry-taken kunnen consistent containerimages bouwen en publiceren, ongeacht de brontaal.

  • Integratie- en belastingstests: Updates van Teams worden in hun eigen tempo uitgebracht, zodat het lastig kan zijn om robuuste end-to-end tests te ontwerpen, met name wanneer services afhankelijk zijn van andere services. Het uitvoeren van een volledig productiecluster kan kostbaar zijn, dus het is onwaarschijnlijk dat elk team een eigen volledig cluster op productieschaal uitvoert voor testen.

    Mitigation: Gebruik tijdelijke preview-omgevingen, zoals naamruimten per pull-aanvraag in Kubernetes of Azure Container Apps omgevingen die op aanvraag zijn gemaakt. Gebruik contracttests zodat u integratieproblemen vroegtijdig aan het licht brengt zonder dat u daarvoor een volledige kopie van de productieomgeving nodig hebt.

  • Releasebeheer: Elk team moet een update naar productie kunnen implementeren. Deze vereiste betekent niet dat elk teamlid machtigingen heeft om te implementeren. Een gecentraliseerde releasebeheerrol kan de implementatiesnelheid verminderen.

    Mitigatie: Hoe meer uw CI/CD-proces geautomatiseerd en betrouwbaar is, hoe minder u een centrale instantie nodig hebt. Mogelijk hebt u nog steeds verschillende beleidsregels voor het vrijgeven van belangrijke functie-updates versus kleine bugfixes. Een gedecentraliseerde benadering betekent niet nul governance. Goedkeuringen afdwingen met behulp van Azure-pipelines omgevingen en goedkeuringen of GitHub Actions implementatieomgevingen en vereiste revisoren en clusterbeleid codificeren met behulp van Azure Policy voor Azure Kubernetes Service (AKS) of OPA Gatekeeper.

  • Service-updates: Wanneer u een service bijwerkt naar een nieuwe versie, moet de update er niet toe leiden dat andere services die ervan afhankelijk zijn, mislukken.

    Mitigatie: Gebruik implementatietechnieken zoals blauw-groen of canary-releases voor niet-brekende wijzigingen. Voor belangrijke API-wijzigingen implementeert u de nieuwe versie naast de vorige versie. Met deze aanpak kunnen services die de vorige API gebruiken, worden bijgewerkt en getest voor de nieuwe API. Zie Update-services voor meer informatie.

  • Pijplijnidentiteit en geheimbeheer: Langlevende service-principalgeheimen die zijn opgeslagen in pijplijnen, zijn een veelvoorkomende bron van inbreuk en operationeel werk. Secrets van service-principals verlopen, kunnen uitlekken en moeten regelmatig worden vernieuwd in veel onafhankelijke microservice-pipelines.

    Maatregel: Verifieer pijplijnen bij Azure met federatie van workloadidentiteiten, die gebruikmaakt van OpenID Connect (OIDC), zodat er geen clientgeheim in de pijplijn wordt opgeslagen. Zie Workload-identiteiten voor Azure-pipelines en Configure OpenID Connect configureren in Azure voor GitHub Actions voor meer informatie. Sla eventuele resterende geheimen op in Azure Key Vault en verwijs ernaar tijdens runtime.

  • Supply chain-beveiliging: Alles wat u naar productie verzendt, moet traceerbaar zijn voor de code en afhankelijkheden waaruit deze is gebouwd. Microservices verhogen het aantal images, registries en pipelines, waardoor het aanvalsoppervlak van uw supply chain toeneemt.

    Beperking: Onderteken containerimages met behulp van Notation en Key Vault en verifieer handtekeningen tijdens toelating met behulp van AKS image integrity of Ratify. Genereer een SBOM als build-artefact. Scan code, afhankelijkheden en pijplijnen met behulp van Microsoft Defender voor Cloud DevOps-beveiliging en GitHub Advanced Security. Scan runtime-installatiekopieën met behulp van Microsoft Defender voor containers. Vereisen dat alle scans worden doorgegeven voordat een release kan worden voortgezet.

Monorepo versus multirepo

Voordat u een CI/CD-werkstroom maakt, moet u weten hoe de codebasis is gestructureerd en beheerd, waaronder:

  • Of teams nu in afzonderlijke repositories werken of in een monorepo.
  • Uw vertakkingsstrategie.
  • Wie kan code naar productie pushen en of er een releasemanager bestaat.

Teams gebruiken beide benaderingen in productie op grote schaal. Uw keuze is afhankelijk van de teamtopologie, de ontwikkeling van hulpprogramma's en hoeveel code wordt gedeeld tussen services.

  Monorepo Meerdere repos
Voordelen - Code delen

- Eenvoudiger om code en hulpprogramma's te standaardiseren

- Eenvoudiger om code te herstructureren

- Detectie (één weergave van de code)
- Duidelijk eigenaarschap per team

- Mogelijk minder samenvoegingsconflicten

- Helpt bij het afdwingen van microserviceontsleuteling
uitdagingen - Wijzigingen in gedeelde code kunnen van invloed zijn op meerdere microservices

- Groter potentieel voor samenvoegingsconflicten

- Tools moeten schaalbaar zijn voor een grote codebasis

- Toegangsbeheer

- Complexer implementatieproces
- Moeilijker om code te delen

- Moeilijker om coderingsstandaarden af te dwingen

- Afhankelijkheidsbeheer

- Diffuse codebasis, slechte detectie

- Gebrek aan gedeelde infrastructuur

Ongeacht het model dat u kiest, gebruikt padgebonden triggers in uw pijplijnen, zoals padfilters in GitHub Actions of triggerpaden in Azure-pipelines. Padgebonden triggers helpen ervoor te zorgen dat bij elke commit alleen de betrokken microservices opnieuw worden gebouwd en uitgerold.

Services bijwerken

Er zijn verschillende strategieën voor het bijwerken van een service die al in productie is, waaronder rolling update, blauwgroene implementatie en canary-release. Deze patronen worden vaak gecoördineerd via een GitOps-werkstroom. Zie GitOps en progressieve levering voor meer informatie.

Voortschrijdende updates

In een rolling update implementeert u nieuwe exemplaren van een service en beginnen de nieuwe exemplaren onmiddellijk aanvragen te ontvangen. Wanneer de nieuwe exemplaren gereed zijn, worden de vorige exemplaren verwijderd.

Voorbeeld in Kubernetes: 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 vorige ReplicaSet omlaag wordt geschaald om het gewenste aantal replica's te behouden. De vorige pods worden pas verwijderd als de nieuwe pods gereed zijn. Kubernetes houdt een geschiedenis van de update bij, zodat u indien nodig een update kunt terugdraaien.

Voorbeeld in Container Apps: Container Apps maakt gebruik van revisies om rolling updates te beheren. Wanneer u een nieuwe revisie implementeert, kan Container Apps het verkeer van de vorige revisie geleidelijk naar de nieuwe revisie verplaatsen met behulp van regels voor het splitsen van verkeer. Als er problemen optreden met de nieuwe revisie, kunt u teruggaan naar de vorige revisie door het verkeer daarnaartoe om te leiden. U kunt meerdere actieve revisies tegelijk configureren en het percentage verkeer beheren dat elke revisie ontvangt.

Een uitdaging van rolling updates is dat tijdens het updateproces een combinatie van eerdere en nieuwe versies wordt uitgevoerd en verkeer ontvangt. Gedurende deze periode kan het systeem elke aanvraag naar een van beide versies routeren.

Voor belangrijke API-wijzigingen is het raadzaam beide versies naast elkaar te ondersteunen totdat alle clients van de vorige versie worden bijgewerkt. Zie API-versiebeheer voor meer informatie.

Blauwgroene implementatie

In een blauwgroene 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 switch bewaakt u de toepassing op eventuele problemen. Als er een probleem is, kunt u verkeer terugschakelen naar de vorige versie. Als er geen problemen zijn, kunt u de vorige versie verwijderen.

Met een meer traditionele monolithische of N-tier-toepassing betekent blauwgroene implementatie over het algemeen dat u twee identieke omgevingen maakt. U implementeert de nieuwe versie in een faseringsomgeving en stuurt vervolgens clientverkeer om naar die omgeving, bijvoorbeeld door een virtueel IP-adres te wisselen. In een microservicearchitectuur worden updates uitgevoerd op het niveau van de microservice, zodat u de update doorgaans in dezelfde omgeving implementeert en een servicedetectiemechanisme gebruikt om van verkeer te wisselen.

Voorbeeld in Kubernetes: In Kubernetes hoeft u geen afzonderlijk cluster te maken om blauwgroene implementaties uit te voeren. In plaats daarvan kunt u gebruik maken van selecties. Maak een nieuwe implementatieresource met een nieuwe podspecificatie en een andere set labels. Maak deze implementatie, maar verwijder de vorige implementatie niet of wijzig de service die ernaar verwijst. Nadat de nieuwe pods zijn 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 aanzienlijke CPU- of geheugenresources gebruiken, moet u het cluster mogelijk tijdelijk uitschalen om te voldoen aan de hogere vraag naar resources.

Canary-uitgave

In een canary-release implementeert u een bijgewerkte versie naar een kleine subset van clients en controleert u vervolgens het gedrag van de nieuwe service voordat u deze implementeert op alle clients. Met deze aanpak kunt u geleidelijk op een gecontroleerde manier implementeren, echte gegevens bewaken en problemen identificeren voordat ze van invloed zijn op alle klanten.

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

Voorbeeld in Kubernetes: 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 grof omdat Kubernetes load balances over pods verdeelt. Als u bijvoorbeeld in totaal 10 replica's hebt, kunt u alleen verkeer verplaatsen in stappen van 10%. Als u een service-mesh gebruikt, kunt u de regels voor service-mesh-routering gebruiken om een geavanceerdere canary-releasestrategie te implementeren.

Voorbeeld in Container Apps: In Container Apps kunt u verkeerssplitsing gebruiken om een bepaald percentage van het verkeer naar een nieuwe revisie te leiden (zoals 10% naar v2 terwijl 90% op v1 blijft) en de gewichten te verschuiven naarmate het vertrouwen toeneemt, zonder dat een externe servicemesh vereist is.

Progressieve levering en GitOps

Voor teams die veel microservices op Kubernetes gebruiken, vormt een GitOps pull-model een aanvulling op de eerdere push-gebaseerde voorbeelden. De gewenste clusterstatus staat in Git, en een operator binnen het cluster brengt het cluster in overeenstemming met die status. CI bouwt, test, scant, ondertekent en pusht de afbeelding. Met CD wordt het cluster in overeenstemming gebracht met het manifest. Deze scheiding biedt u audittrails en eenvoudiger herstel na noodgevallen (DR). Het neemt ook de noodzaak weg dat de CI-runner over directe toegangsgegevens voor het cluster moet beschikken.

Volgende stappen