Personalizzare i contenitori Docker in Visual Studio

È possibile personalizzare le immagini del contenitore modificando il Dockerfile generato da Visual Studio quando si aggiunge il supporto Docker al progetto. Sia che si crei un contenitore personalizzato dall'IDE di Visual Studio o si stia configurando una compilazione da riga di comando, è necessario sapere come Visual Studio usa il Dockerfile per compilare i progetti. È necessario conoscere questi dettagli perché, per motivi di prestazioni, Visual Studio segue un processo speciale per la compilazione e l'esecuzione di app in contenitori che non sono evidenti dal Dockerfile.

Si supponga di voler apportare una modifica nel Dockerfile e visualizzare i risultati sia nel debug che nei contenitori di produzione. In tal caso, è possibile aggiungere comandi nel Dockerfile per modificare la prima fase (in genere base). Vedere Modificare l'immagine del contenitore per il debug e l'ambiente di produzione. Tuttavia, se si vuole apportare una modifica solo quando si esegue il debug, ma non in produzione, è necessario creare un'altra fase e usare l'impostazione DockerfileFastModeStage di compilazione per indicare a Visual Studio di usare tale fase per le compilazioni di debug. Vedere Modificare l'immagine del contenitore solo per il debug.

Questo articolo illustra in dettaglio il processo di compilazione di Visual Studio per le app in contenitori, quindi contiene informazioni su come modificare il Dockerfile in modo da influire sia sul debug che sulle compilazioni di produzione o solo per il debug.

Compilazioni Dockerfile in Visual Studio

Nota

Questa sezione descrive il processo di compilazione del contenitore usato da Visual Studio quando si sceglie il tipo di compilazione del contenitore Dockerfile. Se si usa il tipo di compilazione .NET SDK, le opzioni di personalizzazione sono diverse e le informazioni contenute in questa sezione non sono applicabili. Vedere invece Containerize a .NET app with dotnet publish and use the properties described in Customize your container to configure the container build process (Personalizzare il contenitore per configurare il processo di compilazione del contenitore ).

Compilazione a più fattori

Quando Visual Studio compila un progetto che non usa contenitori Docker, richiama MSBuild nel computer locale e genera i file di output in una cartella (in bingenere ) nella cartella della soluzione locale. Per un progetto in contenitori, tuttavia, il processo di compilazione tiene conto delle istruzioni del Dockerfile per la compilazione dell'app in contenitori. Il Dockerfile usato da Visual Studio è suddiviso in più fasi. Questo processo si basa sulla funzionalità di compilazione a più fasi di Docker.

La funzionalità di compilazione a più fasi consente di rendere più efficiente il processo di compilazione dei contenitori e rende i contenitori più piccoli consentendo loro di contenere solo i bit necessari per l'app in fase di esecuzione. La compilazione a più istanze viene usata per i progetti .NET Core, non per i progetti .NET Framework.

La compilazione a più fasi consente di creare immagini del contenitore in fasi che producono immagini intermedie. Si consideri ad esempio un Dockerfile tipico. La prima fase viene chiamata base nel Dockerfile generato da Visual Studio, anche se gli strumenti non richiedono tale nome.

FROM mcr.microsoft.com/dotnet/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

Le righe nel Dockerfile iniziano con l'immagine ASP.NET da Registro Contenitori Microsoft (mcr.microsoft.com) e creano un'immagine base intermedia che espone le porte 80 e 443 e imposta la directory di lavoro su /app.

La fase successiva è build, che viene visualizzata come segue:

FROM mcr.microsoft.com/dotnet/sdk:3.1-buster-slim AS build
WORKDIR /src
COPY ["WebApplication43/WebApplication43.csproj", "WebApplication43/"]
RUN dotnet restore "WebApplication43/WebApplication43.csproj"
COPY . .
WORKDIR "/src/WebApplication43"
RUN dotnet build "WebApplication43.csproj" -c Release -o /app/build

È possibile notare che la build fase inizia da un'immagine originale diversa dal Registro di aspnetsistema (sdk anziché ), anziché continuare dalla base. L'immagine sdk ha tutti gli strumenti di compilazione e per questo motivo è molto più grande dell'immagine aspnet, che contiene solo i componenti di runtime. Il motivo dell'uso di un'immagine separata diventa chiaro quando si esamina il resto del Dockerfile:

FROM build AS publish
RUN dotnet publish "WebApplication43.csproj" -c Release -o /app/publish

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

La fase finale inizia di nuovo da basee include per COPY --from=publish copiare l'output pubblicato nell'immagine finale. Questo processo rende possibile che l'immagine finale sia molto più piccola, poiché non è necessario includere tutti gli strumenti di compilazione presenti nell'immagine sdk .

MSBuild

Nota

Questa sezione descrive come personalizzare i contenitori Docker quando si sceglie il tipo di compilazione del contenitore Dockerfile. Se si usa il tipo di compilazione .NET SDK, le opzioni di personalizzazione sono diverse e le informazioni contenute in questo articolo non sono applicabili. Vedere invece Containerize a .NET app with dotnet publish (Containerize a .NET app with dotnet publish).

I Dockerfile creati da Visual Studio per i progetti .NET Framework (e per i progetti .NET Core creati con versioni di Visual Studio precedenti a Visual Studio 2017 Update 4) non sono dockerfile a più passaggi. I passaggi in questi Dockerfile non compilano il codice. Al contrario, quando Visual Studio compila un Dockerfile di .NET Framework, compila prima il progetto usando MSBuild. Quando l'operazione riesce, Visual Studio compila quindi il Dockerfile, che copia semplicemente l'output di compilazione da MSBuild nell'immagine Docker risultante. Poiché i passaggi per compilare il codice non sono inclusi nel Dockerfile, non è possibile compilare dockerfile .NET Framework usando docker build dalla riga di comando. È consigliabile usare MSBuild per compilare questi progetti.

Per compilare un'immagine per un singolo progetto contenitore Docker, è possibile usare MSBuild con l'opzione di /t:ContainerBuild comando . Questo comando indica a MSBuild di compilare la destinazione ContainerBuild anziché la destinazione Buildpredefinita. Ad esempio:

MSBuild MyProject.csproj /t:ContainerBuild /p:Configuration=Release

Quando si compila la soluzione dall'IDE di Visual Studio, viene visualizzato un output simile a quello visualizzato nella finestra Output . Usare /p:Configuration=Releasesempre , poiché nei casi in cui Visual Studio usa l'ottimizzazione della compilazione a più scenari, i risultati durante la compilazione della configurazione di debug potrebbero non essere come previsto. Vedere Debug.

Se si usa un progetto Docker Compose, usare questo comando per compilare immagini:

msbuild /p:SolutionPath=<solution-name>.sln /p:Configuration=Release docker-compose.dcproj

Debug

Nota

Questa sezione descrive come personalizzare i contenitori Docker quando si sceglie il tipo di compilazione del contenitore Dockerfile. Se si usa il tipo di compilazione .NET SDK, le opzioni di personalizzazione sono diverse e la maggior parte delle informazioni in questa sezione non è applicabile. Vedere invece Containerize a .NET app with dotnet publish (Containerize a .NET app with dotnet publish).

Quando si esegue la compilazione nella configurazione di debug , Visual Studio offre diverse ottimizzazioni che consentono di ottenere prestazioni del processo di compilazione per i progetti in contenitori. Il processo di compilazione per le app in contenitori non è semplice come seguire semplicemente i passaggi descritti nel Dockerfile. La compilazione in un contenitore è più lenta rispetto alla compilazione nel computer locale. Quindi, quando si compila nella configurazione di debug , Visual Studio compila effettivamente i progetti nel computer locale e quindi condivide la cartella di output nel contenitore usando il montaggio del volume. Una compilazione con questa ottimizzazione abilitata è denominata compilazione in modalità rapida .

In modalità Veloce , Visual Studio chiama docker build con un argomento che indica a Docker di compilare solo la prima fase nel Dockerfile (in genere la base fase). È possibile modificarlo impostando la proprietà MSBuild, DockerfileFastModeStage, descritta in Strumenti contenitori proprietà MSBuild. Visual Studio gestisce il resto del processo senza considerare il contenuto del Dockerfile. Pertanto, quando si modifica il Dockerfile, ad esempio per personalizzare l'ambiente contenitore o installare dipendenze aggiuntive, è necessario inserire le modifiche nella prima fase. Tutti i passaggi personalizzati inseriti nelle fasi , publisho final di buildDockerfile non vengono eseguiti.

Questa ottimizzazione delle prestazioni si verifica solo quando si compila nella configurazione di debug . Nella configurazione release la compilazione viene eseguita nel contenitore come specificato nel Dockerfile.

Se si vuole disabilitare l'ottimizzazione delle prestazioni e la compilazione come specificato dal Dockerfile, impostare la proprietà ContainerDevelopmentMode su Regular nel file di progetto come indicato di seguito:

<PropertyGroup>
   <ContainerDevelopmentMode>Regular</ContainerDevelopmentMode>
</PropertyGroup>

Per ripristinare l'ottimizzazione delle prestazioni, rimuovere la proprietà dal file di progetto.

Quando si avvia il debug (F5), un contenitore avviato in precedenza viene riutilizzato, se possibile. Se non si vuole riutilizzare il contenitore precedente, è possibile usare i comandi Ricompila o Pulisci in Visual Studio per forzare Visual Studio a usare un nuovo contenitore.

Il processo di esecuzione del debugger dipende dal tipo di progetto e dal sistema operativo del contenitore:

Scenario Processo del debugger
App .NET Core (contenitori Linux) Visual Studio scarica vsdbg ed esegue il mapping al contenitore, quindi viene chiamato con il programma e gli argomenti (ovvero dotnet webapp.dll), e quindi il debugger si collega al processo.
App .NET Core (contenitori di Windows) Visual Studio usa onecoremsvsmon e ne esegue il mapping al contenitore, lo esegue come punto di ingresso e quindi Visual Studio si connette e si connette al programma. È simile a come si configura normalmente il debug remoto in un altro computer o in un'altra macchina virtuale.
App .NET Framework Visual Studio usa msvsmon e ne esegue il mapping al contenitore, lo esegue come parte del punto di ingresso in cui Visual Studio può connettersi e collega al programma.

Per informazioni su vsdbg.exe, vedere Debug offroad di .NET Core in Linux e OS X da Visual Studio.

Modificare l'immagine del contenitore per il debug e la produzione

Per modificare l'immagine del contenitore sia per il debug che per la produzione, modificare la base fase. Aggiungere le personalizzazioni al Dockerfile nella sezione della fase di base, in genere la prima sezione del Dockerfile. Per informazioni sui comandi Dockerfile, vedere le informazioni di riferimento sul Dockerfile nella documentazione di Dockerfile.

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
# <add your commands here>

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["WebApplication3/WebApplication3.csproj", "WebApplication3/"]
RUN dotnet restore "WebApplication3/WebApplication3.csproj"
COPY . .
WORKDIR "/src/WebApplication3"
RUN dotnet build "WebApplication3.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "WebApplication3.csproj" -c Release -o /app/publish

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

Modificare l'immagine del contenitore solo per il debug

Questo scenario si applica quando si vuole eseguire operazioni con i contenitori per facilitare il processo di debug, ad esempio l'installazione di un elemento a scopo di diagnostica, ma non si vuole che sia installato nelle compilazioni di produzione.

Per modificare il contenitore solo per il debug, creare una fase e quindi usare la proprietà DockerfileFastModeStage MSBuild per indicare a Visual Studio di usare la fase personalizzata durante il debug. Per informazioni sui comandi Dockerfile, vedere le informazioni di riferimento sul Dockerfile nella documentazione di Dockerfile.

Nell'esempio seguente viene installato il pacchetto procps-ng, ma solo in modalità di debug. Questo pacchetto fornisce il comando pidof, che Visual Studio richiede ma non è nell'immagine Mariner usata qui. La fase usata per il debug in modalità rapida è debug, una fase personalizzata definita qui. La fase in modalità rapida non deve ereditare dalla build fase o publish , può ereditare direttamente dalla base fase, perché Visual Studio monta un volume che contiene tutti gli elementi necessari per eseguire l'app, come descritto in precedenza in questo articolo.

#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM mcr.microsoft.com/dotnet/aspnet:6.0-cbl-mariner2.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM base AS debug
RUN tdnf install procps-ng -y

FROM mcr.microsoft.com/dotnet/sdk:6.0-cbl-mariner2.0 AS build
WORKDIR /src
COPY ["WebApplication1/WebApplication1.csproj", "WebApplication1/"]
RUN dotnet restore "WebApplication1/WebApplication1.csproj"
COPY . .
WORKDIR "/src/WebApplication1"
RUN dotnet build "WebApplication1.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "WebApplication1.csproj" -c Release -o /app/publish

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

Nel file di progetto aggiungere questa impostazione per indicare a Visual Studio di usare la fase debug personalizzata durante il debug.

  <PropertyGroup>
     <!-- other property settings -->
     <DockerfileFastModeStage>debug</DockerfileFastModeStage>
  </PropertyGroup>

Le sezioni successive contengono informazioni che potrebbero essere utili in alcuni casi, ad esempio quando si vuole specificare un punto di ingresso diverso o se l'app è abilitata per SSL e si sta modificando qualcosa che potrebbe influire sulla modalità di gestione dei certificati SSL.

Compilazione dalla riga di comando

Se si vuole compilare un progetto contenitore con un Dockerfile all'esterno di Visual Studio, è possibile usare docker build, MSBuild, dotnet buildo dotnet publish per la compilazione dalla riga di comando.

Se si usa il tipo di compilazione .NET SDK, non si dispone di un Dockerfile, quindi non è possibile usare docker build, usare MSBuilddotnet build invece o dotnet publish per eseguire la compilazione nella riga di comando.

Usare la compilazione Docker

Per compilare una soluzione in contenitori dalla riga di comando, è in genere possibile usare il comando docker build <context> per ogni progetto nella soluzione. Specificare l'argomento del contesto di compilazione. Il contesto di compilazione per un Dockerfile è la cartella nel computer locale usato come cartella di lavoro per generare l'immagine. Ad esempio, si tratta della cartella da cui si copiano i file quando si copia nel contenitore. Nei progetti .NET Core usare la cartella che contiene il file della soluzione (.sln). Espresso come percorso relativo, questo argomento è in genere ".." per un Dockerfile in una cartella del progetto e il file della soluzione nella cartella padre. Per i progetti .NET Framework, il contesto di compilazione è la cartella del progetto, non la cartella della soluzione.

docker build -f Dockerfile ..

Riscaldamento del progetto

Il riscaldamento del progetto fa riferimento a una serie di passaggi che si verificano quando viene selezionato il profilo Docker per un progetto ( ovvero quando viene caricato un progetto o viene aggiunto il supporto docker) per migliorare le prestazioni delle esecuzioni successive (F5 o CTRL+F5). Questo comportamento è configurabile in Strumenti>Opzioni>Strumenti. Ecco le attività eseguite in background:

  • Verificare che Docker Desktop sia installato e in esecuzione.
  • Assicurarsi che Docker Desktop sia impostato sullo stesso sistema operativo del progetto.
  • Eseguire il pull delle immagini nella prima fase del Dockerfile (la base fase nella maggior parte dei Dockerfile).
  • Compilare il Dockerfile e avviare il contenitore.

Il riscaldamento avviene solo in modalità veloce, quindi il contenitore in esecuzione ha il volume della cartella dell'app montato. Ciò significa che le modifiche apportate all'app non invalidano il contenitore. Questo comportamento migliora notevolmente le prestazioni di debug e riduce il tempo di attesa per le attività a esecuzione prolungata, ad esempio il pull di immagini di grandi dimensioni.

Mapping dei volumi

Per il funzionamento del debug nei contenitori, Visual Studio usa il mapping dei volumi per eseguire il mapping delle cartelle del debugger e NuGet dal computer host. Il mapping dei volumi è descritto nella documentazione di Docker qui. È possibile visualizzare i mapping dei volumi per un contenitore usando la finestra Contenitori in Visual Studio.

Ecco i volumi montati nel contenitore:

Volume Descrizione
Cartella dell'app Contiene la cartella del progetto in cui si trova il Dockerfile.
Cartelle dei pacchetti NuGet Contiene i pacchetti NuGet e le cartelle di fallback letti dal file obj{project}.csproj.nuget.g.props nel progetto.
Debugger remoto Contiene i bit necessari per eseguire il debugger nel contenitore a seconda del tipo di progetto. Vedere la sezione Debug .
Cartella di origine Contiene il contesto di compilazione passato ai comandi Docker.

Ecco i volumi montati nel contenitore. Gli elementi visualizzati nei contenitori potrebbero variare a seconda della versione secondaria di Visual Studio 2022 in uso.

Volume Descrizione
Cartella dell'app Contiene la cartella del progetto in cui si trova il Dockerfile.
HotReloadAgent Contiene i file per l'agente di ricaricamento rapido.
HotReloadProxy Contiene i file necessari per eseguire un servizio che consente all'agente di ricaricare l'host di comunicare con Visual Studio nell'host.
Cartelle dei pacchetti NuGet Contiene i pacchetti NuGet e le cartelle di fallback letti dal file obj{project}.csproj.nuget.g.props nel progetto.
Debugger remoto Contiene i bit necessari per eseguire il debugger nel contenitore a seconda del tipo di progetto. Questo argomento è illustrato in modo più dettagliato nella sezione Debug .
Cartella di origine Contiene il contesto di compilazione passato ai comandi Docker.
TokenService.Proxy Contiene i file necessari per eseguire un servizio che consente a VisualStudioCredential di comunicare con Visual Studio nell'host.

Per .NET 8, potrebbero essere presenti anche punti di montaggio aggiuntivi alla radice e per l'utente dell'app che contengono segreti utente e il certificato HTTPS. E in Visual Studio 17.10 preview, il volume Ricaricamento rapido e servizio token, insieme a un altro componente, l'helper senza distribuzione, vengono combinati in un singolo punto di montaggio, /VSTools.

Nota

Visual Studio 17.10 preview Se si usa il motore Docker in sottosistema Windows per Linux (WSL) senza Docker Desktop, impostare la variabile VSCT_WslDaemon=1 di ambiente in modo che Visual Studio usi percorsi WSL durante la creazione di montaggi di volumi. È necessario anche il pacchetto NuGet Microsoft.VisualStudio.Azure.Containers.Tools.Targets 1.20.0-Preview 1 .

Per ASP.NET app Web di base, potrebbero essere presenti due cartelle aggiuntive per il certificato SSL e i segreti utente, illustrati in modo più dettagliato nella sezione successiva.

Abilitare i log dettagliati degli strumenti contenitore

A scopo di diagnostica, è possibile abilitare determinati log di Strumenti contenitore. È possibile abilitare questi log impostando determinate variabili di ambiente. Per i progetti a contenitore singolo, la variabile di ambiente è MS_VS_CONTAINERS_TOOLS_LOGGING_ENABLED, che quindi esegue l'accesso a %tmp%\Microsoft.VisualStudio.Containers.Tools. Per i progetti Docker Compose, è , che quindi esegue l'accesso MS_VS_DOCKER_TOOLS_LOGGING_ENABLEDin %tmp%\Microsoft.VisualStudio.DockerCompose.Tools.

Attenzione

Quando la registrazione è abilitata e si usa un proxy di token per l'autenticazione di Azure, è possibile registrare le credenziali di autenticazione come testo normale. Vedere Configurare l'autenticazione di Azure.

Punto di ingresso del contenitore

Visual Studio usa un punto di ingresso del contenitore personalizzato a seconda del tipo di progetto e del sistema operativo del contenitore, ecco le diverse combinazioni:

Tipo di contenitore Punto di accesso
Contenitori Linux Il punto di ingresso è tail -f /dev/null, che è un'attesa infinita per mantenere il contenitore in esecuzione. Quando l'app viene avviata tramite il debugger, è il debugger responsabile dell'esecuzione dell'app , ovvero dotnet webapp.dll. Se avviato senza eseguire il debug, gli strumenti eseguono un docker exec -i {containerId} dotnet webapp.dll oggetto per eseguire l'app.
Contenitori Windows Il punto di ingresso è simile C:\remote_debugger\x64\msvsmon.exe /noauth /anyuser /silent /nostatus al quale viene eseguito il debugger, quindi è in ascolto delle connessioni. Questo metodo si applica quando il debugger esegue l'app. Quando viene avviato senza eseguire il debug, viene usato un docker exec comando . Per le app Web .NET Framework, il punto di ingresso è leggermente diverso da dove ServiceMonitor viene aggiunto al comando.

Il punto di ingresso del contenitore può essere modificato solo nei progetti Docker Compose, non in progetti a contenitore singolo.

App di base abilitate per SSL ASP.NET

Gli strumenti contenitore in Visual Studio supportano il debug di un'app principale ASP.NET abilitata per SSL con un certificato di sviluppo, allo stesso modo in cui si prevede che funzioni senza contenitori. A tale scopo, Visual Studio aggiunge un paio di passaggi aggiuntivi per esportare il certificato e renderlo disponibile per il contenitore. Di seguito è riportato il flusso gestito da Visual Studio durante il debug nel contenitore:

  1. Assicura che il certificato di sviluppo locale sia presente e attendibile nel computer host tramite lo dev-certs strumento .

  2. Esporta il certificato in %APPDATA%\ASP.NET\Https con una password sicura archiviata nell'archivio dei segreti utente per questa particolare app.

  3. Il volume monta le directory seguenti:

    • *%APPDATA%\Microsoft\UserSecrets
    • *%APPDATA%\ASP.NET\Https

ASP.NET Core cerca un certificato corrispondente al nome dell'assembly nella cartella Https , motivo per cui viene eseguito il mapping al contenitore in tale percorso. Il percorso e la password del certificato possono essere definiti in alternativa usando variabili di ambiente (ovvero ASPNETCORE_Kestrel__Certificates__Default__Path e ASPNETCORE_Kestrel__Certificates__Default__Password) o nel file JSON dei segreti utente, ad esempio:

{
  "Kestrel": {
    "Certificates": {
      "Default": {
        "Path": "c:\\app\\mycert.pfx",
        "Password": "strongpassword"
      }
    }
  }
}

Se la configurazione supporta compilazioni in contenitori e non in contenitori, è consigliabile usare le variabili di ambiente, perché i percorsi sono specifici dell'ambiente contenitore.

Per altre informazioni sull'uso di SSL con app ASP.NET Core nei contenitori, vedere Hosting di immagini ASP.NET Core con Docker su HTTPS.

Per un esempio di codice che illustra la creazione di certificati personalizzati per un'app multiservizio considerata attendibile nell'host e nei contenitori per la comunicazione da servizio a servizio HTTPS, vedere CertExample.