Condividi tramite


Creazione di pipeline di distribuzione personalizzate (anteprima)

Aspire offre API avanzate per la creazione di immagini del contenitore dalle risorse durante le operazioni di pubblicazione e distribuzione. Questo articolo illustra i componenti chiave che consentono la creazione di immagini del contenitore a livello di codice e la creazione di report sullo stato di avanzamento.

Informazioni generali

Durante la pubblicazione e la distribuzione, il generatore di immagini del contenitore è disponibile per creare immagini per le risorse necessarie. Aspire usa questo generatore quando una risorsa richiede un'immagine del contenitore, ad esempio durante la pubblicazione con Docker Compose. Il processo prevede due componenti principali:

  • IResourceContainerImageBuilder: servizio che trasforma le definizioni delle risorse in immagini contenitore eseguibili.
  • IPipelineActivityReporter: l'API che fornisce report sullo stato di avanzamento strutturati durante le operazioni a esecuzione prolungata.

Queste API offrono un controllo granulare sul processo di compilazione delle immagini e forniscono feedback in tempo reale agli utenti durante operazioni di compilazione lunghe.

Importante

Queste API sono attualmente in anteprima e soggette a modifiche. Sono progettati per scenari avanzati in cui è necessario un controllo personalizzato sulla creazione di immagini del contenitore e sulla creazione di report sullo stato di avanzamento. Per eliminare gli avvisi per queste API, vedere Errore del compilatore ASPIREPUBLISHERS001.

Quando usare queste API

Prendere in considerazione l'uso delle API di creazione delle immagini e di generazione dei report di avanzamento del container in questi scenari:

  • Destinazioni di distribuzione personalizzate: quando è necessario eseguire la distribuzione in piattaforme che richiedono formati di immagine specifici o configurazioni di compilazione.
  • Pipeline di compilazione complesse: quando il processo di pubblicazione prevede più passaggi che gli utenti dovrebbero vedere.
  • Scenari aziendali: quando è necessario creare report sullo stato di avanzamento personalizzati per l'integrazione con sistemi o dashboard CI/CD.
  • Tipi di risorse personalizzati: quando si implementano risorse personalizzate che devono partecipare al processo di pubblicazione e distribuzione.

Annotazioni

Per la maggior parte delle applicazioni standard Aspire , il processo di pubblicazione predefinito compila automaticamente immagini del contenitore senza richiedere queste API.

API del generatore di immagini del contenitore di risorse

IResourceContainerImageBuilder è il servizio principale nel Aspire.Hosting.Publishing livello che converte le definizioni delle risorse in immagini del contenitore. Analizza ogni risorsa nel modello di applicazione distribuita e determina se:

  • Riutilizzare un'immagine esistente.
  • Compilare da un .NET progetto usando dotnet publish /t:PublishContainer.
  • Compilare da un Dockerfile usando il runtime del contenitore locale.

Opzioni di build del contenitore

La ContainerBuildOptions classe fornisce una configurazione fortemente tipizzata per le compilazioni di contenitori. Questa classe consente di specificare:

  • Formato immagine: Docker o Formato OCI (Open Container Initiative).
  • Piattaforma di destinazione: Linux x64, Windows, ARM64 e così via.
  • Percorso di output: dove salvare le immagini compilate.

Verifiche di integrità del runtime del contenitore

Il generatore esegue controlli di integrità del runtime del contenitore (Docker/Podman) solo quando almeno una risorsa richiede una Dockerfile compilazione. Questa modifica elimina gli errori falsi positivi nei progetti che pubblicano direttamente dagli .NET assembly. Se il runtime del contenitore è necessario ma malfunzionante, il compilatore genera un'eccezione esplicita InvalidOperationException in modo precoce per far emergere il problema.

API del reporter dell'attività della pipeline

L'API PipelineActivityReporter permette la creazione di report strutturati sullo stato di avanzamento durante i comandi aspire publish e aspire deploy. Ciò riduce l'incertezza durante le operazioni a esecuzione prolungata e presenta in anticipo gli errori.

Panoramica e comportamento delle API

Il reporter di avanzamento utilizza un modello gerarchico con ordinamento garantito e operazioni sicure per i thread.

Concetto Description Rendering dell'interfaccia della riga di comando Comportamento
Step Fase di primo livello, ad esempio "Compilare immagini" o "Distribuire carichi di lavoro". Messaggio di fase con glifi di stato e tempo trascorso. Forma una struttura ad albero rigorosa; i passaggi annidati non sono supportati. I passaggi vengono creati automaticamente durante l'esecuzione della pipeline.
Attività Unità di lavoro discreta annidata in un passaggio. Messaggio di attività con rientro. Appartiene a un singolo passaggio; supporta la creazione parallela con l'ordinamento di completamento deterministico.
Stato di completamento Stato finale: Completed, Warningo Error. ✅ (Completato), ⚠️ (avviso), ❌ (errore) Ogni passaggio/attività passa esattamente una volta a uno stato finale.

Struttura e utilizzo delle API

L'API reporter fornisce accesso strutturato alla creazione di report sullo stato di avanzamento con le caratteristiche seguenti:

  • Acquisizione: recuperata da PublishingContext.ActivityReporter o tramite la proprietà nei passaggi della PipelineStepContext.ReportingStep pipeline.
  • Creazione dei passaggi: i passaggi vengono ora creati automaticamente durante l'esecuzione della pipeline. Il CreateStepAsync(title, ct) metodo restituisce un oggetto IReportingStep.
  • Creazione di attività: IReportingStep.CreateTaskAsync(title, ct) restituisce un oggetto IReportingTask.
  • Transizioni di stato: SucceedAsync, WarnAsynci FailAsync metodi accettano un messaggio di riepilogo.
  • Completamento: CompletePublishAsync(message, state, isDeploy, ct) contrassegna l'intera operazione.
  • Ordinamento: gli eventi di creazione e completamento mantengono l'ordine di chiamata; gli aggiornamenti vengono serializzati.
  • Annullamento: tutte le API accettano CancellationToken e propagano l'annullamento all'interfaccia della riga di comando.
  • Contratto di smaltimento: i passaggi di smaltimento vengono completati automaticamente se incompleti, impedendo le fasi orfane.

Esempio: Compilare immagini del contenitore e segnalare lo stato di avanzamento

Per usare queste API, aggiungere un PublishingCallbackAnnotation, un DeployingCallbackAnnotation, o entrambi a una risorsa nel modello di app. È possibile annotare risorse personalizzate (o predefinite) aggiungendo annotazioni alla IResource.Annotations raccolta.

Gli sviluppatori possono scegliere di:

  • Usare entrambe le annotazioni se la risorsa deve eseguire operazioni sia nella pubblicazione che nella distribuzione. Ad esempio, compilare immagini e generare manifesti durante la pubblicazione, quindi eseguire il push delle immagini o configurare le destinazioni di distribuzione durante la distribuzione. La pubblicazione avviene sempre prima della distribuzione, in modo da poter mantenere separata la logica per ogni fase.

  • Usare solo PublishingCallbackAnnotation se la risorsa deve eseguire operazioni solo durante la pubblicazione. Questa operazione è comune quando è sufficiente compilare artefatti o immagini, ma non è necessario eseguire alcuna operazione durante la distribuzione.

  • Usare solo DeployingCallbackAnnotation se la risorsa deve eseguire operazioni solo durante la distribuzione. Questo si adatta ai casi in cui si usano immagini predefinite ed è sufficiente distribuirle o configurarle.

Scegliere una o più annotazioni che corrispondono alle responsabilità della risorsa per mantenere il modello di applicazione chiaro e gestibile. Questa separazione consente di poter definire chiaramente la logica per ogni fase, ma è possibile usare sia il rilevatore di attività che il builder delle immagini del contenitore di risorse in entrambi i callback secondo necessità.

Risorsa di esempio con annotazioni

Si consideri ad esempio il ComputeEnvironmentResource costruttore:

public ComputeEnvironmentResource(string name) : base(name)
{
    Annotations.Add(new PublishingCallbackAnnotation(PublishAsync));
    Annotations.Add(new DeployingCallbackAnnotation(DeployAsync));
}

Quando viene creata un'istanza, definisce sia una pubblicazione che un'annotazione di callback.

Dato il tipo di esempio ComputeEnvironmentResource (Resource), si supponga di avere un metodo di estensione che consente ai consumatori di aggiungere l'ambiente di calcolo.

using System.Diagnostics.CodeAnalysis;

[Experimental("ASPIRECOMPUTE001")]
public static class ComputeEnvironmentResourceExtensions
{
    public static IResourceBuilder<ComputeEnvironmentResource> AddComputeEnvironment(
        this IDistributedApplicationBuilder builder,
        [ResourceName] string name)
    {
        var resource = new ComputeEnvironmentResource(name);

        return builder.AddResource(resource);
    }
}

Il codice precedente:

  • Definisce un metodo di estensione in IDistributedApplicationBuilder.
  • Accetta un name come oggetto per la risorsa dell'ambiente di calcolo, protetto da ResourceNameAttribute.
  • Crea un'istanza di ComputeEnvironmentResource dato il name e lo aggiunge all'builder.

Esempio di AppHost

Nel tuo AppHost, puoi aggiungere il ComputeEnvironmentResource al modello di applicazione come questo:

var builder = DistributedApplication.CreateBuilder(args);

var cache = builder.AddRedis("redis");

builder.AddProject<Projects.Api>("api")
       .WithReference(cache);

builder.AddComputeEnvironment("compute-env");

builder.Build().Run();

Il codice precedente usa il AddComputeEnvironment metodo di estensione per aggiungere l'oggetto ComputeEnvironmentResource al modello di applicazione.

Pubblicazione dell'annotazione del callback

Quando si aggiunge ComputeEnvironmentResource, registra un oggetto PublishingCallbackAnnotation. Il callback usa il PublishAsync metodo :

private static async Task PublishAsync(PublishingContext context)
{
    var reporter = context.ActivityReporter;
    var imageBuilder = context.Services.GetRequiredService<IResourceContainerImageBuilder>();

    // Build container images for all project resources in the application
    await using (var buildStep = await reporter.CreateStepAsync(
        "Build container images", context.CancellationToken))
    {
        // Find all resources that need container images
        var projectResources = context.Model.Resources
            .OfType<ProjectResource>()
            .ToList();

        if (projectResources.Count > 0)
        {
            // Configure how images should be built
            var buildOptions = new ContainerBuildOptions
            {
                ImageFormat = ContainerImageFormat.Oci,
                TargetPlatform = ContainerTargetPlatform.LinuxAmd64,
                OutputPath = Path.Combine(context.OutputPath, "images")
            };

            var buildTask = await buildStep.CreateTaskAsync(
                $"Building {projectResources.Count} container image(s)", context.CancellationToken);

            // Build all the container images
            await imageBuilder.BuildImagesAsync(
                projectResources, buildOptions, context.CancellationToken);

            await buildTask.SucceedAsync(
                $"Built {projectResources.Count} image(s) successfully", context.CancellationToken);
        }
        else
        {
            var skipTask = await buildStep.CreateTaskAsync(
                "No container images to build", context.CancellationToken);
                
            await skipTask.SucceedAsync("Skipped - no project resources found", context.CancellationToken);
        }

        await buildStep.SucceedAsync("Container image build completed", context.CancellationToken);
    }

    // Generate deployment manifests
    await using (var manifestStep = await reporter.CreateStepAsync(
        "Generate deployment manifests", context.CancellationToken))
    {
        var bicepTask = await manifestStep.CreateTaskAsync(
            "Write main.bicep", context.CancellationToken);

        // Write file to context.OutputPath …
        await bicepTask.SucceedAsync(
            $"main.bicep at {context.OutputPath}", context.CancellationToken);

        await manifestStep.SucceedAsync("Manifests ready", context.CancellationToken);
    }

    // Complete the publishing operation
    await reporter.CompletePublishAsync(
        completionMessage: "Publishing pipeline completed successfully",
        completionState: CompletionState.Completed,
        cancellationToken: context.CancellationToken);
}

Il codice precedente:

  • Implementa una pipeline di pubblicazione che costruisce immagini contenitore e genera manifesti di distribuzione.
  • Usa l'API IResourceContainerImageBuilder per compilare immagini del contenitore.
  • Segnala lo stato di avanzamento e completamento usando l'API PipelineActivityReporter .

Il callback di pubblicazione potrebbe usare IResourceContainerImageBuilder per compilare immagini del contenitore, mentre il callback della distribuzione potrebbe usare le immagini compilate ed eseguirne il push in un registro o in una destinazione di distribuzione.

Implementazione dell'annotazione di callback

Analogamente al callback di pubblicazione, il callback di distribuzione viene registrato usando DeployingCallbackAnnotation e chiama il DeployAsync metodo :

private static async Task DeployAsync(DeployingContext context)
{
    var reporter = context.ActivityReporter;

    await using (var deployStep = await reporter.CreateStepAsync(
        "Deploy to target environment", context.CancellationToken))
    {
        var applyTask = await deployStep.CreateTaskAsync(
            "Apply Kubernetes manifests", context.CancellationToken);

        // Simulate deploying to Kubernetes cluster
        await Task.Delay(1_000, context.CancellationToken);

        await applyTask.SucceedAsync("All workloads deployed", context.CancellationToken);
        await deployStep.SucceedAsync("Deployment to cluster completed", context.CancellationToken);
    }

    // Complete the deployment operation
    await reporter.CompletePublishAsync(
        completionMessage: "Deployment completed successfully",
        completionState: CompletionState.Completed,
        isDeploy: true,
        cancellationToken: context.CancellationToken);
}

Il codice precedente:

  • Simula la distribuzione dei carichi di lavoro in un Kubernetes cluster.
  • Usa l'API PipelineActivityReporter per creare e gestire i passaggi e le attività di distribuzione.
  • Segnala lo stato di avanzamento e contrassegna ogni fase di distribuzione come completata.
  • Completa l'operazione di distribuzione con un aggiornamento dello stato finale.
  • Gestisce l'annullamento tramite l'oggetto fornito CancellationToken.

Procedure consigliate

Quando si usano queste API, seguire queste linee guida:

Creazione di immagini

  • Specificare sempre esplicitamente ContainerBuildOptions per gli scenari di produzione.
  • Prendere in considerazione i requisiti della piattaforma di destinazione durante la preparazione per la distribuzione.
  • Usare il formato OCI per garantire la massima compatibilità con i registri contenitori.
  • Gestire InvalidOperationException quando le verifiche di integrità del runtime del contenitore hanno esito negativo.

Relazione sullo stato di avanzamento

  • Incapsulare le fasi logiche a lungo termine nei passi anziché emettere attività grezze.
  • Mantenere concisi i titoli (sotto 60 caratteri) perché l'interfaccia della riga di comando tronca stringhe più lunghe.
  • Chiamare CompletePublishAsync esattamente una volta per operazione di pubblicazione o distribuzione.
  • Considerare gli avvisi come ripristinabili e consentire ai passaggi successivi di procedere.
  • Considerare gli errori come fatali e fallire rapidamente con una diagnostica chiara.
  • Usare operazioni asincrone con riconoscimento dell'annullamento per evitare di bloccare l'elaborazione degli eventi.

Gestione dello stato

  • Ogni passaggio e attività viene avviato nello stato In esecuzione e passa esattamente una volta a Completato, Avviso o Errore.
  • Generare un'eccezione quando si tentano più transizioni di stato.
  • Sfrutta il reporter per garantire eventi ordinati e impedire l'interferenza.
  • Smaltisci IPublishingActivityStep per completare automaticamente i passaggi non completati.

Vedere anche