Creare una pipeline CI/CD per i microservizi in Kubernetes usando Azure DevOps e Helm

La creazione di un processo di integrazione continua e recapito continuo (CI/CD) affidabile per un'architettura di microservizi può risultare complessa. Ogni team deve rilasciare i servizi in modo rapido e affidabile, senza interrompere altri team o destabilizzare l'applicazione nel suo complesso.

Questo articolo descrive una pipeline CI/CD di esempio per la distribuzione di microservizi in Servizio Azure Kubernetes (AKS). Ogni team e progetto è diverso, quindi non considerare questo articolo come un insieme di regole rigide. Usarlo invece come punto di partenza per progettare un processo CI/CD personalizzato.

L'elenco seguente riepiloga gli obiettivi di una pipeline CI/CD per i microservizi ospitati in Kubernetes:

  • Teams può creare e distribuire i propri servizi in modo indipendente.
  • Modifiche al codice che passano automaticamente il processo CI vengono distribuite in un ambiente simile a quello di produzione.
  • Ogni fase della pipeline applica controlli di qualità.
  • Una nuova versione di un servizio può essere distribuita side-by-side con la versione precedente.

Per altre informazioni, vedere CI/CD per le architetture di microservizi.

Presupposti

Per questo esempio, ecco alcuni presupposti relativi al team di sviluppo e alla codebase:

  • Il repository del codice è organizzato come un monorepo, con cartelle disposte per microservizio.
  • La strategia di ramificazione del team è basata sullo trunk-based development.
  • Il team usa i rami di rilascio per gestire le versioni. Vengono create versioni separate per ogni microservizio.
  • Il processo CI/CD usa Azure Pipelines per compilare, testare e distribuire i microservizi su AKS.
  • Le immagini del contenitore per tutti i microservizi vengono archiviate in un'unica istanza di Registro Azure Container condivisa, con un repository separato per ogni microservizio. Usare Registro Container Azure autorizzazioni del repository ABAC per definire l'ambito di ogni identità della pipeline nel proprio repository o usare registri separati in cui sono necessari limiti di attendibilità più sicuri.
  • Il team usa grafici Helm per creare un pacchetto di ogni microservizio.
  • Viene usato un modello di distribuzione push, in cui Azure Pipelines e gli agenti associati eseguono distribuzioni connettendosi direttamente al cluster del servizio Azure Kubernetes.

Questi presupposti determinano molti dei dettagli specifici della pipeline CI/CD. Tuttavia, è possibile adattare l'approccio di base descritto qui per altri processi, strumenti e servizi, ad esempio GitHub Actions, Jenkins o Docker Hub.

Le alternative

Quando si sceglie una strategia CI/CD con il servizio Azure Kubernetes, prendere in considerazione le alternative comuni seguenti:

  • Anziché usare Helm come strumento di gestione e distribuzione dei pacchetti, è possibile usare Kustomize, uno strumento di gestione della configurazione nativa di Kubernetes che introduce un modo senza modello per personalizzare e parametrizzare la configurazione dell'applicazione.

  • Anziché usare Azure DevOps per i repository e le pipeline Git, è possibile usare repository GitHub per repository Git privati e pubblici e GitHub Actions per le pipeline CI/CD.

    GitHub Actions fornisce l'integrazione con il servizio Azure Kubernetes con i flussi di lavoro iniziali e supporta OpenID Connect (OIDC) per l'autenticazione sicura e senza segreti da Azure.

  • Invece di usare un modello di distribuzione push, è consigliabile gestire la configurazione di Kubernetes su larga scala usando un gitOps (modello di distribuzione pull). Un operatore Kubernetes nel cluster, ad esempio Flux o Argo CD , sincronizza lo stato del cluster in base alla configurazione archiviata in un repository Git. GitOps elimina la necessità di avere accesso diretto ai cluster, riduce la superficie di attacco e offre funzionalità di rilevamento auto-correzione e deviazione.

    Tip

    Quando si valutano modelli di distribuzione basati su push e basati su pull (GitOps), prendere in considerazione i requisiti del carico di lavoro. Le distribuzioni basate su push offrono aggiornamenti deterministici e controllo diretto della pipeline, che si adatta alle procedure di distribuzione sicure. Le distribuzioni basate sul pull (GitOps) offrono coerenza, controllabilità e riparazione automatica, che li rende ideali per gli ambienti in cui i cluster devono riconciliarsi con uno stato desiderato senza l'accesso diretto da pipeline a cluster.

Compilazioni di convalida

Si supponga che uno sviluppatore stia lavorando a un microservizio denominato Servizio di distribuzione. Quando si sviluppa una nuova funzionalità, lo sviluppatore controlla il codice in un ramo di funzionalità. Per convenzione, i rami di funzionalità sono denominati feature/*.

Diagramma di un flusso di lavoro del ramo di funzionalità.

Il diagramma mostra due linee di ramo Git orizzontali e una pipeline di compilazione sotto di esse. Nella parte superiore è presente il ramo principale, rappresentato come linea orizzontale. Il ramo feature/8150 si differenzia dal principale. Questo ramo ha tre punti posizionati su di esso, collettivamente etichettati commit. Da ognuno dei tre commit, una freccia punta verso il basso verso il basso verso una casella nella parte inferiore del diagramma. La casella è denominata Pipeline di compilazione e contiene il nome della pipeline ci-delivery-validation. Tutte e tre le frecce tratteggiate convergeno in questa singola casella della pipeline di compilazione, a indicare che ogni commit nella funzionalità/8150 branch attiva un'esecuzione della pipeline di convalida della distribuzione ci.

Il file di definizione di compilazione include un trigger che filtra in base al nome del ramo e al percorso di origine:

trigger:
  batch: true
  branches:
    include:
    # for new release to production: release flow strategy
    - release/delivery/v*
    - refs/release/delivery/v*
    - main
    - feature/delivery/*
    - topic/delivery/*
  paths:
    include:
    - /src/shipping/delivery/

Quando si usa questo approccio, ogni team può avere una propria pipeline di compilazione. Solo il codice archiviato nella /src/shipping/delivery cartella attiva una compilazione del servizio di recapito. Il push dei commit in un ramo che corrisponde al filtro attiva una compilazione CI. A questo punto nel flusso di lavoro, la compilazione CI esegue alcune verifiche minime del codice:

  1. Compilare il codice.
  2. Eseguire gli unit test.

L'obiettivo è mantenere brevi i tempi di compilazione in modo che lo sviluppatore possa ottenere feedback rapido. Quando la funzionalità è pronta per l'unione in main, lo sviluppatore apre una richiesta pull. Questa operazione attiva un'altra compilazione CI che esegue alcuni controlli aggiuntivi:

  1. Compilare il codice.
  2. Eseguire gli unit test.
  3. Eseguire test di sicurezza delle applicazioni statiche (SAST) nel codice sorgente.
  4. Creare l'immagine del contenitore di runtime.
  5. Eseguire analisi della vulnerabilità nell'immagine.

Diagramma che mostra

Il diagramma mostra due linee di ramo Git orizzontali e una pipeline di compilazione sotto di esse. Nella parte superiore è presente il ramo principale, rappresentato come linea orizzontale. Di seguito, il ramo feature/8150 viene eseguito in parallelo e ha tre puntini su di esso che rappresentano i commit. Alla fine destra del ramo feature/8150, una freccia tratteggiata si connette a un punto del ramo principale. Tale punto è contrassegnato con un altro punto etichettato per la richiesta pull, a indicare che una richiesta pull è stata aperta rispetto a main. Dal punto di richiesta pull nel ramo principale, una freccia tratteggiata punta verso il basso a una pipeline di compilazione con etichetta box, che contiene il nome della pipeline ci-delivery-full. Questa riga illustra che l'apertura di una richiesta pull dal ramo di funzionalità al main attiva la pipeline completa per il recapito ci, che esegue il set esteso di controlli CI.

Note

In Azure Repos, uno dei servizi in Azure DevOps, è possibile definire criteri per proteggere i rami. Ad esempio, i criteri potrebbero richiedere una compilazione CI corretta e una disconnettezione da un responsabile approvazione prima di un merge in main.

Compilazione CI/CD completa

Quando il team è pronto per distribuire una nuova versione del servizio di distribuzione, il responsabile della versione crea un ramo dal ramo principale, usando questo modello di denominazione: release/<microservice name>/<semver>. Ad esempio: release/delivery/v1.0.2.

Diagramma che mostra un ramo di versione che attiva una pipeline di compilazione seguita da una pipeline di versione.

Il diagramma mostra tre linee di ramo Git orizzontali e due pipeline sotto di esse. Al centro è il ramo principale, rappresentato come linea orizzontale. Di seguito è riportato il ramo feature/8150, con tre puntini su di esso che rappresentano i commit. Il ramo feature/8150 si connette al ramo principale in un punto etichettato unione, a indicare che il ramo di funzionalità viene unito a main. Sopra e a destra del ramo principale, un nuovo ramo con etichetta release/delivery/v1.0.2 si estende a destra. Questo ramo di versione ha origine da main a un punto a destra dell'unione. Dal ramo release/delivery/v1.0.2, una linea tratteggiata punta verso il basso a una casella con etichetta ci-delivery-full, che si trova sopra l'etichetta Pipeline di compilazione. A destra di ci-delivery-full, una freccia orizzontale punta a una seconda casella con etichetta cd-delivery, che si trova sopra la pipeline di rilascio dell'etichetta.

La creazione di questo ramo attiva una compilazione CI completa che esegue tutti i passaggi precedenti e questi passaggi:

  1. Eseguire il push dell'immagine del contenitore in Registro Contenitori. L'immagine viene contrassegnata con il numero di versione nel nome del ramo.
  2. Eseguire helm package per creare il pacchetto del grafico Helm per il servizio. Il grafico viene anche contrassegnato con un numero di versione.
  3. Inviare il pacchetto Helm nel registro di container.

Se la compilazione ha esito positivo, attiva un processo di distribuzione (CD) usando una pipeline di versione Azure Pipelines. Questa pipeline contiene i passaggi seguenti:

  1. Distribuire il chart Helm in un ambiente QA.
  2. Un responsabile dà l'approvazione prima che il pacchetto venga spostato nell'ambiente di produzione. Vedere Release deployment control using approvals (Controllo della distribuzione della versione tramite approvazioni).
  3. Ripetere il tag dell'immagine Docker per lo spazio dei nomi di produzione nel Registro Container. Ad esempio, se il tag corrente è myrepo.azurecr.io/delivery:v1.0.2, il tag di produzione è myrepo.azurecr.io/prod/delivery:v1.0.2.
  4. Distribuire il chart Helm nell'ambiente di produzione.

Anche in un monorepo, definire l'ambito di queste attività in singoli microservizi in modo che i team possano distribuirli in modo indipendente. Il processo include alcuni passaggi manuali: approvazione delle richieste pull, creazione di rami di rilascio e approvazione delle distribuzioni nel cluster di produzione. I team del carico di lavoro possono automatizzare questi passaggi se vogliono.

Isolamento degli ambienti

I servizi vengono distribuiti in più ambienti, inclusi gli ambienti per lo sviluppo, i test di fumo, i test di integrazione, i test di carico e la produzione. Per questi ambienti è necessario un certo livello di isolamento. In Kubernetes è possibile scegliere tra isolamento fisico e isolamento logico. L'isolamento fisico viene distribuito in cluster separati. L'isolamento logico usa spazi dei nomi e criteri.

È consigliabile creare un cluster di produzione dedicato insieme a un cluster separato per gli ambienti di sviluppo/test. Usare l'isolamento logico per separare gli ambienti all'interno del cluster di sviluppo/prova. I servizi distribuiti nel cluster di sviluppo/prova non avranno accesso agli archivi dati che contengono dati aziendali.

Per applicare l'isolamento all'interno di un cluster, seguire questa procedura:

  • Spazi dei nomi: usare gli spazi dei nomi Kubernetes per separare logicamente gli ambienti , devad esempio , staging, qa.
  • Criteri di rete: applicare criteri di rete Kubernetes per negare la comunicazione da pod a pod tra spazi dei nomi.
  • Quote di risorse: applicare quote di risorse per spazio dei nomi per evitare problemi con i vicini rumorosi nei cluster condivisi.
  • integrazione Microsoft Entra ID: combinare l'autenticazione Microsoft Entra ID con il controllo degli accessi in base al ruolo di Kubernetes e Microsoft Entra ID per controllare quali Microsoft Entra utenti e gruppi possono accedere a ogni spazio dei nomi.

Autenticazione e autorizzazione

Usare l'autenticazione senza segreti laddove possibile, sia per le pipeline che distribuiscono i microservizi che per i carichi di lavoro distribuiti da tali pipeline:

  • Federazione delle identità del carico di lavoro per le pipeline: per Azure Pipelines, usare una connessione del servizio federativo delle identità del carico di lavoro per eseguire l'autenticazione a Azure senza archiviare segreti dell'entità servizio di lunga durata. Per GitHub Actions, configurare OIDC per ottenere la stessa autenticazione senza segreto.
  • ID dei carichi di lavoro di Microsoft Entra per i carichi di lavoro distribuiti: configurare i microservizi distribuiti dalla pipeline per usare ID dei carichi di lavoro di Microsoft Entra anziché inserire credenziali tramite manifesti, valori Helm o segreti Kubernetes. L'ID del carico di lavoro esegue la federazione degli account del servizio Kubernetes con Microsoft Entra ID in modo che i pod possano eseguire l'autenticazione ai servizi Azure (ad esempio Azure Key Vault, Registro Contenitori o Azure SQL) senza che la pipeline gestisca segreti.

Gestione dei segreti

Non incorporare mai segreti (stringhe di connessione, chiavi API, password di database) direttamente nel codice sorgente, dockerfile, file di valori Helm o definizioni di pipeline. Invece:

Importante

Evitare di archiviare credenziali di lunga durata (segreti client, certificati o password) in variabili della pipeline, variabili di ambiente o segreti Kubernetes. Usare invece identità gestite e credenziali federate per ridurre il carico di carico di rotazione delle credenziali e la superficie di attacco.

Processo di costruzione

Quando possibile, creare un pacchetto del processo di compilazione in un contenitore Docker. Questa configurazione consente di compilare artefatti di codice usando Docker senza configurare un ambiente di compilazione in ogni computer di compilazione. Un processo di build containerizzato semplifica la scalabilità della pipeline di integrazione continua aggiungendo nuovi agenti di build. Inoltre, qualsiasi sviluppatore del team può compilare il codice eseguendo il contenitore di compilazione.

Usando compilazioni multistage in Docker, è possibile definire l'ambiente di compilazione e l'immagine di runtime in un singolo Dockerfile. Ad esempio, il Dockerfile seguente compila un'applicazione .NET 10:

FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS base
USER app
WORKDIR /app
EXPOSE 8080

FROM mcr.microsoft.com/dotnet/sdk:10.0 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/build --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/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Fabrikam.Workflow.Service.dll"]

Questo Dockerfile definisce diverse fasi di compilazione. Si noti che la fase denominata base usa l'immagine di runtime .NET 10 ASP.NET, mentre la fase denominata build usa l'SDK completo .NET 10. La build fase compila il progetto .NET. Tuttavia, il contenitore di runtime finale viene compilato da base, che contiene solo il runtime ed è notevolmente inferiore all'immagine completa dell'SDK.

Importante

A partire da .NET 8, le immagini del contenitore .NET basate su Linux ufficiali includono un utente non radice denominato app. L'istruzione USER app nel Dockerfile esegue il contenitore come utente senza privilegi, seguendo il principio dei privilegi minimi. ASP.NET Core immagini del contenitore hanno modificato anche la porta di ascolto predefinita da 80 a 8080.

Creazione di un test runner

Un'altra procedura consigliata consiste nell'eseguire unit test nel contenitore. Ad esempio, il codice seguente mostra parte di un Dockerfile che compila un runner di test:

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"]

Uno sviluppatore può usare questo Dockerfile per eseguire i test in locale:

docker build . -t delivery-test:1 --target=testrunner
docker run delivery-test:1

La pipeline CI deve anche eseguire i test come parte del passaggio di verifica della compilazione.

Questo file usa il comando Docker ENTRYPOINT , non il comando Docker RUN , per eseguire i test.

  • Se si usa il RUN comando , i test vengono eseguiti ogni volta che si compila l'immagine. Se si usa ENTRYPOINT, i test vengono acconsentiti esplicitamente. Vengono eseguiti solo quando si fa riferimento in modo esplicito alla fase testrunner.
  • Un test non superato non causa l'esito negativo del comando Docker build . Questo comportamento consente di distinguere gli errori di compilazione del contenitore dagli errori di test.
  • I risultati dei test possono essere salvati in un volume montato.

Procedure consigliate per i contenitori

Ecco alcune altre procedure consigliate da considerare per i contenitori:

  • Definire convenzioni a livello di organizzazione per tag del contenitore, controllo delle versioni e convenzioni di denominazione per le risorse distribuite nel cluster, ad esempio pod e servizi. L'uso di queste convenzioni può semplificare la diagnosi dei problemi di distribuzione.
  • Durante il ciclo di sviluppo e test, il processo CI/CD compila molte immagini del contenitore. Solo alcune di queste immagini sono candidate per il rilascio e solo alcuni di questi candidati vengono promossi alla produzione. Disporre di una strategia di controllo delle versioni chiara in modo da sapere quali immagini sono attualmente distribuite nell'ambiente di produzione e possono eseguire facilmente il rollback a una versione precedente, se necessario.
  • Distribuire sempre tag di versione del contenitore specifici, non latest.
  • Usare gli spazi dei nomi nel Registro Container per isolare le immagini approvate per la produzione da immagini ancora testate. Non spostare un'immagine nello spazio dei nomi di produzione finché non si è pronti per distribuirla nell'ambiente di produzione. La combinazione di questa procedura con il controllo delle versioni semantiche delle immagini del contenitore può ridurre le probabilità di distribuire accidentalmente una versione non approvata per il rilascio.
  • Seguire il principio dei privilegi minimi eseguendo i contenitori come utente non privilegiato. In Kubernetes usare gli standard di sicurezza dei pod con l'ammissione di sicurezza dei pod (che ha sostituito i criteri di sicurezza dei pod deprecati in Kubernetes 1.25) per applicare restrizioni come impedire l'esecuzione dei contenitori come radice. Usare il restricted profilo per i carichi di lavoro di produzione.
  • Usare immagini di base minime o senza distribuzione (ad esempio immagini basate su Alpine, Azure immagini di base Linux o immagini senza distribuzione o immagini di .NET non crittografate) per ridurre la superficie di attacco delle immagini del contenitore.
  • Per i registri Premium, configurare un criterio di conservazione del Registro Container per eliminare i manifesti senza tag. Per eliminare i tag in base all'età o al nome, pianificare un'attività del Registro Container che esegue acr purge. Mantenere le immagini a cui fanno riferimento le distribuzioni attive e i piani di rollback.

Grafici Helm

È consigliabile usare Helm per gestire la costruzione e il deployment di servizi. Le funzionalità Helm seguenti supportano una pipeline CI/CD:

  • Un singolo microservizio viene spesso definito da più oggetti Kubernetes. Helm consente di creare un pacchetto di questi oggetti in un singolo grafico Helm.
  • È possibile distribuire un grafico usando un singolo comando Helm anziché una serie di comandi kubectl.
  • I grafici sono versionati esplicitamente. Usare Helm per rilasciare una versione, visualizzare le versioni ed eseguire il rollback a una versione precedente. Helm usa il controllo delle versioni semantiche per tenere traccia degli aggiornamenti e delle revisioni.
  • I chart Helm usano modelli per evitare la duplicazione di informazioni, ad esempio etichette e selettori, in molti file.
  • Helm può gestire le dipendenze tra i grafici.
  • È possibile archiviare i grafici in un repository Helm, ad esempio Registro Contenitori, e integrarli nella pipeline di compilazione.

Per altre informazioni, vedere Usare Registro Container come repository Helm per i grafici delle applicazioni.

Un singolo microservizio potrebbe richiedere più file di configurazione kubernetes. Per aggiornare un servizio, potrebbe essere necessario modificare tutti questi file per aggiornare selettori, etichette e tag di immagine. Helm considera questi file come un singolo pacchetto denominato grafico e semplifica l'aggiornamento dei file YAML usando le variabili. Helm usa un linguaggio modello (basato sui modelli Go) che consente di scrivere file di configurazione YAML con parametri.

Ad esempio, ecco una parte di un file YAML che definisce una distribuzione:

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:
  template:
    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 }}

È possibile notare che il nome della distribuzione, le etichette e la specifica del contenitore usano tutti i parametri del modello, forniti in fase di distribuzione. Ad esempio, dalla riga di comando:

helm install <release-name> oci://<registry>/<repository>/<package-chart-name> --version <desiredVersion> \
     --set image.tag=0.1.0 \
     --set image.repository=package \
     --set dockerregistry=$ACR_SERVER \
     --namespace backend

Anche se una pipeline CI/CD può installare un grafico direttamente in Kubernetes, creare un archivio grafico (file con estensione tgz) ed eseguire il push del grafico in un repository Helm, ad esempio Registro Container. Per altre informazioni, vedere Creare un pacchetto e distribuire grafici Helm (attività HelmDeploy).

Revisions

I grafici Helm hanno sempre un numero di versione che deve usare il controllo delle versioni semantico. Un grafico può anche avere un oggetto appVersion. Questo campo è facoltativo e non deve essere correlato alla versione del grafico. Alcuni team potrebbero voler eseguire la versione delle applicazioni separatamente dagli aggiornamenti ai grafici. Un approccio più semplice consiste nell'usare un numero di versione, quindi esiste una relazione 1:1 tra la versione del grafico e la versione dell'applicazione. In questo modo, è possibile archiviare un grafico per versione e distribuire facilmente la versione desiderata:

helm install <package-chart-name> --version <desiredVersion>

Un'altra procedura consigliata consiste nel fornire un'annotazione della causa della modifica nel modello di distribuzione:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "delivery.fullname" . | replace "." "" }}
  labels:
     ...
  annotations:
    kubernetes.io/change-cause: {{ .Values.reason }}

Questa annotazione consente di visualizzare il campo della causa delle modifiche per ogni revisione usando il kubectl rollout history comando . Nell'esempio precedente, la causa della modifica viene fornita come parametro del grafico Helm.

kubectl rollout history deployments/delivery-v010 -n backend
deployment.apps/delivery-v010
REVISION  CHANGE-CAUSE
1         Initial deployment

È anche possibile usare il helm list comando per visualizzare la cronologia delle revisioni:

helm list -n backend
NAME              NAMESPACE   REVISION    UPDATED                                 STATUS      CHART               APP VERSION
delivery-v0.1.0   backend     1           2024-04-07 00:25:30.000000 +0000 UTC    deployed    delivery-v0.1.0     v0.1.0

Azure Pipelines

Azure Pipelines, un servizio in Azure DevOps, ha due tipi di pipeline: pipeline di compilazione e pipeline di versione. La pipeline di compilazione esegue il processo di integrazione continua e crea artefatti di compilazione. Per un'architettura di microservizi in Kubernetes, questi artefatti sono le immagini del contenitore e i grafici Helm che definiscono ogni microservizio. La pipeline di versione esegue il processo cd che distribuisce un microservizio in un cluster.

In base al flusso di integrazione continua descritto in precedenza in questo articolo, una pipeline di build può essere costituita dalle seguenti attività.

  1. Compilare il contenitore dello strumento di esecuzione di test usando l'attività Docker .
  2. Eseguire i test richiamando docker run il contenitore dello strumento di esecuzione di test usando l'attività Docker .
  3. Pubblicare i risultati del test usando l'attività PublishTestResults . Per altre informazioni, vedere Creare un'immagine.
  4. Eseguire SAST nel codice sorgente.
  5. Compilare il contenitore di runtime usando l'attività locale docker build e l'attività Docker oppure usando le compilazioni e l'attività AzureCLI del Registro Container. Generare una fattura software di materiali (SBOM) per l'immagine, ad esempio usando lo strumento SBOM Microsoft e pubblicarlo come artefatto della pipeline associato al digest dell'immagine o collegarlo all'immagine in Registro Contenitori.
  6. Eseguire l'analisi delle vulnerabilità delle immagini del contenitore (ad esempio, usando Microsoft Defender per contenitori o uno strumento non Microsoft come Trivy) per rilevare le vulnerabilità note prima della pubblicazione dell'immagine.
  7. Eseguire il push dell'immagine del contenitore in Registro Contenitori (o in un altro registro contenitori) usando l'attività Docker o AzureCLI .
  8. Firmare l'immagine di cui è stato eseguito il push tramite digest non modificabile per garantire l'integrità e l'autenticità. Per Azure Pipelines, seguire le indicazioni sulla firma della notazione.
  9. Creare il pacchetto del grafico Helm usando l'attività HelmDeploy .
  10. Eseguire il push del pacchetto Helm in Registro Container (o in un altro repository Helm) usando l'attività HelmDeploy .

L'output della pipeline CI è un'immagine del contenitore pronta per la produzione e un grafico Helm aggiornato per il microservizio. A questo punto, la pipeline di rilascio può assumere il controllo. È disponibile una pipeline di versione univoca per ogni microservizio. La pipeline di rilascio è configurata per avere un'origine di attivazione impostata sulla pipeline CI che ha pubblicato l'artefatto. Questa pipeline consente di distribuire ogni microservizio in modo indipendente. Il flusso di rilascio esegue i seguenti passaggi:

  1. Distribuire il chart Helm negli ambienti di sviluppo/QA/staging. È possibile usare il helm upgrade comando con il --install flag per supportare la prima installazione e gli aggiornamenti successivi.
  2. Attendere che un approvatore esprima la sua approvazione o rifiuti la distribuzione.
  3. Ripetere il tag dell'immagine del contenitore per il rilascio.
  4. Pubblica il tag di rilascio nel registro dei contenitori.
  5. Distribuire il grafico Helm nel cluster di produzione. Usare la ratifica con Criteri di Azure per convalidare le firme delle immagini durante l'ammissione e configurare separatamente un criterio per le immagini consentite per limitare le immagini ai registri attendibili.

Note

Per consentire al servizio Azure Kubernetes di eseguire il pull delle immagini dal Registro Container senza segreti pull di immagini separati, usare l'integrazione del servizio Azure Kubernetes perContainer-Registry per un registro che usa il controllo degli accessi in base al ruolo a livello di registro. Per un Registro di sistema abilitato per il controllo degli accessi in base al ruolo, questa integrazione non è supportata. Assegnare invece il Container Registry Repository Reader ruolo all'identità kubelet-managed del cluster.

Per altre informazioni sulla creazione di una pipeline di versione, vedere Pipeline di versione, versioni bozza e opzioni di rilascio.

Il diagramma seguente illustra il processo CI/CD end-to-end descritto in questo articolo:

Diagramma della pipeline CI/CD end-to-end.

Il diagramma mostra la pipeline CI/CD completa da un commit dello sviluppatore tramite la distribuzione di produzione. A sinistra inizia con uno sviluppatore che esegue il push di un commit in un repository Git. Il repository Git attiva la prima pipeline di integrazione continua. La pipeline CI contiene sei passaggi sequenziali: codice di compilazione, esecuzione di unit test, immagine di compilazione, immagine push, pacchetto Helm e grafico push. Ognuno dei primi tre passaggi ha una casella di output corrispondente a destra: il codice di compilazione produce artefatti di codice, l'esecuzione di unit test produce risultati di test e l'immagine di compilazione produce un'immagine del contenitore. Il passaggio Push image esegue il push dell'immagine in Registro Contenitori. Il passaggio del pacchetto Helm genera un file di archivio grafico. Il passaggio Push chart esegue il push del grafico in un repository Helm. Una seconda pipeline di integrazione continua è sotto la prima. Contiene cinque passaggi sequenziali: eseguire la distribuzione in qa, test di integrazione, immagine retag, immagine push e distribuzione nell'ambiente di produzione. Dal passaggio Distribuisci al controllo di qualità, un aggiornamento Helm con etichetta riga porta a un cluster Kubernetes con etichetta test/QA. A sinistra, sotto lo sviluppatore, una riga conduce da un'icona del responsabile approvazione del controllo di qualità al passaggio Distribuisci in produzione. Questa riga è contrassegnata come approvazione. Dal passaggio Distribuisci in produzione, un aggiornamento Helm con etichetta riga porta a un secondo cluster Kubernetes con etichetta cluster di produzione.

GitHub Actions alternativa

Se il team usa GitHub per il controllo del codice sorgente, GitHub Actions fornisce una piattaforma CI/CD equivalente. Oltre ai flussi di lavoro iniziali e all'autenticazione OIDC annotati in precedenza, prendere in considerazione queste funzionalità specifiche di GitHub quando si ha come destinazione il servizio Azure Kubernetes:

  • Ambienti e regole di protezione. Se il piano di GitHub e la visibilità del repository li supportano, usare gli ambienti GitHub con revisori, timer di attesa e rami di distribuzione necessari per implementare i controlli di approvazione.
  • Analisi dei contenitori. Usare GitHub Actions azioni del marketplace per Trivy, Microsoft Defender per DevOps o strumenti simili per l'analisi delle immagini del contenitore direttamente nel flusso di lavoro.

Osservabilità e monitoraggio

Implementare l'osservabilità nell'intera pipeline CI/CD e nell'ambiente di runtime:

  • Monitoraggio della pipeline. Tenere traccia delle durate di compilazione, dei test superati, della frequenza di distribuzione e delle percentuali di errore. Azure DevOps offre analisi predefinite. GitHub Actions possono usare dashboard non Microsoft.
  • Monitoraggio del runtime. Usare Monitoraggio di Azure servizio gestito per Prometheus e Grafana con gestione Azure per monitorare l'integrità del cluster del servizio Azure Kubernetes e le metriche del carico di lavoro.
  • Telemetria dell'applicazione. Instrumentare i microservizi con Monitoraggio di Azure Application Insights per la traccia distribuita, la registrazione delle richieste e il rilevamento delle dipendenze.
  • Creazione di avvisi. Configurare gli avvisi per gli errori di distribuzione, i riavvii dei pod, le percentuali di errore elevate e la saturazione delle risorse per abilitare la risposta rapida agli eventi imprevisti.

allineamento del framework Well-Architected

Quando si progetta la pipeline CI/CD per i microservizi in Kubernetes, considerare i pilastri di Azure Well-Architected Framework:

Pilastro Considerations
Affidabilità Strategie di rollback automatizzate, probe di integrità nelle distribuzioni, strategie di rilascio blu-verde o canary, budget di interruzione dei pod.
Security Autenticazione senza segreto (ID carico di lavoro, OIDC), firma delle immagini, sicurezza della supply chain, controllo degli accessi in base al ruolo con privilegi minimi, criteri di rete.
Ottimizzazione costi Ridimensionamento corretto degli agenti di compilazione, usando strumenti di esecuzione temporanei self-hosted, implementazione dei criteri di conservazione delle immagini del Registro Container e uso di pool di nodi spot solo per carichi di lavoro non di produzione a tolleranza di interruzione.
Eccellenza operativa GitOps per distribuzioni dichiarative, infrastruttura come codice, pipeline come codice (YAML), osservabilità e automazione dei runbook.
efficienza delle prestazioni Fasi della pipeline parallele, memorizzazione nella cache di compilazione (memorizzazione nella cache dei livelli Docker, memorizzazione nella cache delle dipendenze), scalabilità automatica orizzontale dei pod.

Contributori

Microsoft mantiene questo articolo. I seguenti collaboratori hanno scritto questo articolo.

Autore principale:

  • Ray Kao | Principal Solutions Engineer

Per visualizzare i profili LinkedIn non pubblici, accedere a LinkedIn.

Passaggi successivi