Het kan lastig zijn om een betrouwbaar CI/CD-proces (Continuous Integration/Continuous Delivery) te maken voor een microservicesarchitectuur. Afzonderlijke teams moeten services snel en betrouwbaar kunnen vrijgeven, zonder dat andere teams worden verstoord of de toepassing als geheel wordt gedestabiliseerd.
In dit artikel wordt een voorbeeld van een CI/CD-pijplijn beschreven voor het implementeren van microservices in Azure Kubernetes Service (AKS). Elk team en project zijn anders, dus neem dit artikel niet als een set hard-and-fast regels. In plaats daarvan is het bedoeld als uitgangspunt voor het ontwerpen van uw eigen CI/CD-proces.
De doelstellingen van een CI/CD-pijplijn voor door Kubernetes gehoste microservices kunnen als volgt worden samengevat:
- Teams kunnen hun services onafhankelijk bouwen en implementeren.
- Codewijzigingen die het CI-proces doorgeven, worden automatisch geïmplementeerd in een productieachtige omgeving.
- Kwaliteitspoorten worden afgedwongen in elke fase van de pijplijn.
- Een nieuwe versie van een service kan naast de vorige versie worden geïmplementeerd.
Zie CI/CD voor microservicesarchitecturen voor meer achtergrondinformatie.
Aannames
In dit voorbeeld vindt u hier enkele veronderstellingen over het ontwikkelteam en de codebasis:
- De codeopslagplaats is een monorepo, met mappen die zijn georganiseerd door microservice.
- De vertakkingsstrategie van het team is gebaseerd op de ontwikkeling op basis van trunks.
- Het team gebruikt releasebranches om releases te beheren. Voor elke microservice worden afzonderlijke releases gemaakt.
- Het CI/CD-proces maakt gebruik van Azure Pipelines voor het bouwen, testen en implementeren van de microservices in AKS.
- De containerinstallatiekopieën voor elke microservice worden opgeslagen in Azure Container Registry.
- Het team gebruikt Helm-grafieken om elke microservice te verpakken.
- Er wordt een push-implementatiemodel gebruikt, waarbij Azure Pipelines en bijbehorende agents implementaties uitvoeren door rechtstreeks verbinding te maken met het AKS-cluster.
Deze veronderstellingen sturen veel van de specifieke details van de CI/CD-pijplijn aan. De hier beschreven basisbenadering is echter aangepast voor andere processen, hulpprogramma's en services, zoals Jenkins of Docker Hub.
Alternatieven
Hier volgen veelvoorkomende alternatieven die klanten kunnen gebruiken bij het kiezen van een CI/CD-strategie met Azure Kubernetes Service:
- Als alternatief voor het gebruik van Helm als pakketbeheer- en implementatieprogramma is Kustomize een systeemeigen hulpprogramma voor configuratiebeheer van Kubernetes waarmee een sjabloonvrije manier wordt geïntroduceerd om de toepassingsconfiguratie aan te passen en te parameteriseren.
- Als alternatief voor het gebruik van Azure DevOps voor Git-opslagplaatsen en -pijplijnen kunnen GitHub-opslagplaatsen worden gebruikt voor privé- en openbare Git-opslagplaatsen, en GitHub Actions kunnen worden gebruikt voor CI/CD-pijplijnen.
- Als alternatief voor het gebruik van een push-implementatiemodel kan het beheer van de Kubernetes-configuratie op grote schaal worden uitgevoerd met behulp van GitOps (pull-implementatiemodel), waarbij een Kubernetes-operator in het cluster de clusterstatus synchroniseert op basis van de configuratie die is opgeslagen in een Git-opslagplaats.
Validatie-builds
Stel dat een ontwikkelaar werkt aan een microservice met de naam Delivery Service. Tijdens het ontwikkelen van een nieuwe functie controleert de ontwikkelaar code in een functiebranch. Volgens conventie hebben functiebranches de naam feature/*
.
Het builddefinitiebestand bevat een trigger die filtert op de naam van de vertakking en het bronpad:
trigger:
batch: true
branches:
include:
# for new release to production: release flow strategy
- release/delivery/v*
- refs/release/delivery/v*
- master
- feature/delivery/*
- topic/delivery/*
paths:
include:
- /src/shipping/delivery/
Met deze benadering kan elk team een eigen build-pijplijn hebben. Alleen code die is ingecheckt in de /src/shipping/delivery
map activeert een build van de Delivery Service. Doorvoeringen pushen naar een vertakking die overeenkomt met het filter activeert een CI-build. Op dit moment in de werkstroom voert de CI-build een minimale codeverificatie uit:
- Bouw de code.
- Eenheidstests uitvoeren.
Het doel is om de buildtijden kort te houden, zodat de ontwikkelaar snel feedback kan krijgen. Zodra de functie klaar is om samen te voegen in master, opent de ontwikkelaar een pull-aanvraag. Met deze bewerking wordt een andere CI-build geactiveerd waarmee een aantal extra controles worden uitgevoerd:
- Bouw de code.
- Eenheidstests uitvoeren.
- Bouw de installatiekopieën van de runtimecontainer.
- Voer beveiligingsscans uit op de installatiekopie.
Notitie
In Azure DevOps-opslagplaatsen kunt u beleidsregels definiëren om vertakkingen te beveiligen. Het beleid kan bijvoorbeeld een geslaagde CI-build plus een afmelding van een fiatteur vereisen om samen te voegen in master.
Volledige CI/CD-build
Op een bepaald moment is het team klaar om een nieuwe versie van de Delivery-service te implementeren. De releasebeheerder maakt een vertakking van de hoofdvertakking met dit naamgevingspatroon: release/<microservice name>/<semver>
. Bijvoorbeeld: release/delivery/v1.0.2
.
Het maken van deze vertakking activeert een volledige CI-build die alle vorige stappen uitvoert plus:
- Push de containerinstallatiekopieën naar Azure Container Registry. De installatiekopieën worden getagd met het versienummer dat is opgehaald uit de naam van de vertakking.
- Voer deze opdracht uit
helm package
om de Helm-grafiek voor de service te verpakken. De grafiek wordt ook getagd met een versienummer. - Push het Helm-pakket naar Container Registry.
Ervan uitgaande dat deze build slaagt, wordt een implementatieproces (CD) geactiveerd met behulp van een Release-pijplijn van Azure Pipelines. Deze pijplijn heeft de volgende stappen:
- Implementeer de Helm-grafiek in een QA-omgeving.
- Een fiatteur meldt zich af voordat het pakket naar productie gaat. Zie Release-implementatiebeheer met behulp van goedkeuringen.
- Tag de Docker-installatiekopieën voor de productienaamruimte in Azure Container Registry. Als de huidige tag bijvoorbeeld is, is
myrepo.azurecr.io/delivery:v1.0.2
myrepo.azurecr.io/prod/delivery:v1.0.2
de productietag . - Implementeer de Helm-grafiek in de productieomgeving.
Zelfs in een monorepo kunnen deze taken worden afgestemd op afzonderlijke microservices, zodat teams met hoge snelheid kunnen implementeren. Het proces bevat enkele handmatige stappen: goedkeuring van PULL's, het maken van release-vertakkingen en het goedkeuren van implementaties in het productiecluster. Deze stappen zijn handmatig; ze kunnen worden geautomatiseerd als de organisatie de voorkeur geeft.
Isolatie van omgevingen
U hebt meerdere omgevingen waarin u services implementeert, waaronder omgevingen voor ontwikkeling, betrouwbaarheidstests, integratietests, belastingtests en ten slotte productie. Deze omgevingen hebben enige isolatie nodig. In Kubernetes hebt u een keuze tussen fysieke isolatie en logische isolatie. Fysieke isolatie betekent implementeren in afzonderlijke clusters. Logische isolatie maakt gebruik van naamruimten en beleidsregels, zoals eerder beschreven.
We raden u aan om een toegewezen productiecluster te maken, samen met een afzonderlijk cluster voor uw ontwikkel-/testomgevingen. Gebruik logische isolatie om omgevingen binnen het ontwikkel-/testcluster te scheiden. Services die zijn geïmplementeerd in het ontwikkel-/testcluster, mogen nooit toegang hebben tot gegevensarchieven die zakelijke gegevens bevatten.
Bouwproces
Indien mogelijk moet u uw buildproces verpakken in een Docker-container. Met deze configuratie kunt u codeartefacten bouwen met behulp van Docker en zonder dat u een buildomgeving op elke buildcomputer configureert. Met een containerbuildproces kunt u de CI-pijplijn eenvoudig uitschalen door nieuwe buildagents toe te voegen. Bovendien kan elke ontwikkelaar in het team de code bouwen door de buildcontainer uit te voeren.
Met behulp van builds met meerdere fasen in Docker kunt u de buildomgeving en de runtime-installatiekopieën definiëren in één Dockerfile. Hier volgt bijvoorbeeld een Dockerfile waarmee een .NET-toepassing wordt gebouwd:
FROM mcr.microsoft.com/dotnet/core/runtime:3.1 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /src/Fabrikam.Workflow.Service
COPY Fabrikam.Workflow.Service/Fabrikam.Workflow.Service.csproj .
RUN dotnet restore Fabrikam.Workflow.Service.csproj
COPY Fabrikam.Workflow.Service/. .
RUN dotnet build Fabrikam.Workflow.Service.csproj -c release -o /app --no-restore
FROM build AS testrunner
WORKDIR /src/tests
COPY Fabrikam.Workflow.Service.Tests/*.csproj .
RUN dotnet restore Fabrikam.Workflow.Service.Tests.csproj
COPY Fabrikam.Workflow.Service.Tests/. .
ENTRYPOINT ["dotnet", "test", "--logger:trx"]
FROM build AS publish
RUN dotnet publish Fabrikam.Workflow.Service.csproj -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "Fabrikam.Workflow.Service.dll"]
Dit Dockerfile definieert verschillende buildfasen. U ziet dat de fase met de naam base
gebruikmaakt van de .NET-runtime, terwijl de fase met de naam build
gebruikmaakt van de volledige .NET SDK. De build
fase wordt gebruikt om het .NET-project te bouwen. Maar de uiteindelijke runtimecontainer is gebouwd op basis van base
, die alleen de runtime bevat en aanzienlijk kleiner is dan de volledige SDK-installatiekopie.
Een testloper bouwen
Een andere goede gewoonte is het uitvoeren van eenheidstests in de container. Hier maakt u bijvoorbeeld deel uit van een Docker-bestand waarmee een testrunner wordt gebouwd:
FROM build AS testrunner
WORKDIR /src/tests
COPY Fabrikam.Workflow.Service.Tests/*.csproj .
RUN dotnet restore Fabrikam.Workflow.Service.Tests.csproj
COPY Fabrikam.Workflow.Service.Tests/. .
ENTRYPOINT ["dotnet", "test", "--logger:trx"]
Een ontwikkelaar kan dit Docker-bestand gebruiken om de tests lokaal uit te voeren:
docker build . -t delivery-test:1 --target=testrunner
docker run delivery-test:1
De CI-pijplijn moet ook de tests uitvoeren als onderdeel van de buildverificatiestap.
Houd er rekening mee dat dit bestand de Docker-opdracht ENTRYPOINT
gebruikt om de tests uit te voeren, niet de Docker-opdracht RUN
.
- Als u de
RUN
opdracht gebruikt, worden de tests elke keer uitgevoerd wanneer u de installatiekopieën bouwt. Door gebruik te makenENTRYPOINT
, worden de tests aangemeld. Ze worden alleen uitgevoerd wanneer u de fase expliciet richttestrunner
. - Een mislukte test zorgt er niet voor dat de Docker-opdracht
build
mislukt. Op die manier kunt u fouten in de build van containers onderscheiden van testfouten. - Testresultaten kunnen worden opgeslagen op een gekoppeld volume.
Best practices voor containers
Hier volgen enkele andere aanbevolen procedures voor containers:
Definieer organisatiebrede conventies voor containertags, versiebeheer en naamconventies voor resources die zijn geïmplementeerd in het cluster (pods, services enzovoort). Dit kan het eenvoudiger maken om implementatieproblemen vast te stellen.
Tijdens de ontwikkelings- en testcyclus bouwt het CI/CD-proces veel containerinstallatiekopieën. Slechts enkele van deze installatiekopieën zijn kandidaten voor release en vervolgens worden slechts enkele van deze releasekandidaten gepromoveerd naar productie. Zorg voor een duidelijke versiebeheerstrategie zodat u weet welke installatiekopieën momenteel in productie zijn geïmplementeerd en om zo nodig terug te keren naar een eerdere versie.
Implementeer altijd specifieke tags voor containerversies, niet
latest
.Gebruik naamruimten in Azure Container Registry om installatiekopieën te isoleren die zijn goedgekeurd voor productie van installatiekopieën die nog worden getest. Verplaats een installatiekopieën pas naar de productienaamruimte als u klaar bent om deze in productie te implementeren. Als u deze procedure combineert met semantische versiebeheer van containerinstallatiekopieën, kan dit de kans verminderen dat er per ongeluk een versie wordt geïmplementeerd die niet is goedgekeurd voor de release.
Volg het principe van minimale bevoegdheden door containers uit te voeren als een niet-gemachtigde gebruiker. In Kubernetes kunt u een beveiligingsbeleid voor pods maken dat voorkomt dat containers als hoofdmap worden uitgevoerd.
Helm-grafieken
Overweeg het gebruik van Helm om het bouwen en implementeren van services te beheren. Hier volgen enkele van de functies van Helm die u helpen met CI/CD:
- Vaak wordt één microservice gedefinieerd door meerdere Kubernetes-objecten. Met Helm kunnen deze objecten in één Helm-grafiek worden verpakt.
- Een grafiek kan worden geïmplementeerd met één Helm-opdracht in plaats van een reeks kubectl-opdrachten.
- Grafieken worden expliciet geversied. Gebruik Helm om een versie vrij te geven, releases weer te geven en terug te keren naar een vorige versie. Updates en revisies bijhouden, met behulp van semantische versiebeheer, samen met de mogelijkheid om terug te keren naar een eerdere versie.
- Helm-grafieken gebruiken sjablonen om te voorkomen dat gegevens, zoals labels en selectors, in veel bestanden worden gedupliceerd.
- Helm kan afhankelijkheden tussen grafieken beheren.
- Grafieken kunnen worden opgeslagen in een Helm-opslagplaats, zoals Azure Container Registry, en kunnen worden geïntegreerd in de build-pijplijn.
Zie Azure Container Registry als Helm-opslagplaats gebruiken als helm-opslagplaats voor uw toepassingsgrafieken voor meer informatie over het gebruik van Container Registry als helm-opslagplaats.
Een enkele microservice kan betrekking hebben op meerdere Kubernetes-configuratiebestanden. Het bijwerken van een service kan betekenen dat al deze bestanden worden aangeraakt om selectors, labels en afbeeldingstags bij te werken. Helm behandelt deze als één pakket dat een grafiek wordt genoemd en stelt u in staat om de YAML-bestanden eenvoudig bij te werken met behulp van variabelen. Helm maakt gebruik van een sjabloontaal (op basis van Go-sjablonen) waarmee u geparameteriseerde YAML-configuratiebestanden kunt schrijven.
Hier maakt u bijvoorbeeld deel uit van een YAML-bestand dat een implementatie definieert:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "package.fullname" . | replace "." "" }}
labels:
app.kubernetes.io/name: {{ include "package.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
annotations:
kubernetes.io/change-cause: {{ .Values.reason }}
...
spec:
containers:
- name: &package-container_name fabrikam-package
image: {{ .Values.dockerregistry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: LOG_LEVEL
value: {{ .Values.log.level }}
U kunt zien dat de implementatienaam, labels en containerspecificaties allemaal sjabloonparameters gebruiken, die tijdens de implementatie worden opgegeven. Bijvoorbeeld vanaf de opdrachtregel:
helm install $HELM_CHARTS/package/ \
--set image.tag=0.1.0 \
--set image.repository=package \
--set dockerregistry=$ACR_SERVER \
--namespace backend \
--name package-v0.1.0
Hoewel uw CI/CD-pijplijn een grafiek rechtstreeks naar Kubernetes kan installeren, raden we u aan een grafiekarchief (.tgz-bestand) te maken en de grafiek naar een Helm-opslagplaats te pushen, zoals Azure Container Registry. Zie Package Docker-apps in Helm-grafieken in Azure Pipelines voor meer informatie.
Revisies
Helm-grafieken hebben altijd een versienummer, dat semantische versiebeheer moet gebruiken. Een grafiek kan ook een appVersion
. Dit veld is optioneel en hoeft niet gerelateerd te zijn aan de grafiekversie. Sommige teams willen mogelijk afzonderlijke toepassingsversies van updates naar de grafieken. Maar een eenvoudigere benadering is om één versienummer te gebruiken, dus er is een 1:1-relatie tussen de grafiekversie en de toepassingsversie. Op die manier kunt u één grafiek per release opslaan en eenvoudig de gewenste release implementeren:
helm install <package-chart-name> --version <desiredVersion>
Een andere goede gewoonte is om een wijzigingsoorzaakaantekening op te geven in de implementatiesjabloon:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "delivery.fullname" . | replace "." "" }}
labels:
...
annotations:
kubernetes.io/change-cause: {{ .Values.reason }}
Hiermee kunt u het veld wijzigingsoorzaak voor elke revisie weergeven met behulp van de kubectl rollout history
opdracht. In het vorige voorbeeld wordt de wijzigingsoorzaak opgegeven als een Helm-grafiekparameter.
kubectl rollout history deployments/delivery-v010 -n backend
deployment.extensions/delivery-v010
REVISION CHANGE-CAUSE
1 Initial deployment
U kunt ook de helm list
opdracht gebruiken om de revisiegeschiedenis weer te geven:
helm list
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
delivery-v0.1.0 1 Sun Apr 7 00:25:30 2020 DEPLOYED delivery-v0.1.0 v0.1.0 backend
Azure DevOps Pipeline
In Azure Pipelines zijn pijplijnen onderverdeeld in build-pijplijnen en release-pijplijnen. De build-pijplijn voert het CI-proces uit en maakt buildartefacten. Voor een microservicesarchitectuur in Kubernetes zijn deze artefacten de containerinstallatiekopieën en Helm-grafieken die elke microservice definiëren. Met de release-pijplijn wordt dat CD-proces uitgevoerd waarmee een microservice in een cluster wordt geïmplementeerd.
Op basis van de CI-stroom die eerder in dit artikel is beschreven, kan een build-pijplijn bestaan uit de volgende taken:
Bouw de testrunnercontainer met behulp van de
Docker
taak.Voer de tests uit door Docker aan te roepen voor de testrunnercontainer. Hierbij wordt de
Docker
taak gebruikt.Publiceer de testresultaten met behulp van de
PublishTestResults
taak. Zie Een installatiekopieën bouwen.Bouw de runtimecontainer met behulp van lokale Docker-build en de
Docker
taak of met behulp van Azure Container Registry-builds en deAzureCLI
taak.Push de containerinstallatiekopieën naar Azure Container Registry (of een ander containerregister) met behulp van de
Docker
ofAzureCLI
taken.Pak de Helm-grafiek in met behulp van de
HelmDeploy
taak.Push het Helm-pakket naar Azure Container Registry (of een andere Helm-opslagplaats), met behulp van de
HelmDeploy
taak.
De uitvoer van de CI-pijplijn is een containerinstallatiekopieën die gereed zijn voor productie en een bijgewerkte Helm-grafiek voor de microservice. Op dit moment kan de release-pijplijn worden overgenomen. Er is een unieke release-pijplijn voor elke microservice. De release-pijplijn wordt geconfigureerd voor een triggerbron die is ingesteld op de CI-pijplijn die het artefact heeft gepubliceerd. Met deze pijplijn kunt u onafhankelijke implementaties van elke microservice uitvoeren. De release-pijplijn voert de volgende stappen uit:
- Implementeer de Helm-grafiek in ontwikkel-/QA-/faseringsomgevingen. De
helm upgrade
opdracht kan worden gebruikt met de--install
vlag ter ondersteuning van de eerste installatie en volgende upgrades. - Wacht totdat een fiatteur de implementatie goedkeurt of weigert.
- De containerinstallatiekopieën opnieuw tagen voor release
- Push de releasetag naar het containerregister.
- Implementeer de Helm-grafiek in het productiecluster.
Zie Release-pijplijnen, conceptversies en releaseopties voor meer informatie over het maken van een release-pijplijn.
In het volgende diagram ziet u het end-to-end CI/CD-proces dat in dit artikel wordt beschreven:
Volgende stappen
- Een Git-vertakkingsstrategie aannemen
- Wat is Azure Pipelines?
- Release-pijplijnen, conceptreleases en releaseopties
- Implementatiebeheer vrijgeven met behulp van gates
- Inleiding tot containerregisters in Azure