Personalizar contêineres do Docker no Visual Studio

Você pode personalizar suas imagens de contêiner editando o Dockerfile gerado pelo Visual Studio ao adicionar suporte do Docker ao seu projeto. Se você estiver criando um contêiner personalizado no IDE do Visual Studio ou configurando um build de linha de comando, precisará saber como o Visual Studio usa o Dockerfile para criar seus projetos. Você precisa saber esses detalhes porque, por motivos de desempenho, o Visual Studio segue um processo especial para criar e executar aplicativos conteinerizados que não são óbvios no Dockerfile.

Suponha que você queira fazer uma alteração no Dockerfile e ver os resultados contêineres de depuração e produção. Nesse caso, você pode adicionar comandos no Dockerfile para modificar a primeira fase (geralmente base). Confira Modificar a imagem de contêiner para depuração e produção. Porém, se você quiser fazer uma alteração somente durante a depuração, mas não na produção, deve criar outra fase e usar a configuração de build DockerfileFastModeStage para dizer ao Visual Studio para usar essa fase para builds de depuração. Confira Modificar a imagem de contêiner somente para depuração.

Este artigo explica detalhadamente o processo de build do Visual Studio para aplicativos conteinerizados e contém informações sobre como modificar o Dockerfile para afetar os builds de depuração e produção ou apenas para depuração.

Builds do Dockerfile no Visual Studio

Observação

Esta seção descreve o processo de build de contêiner que o Visual Studio usa quando você escolhe o tipo de build do contêiner Dockerfile. Se você estiver usando o tipo de build do SDK do .NET, as opções de personalização serão diferentes e as informações nesta seção não serão aplicáveis. Em vez disso, consulte Conteinerizar um aplicativo .NET com dotnet publish e use as propriedades descritas em Personalizar seu contêiner para configurar o processo de build do contêiner.

Build de várias fases

Quando o Visual Studio compila um projeto que não usa contêineres do Docker, ele invoca o MSBuild no computador local e gera os arquivos de saída em uma pasta (normalmente bin) na pasta da solução local. No entanto, para um projeto conteinerizado, o processo de build leva em conta as instruções do Dockerfile para criar o aplicativo conteinerizado. O Dockerfile que o Visual Studio usa é dividido em várias fases. Esse processo depende do recurso de build de várias fases do Docker.

O recurso de build de várias fases ajuda a tornar o processo de criação de contêineres mais eficiente e torna os contêineres menores, permitindo que contenham apenas os bits de que seu aplicativo precisa no tempo de execução. O build de várias fases é usado para projetos do .NET Core, e não para projetos do .NET Framework.

O build de várias fases permite que imagens de contêiner sejam criadas em fases que produzem imagens intermediárias. Por exemplo, considere um Dockerfile típico. A primeira fase é chamada de base no Dockerfile gerado pelo Visual Studio, embora as ferramentas não exijam esse nome.

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

As linhas no Dockerfile começam com a imagem do ASP.NET no Registro de Contêiner da Microsoft (mcr.microsoft.com) e criam uma imagem intermediária base que expõe as portas 80 e 443 e define o diretório de trabalho como /app.

A próxima fase é build, que aparece da seguinte maneira:

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

Você pode ver que a fase build começa a partir de uma imagem original diferente do registro (sdk em vez de aspnet), em vez de continuar da base. A imagem sdk tem todas as ferramentas de build e, por esse motivo, é muito maior do que a imagem aspnet, que contém apenas componentes de runtime. O motivo para usar uma imagem separada fica claro quando você olha para o restante do 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"]

A fase final começa novamente em base e inclui o COPY --from=publish para copiar a saída publicada para a imagem final. Esse processo possibilita que a imagem final seja muito menor, pois não precisa incluir todas as ferramentas de build que estavam na imagem sdk.

MSBuild

Observação

Esta seção descreve como você pode personalizar seus contêineres do Docker ao escolher o tipo de build de contêiner do Dockerfile. Se você estiver usando o tipo de build do SDK do .NET, as opções de personalização serão diferentes e as informações neste artigo não serão aplicáveis. Em vez disso, consulte Colocar em contêiner um aplicativo .NET com dotnet publish.

Os Dockerfiles criados pelo Visual Studio para projetos do .NET Framework (e para projetos do .NET Core criados com versões do Visual Studio anteriores à Atualização 4 do Visual Studio 2017) não são Dockerfiles de várias fases. As etapas nesses Dockerfiles não compilam o seu código. Em vez disso, quando o Visual Studio compila um Dockerfile do .NET Framework, ele primeiro compila o projeto usando o MSBuild. Quando esse processo é bem-sucedido, o Visual Studio compila o Dockerfile, que simplesmente copia a saída de build do MSBuild para a imagem resultante do Docker. Como as etapas para compilar seu código não estão incluídas no Dockerfile, você não pode compila os Dockerfiles do .NET Framework usando docker build a partir da linha de comando. Você deve usar o MSBuild para compilar esses projetos.

Para criar uma imagem para um único projeto de contêiner do Docker, você pode usar o MSBuild com a opção de comando /t:ContainerBuild. Isso diz ao MSBuild para compilar o ContainerBuild de destino, em vez do Build de destino padrão. Por exemplo:

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

Você verá uma saída semelhante à que você vê na janela Saída ao compilar sua solução a partir do IDE do Visual Studio. Sempre use /p:Configuration=Release, já que, nos casos em que o Visual Studio usa a otimização de build de várias fases, os resultados ao compilar a configuração de Depuração podem não ser os esperados. Confira Depuração.

Se você estiver usando um projeto do Docker Compose, use este comando para compilar imagens:

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

Depuração

Observação

Esta seção descreve como você pode personalizar seus contêineres do Docker ao escolher o tipo de build de contêiner do Dockerfile. Se você estiver usando o tipo de build do SDK do .NET, as opções de personalização serão diferentes e a maioria das informações nesta seção não serão aplicáveis. Em vez disso, consulte Colocar em contêiner um aplicativo .NET com dotnet publish.

Ao compilar na configuração de Depuração, o Visual Studio faz várias otimizações que ajudam no desempenho do processo de compilação para projetos conteinerizados. O processo de compilação para aplicativos conteinerizados não é tão simples quanto apenas seguir as etapas descritas no Dockerfile. A compilação em um contêiner é muito mais lenta do que a compilação no computador local. Portanto, quando você compila na configuração de Depuração, o Visual Studio realmente compila seus projetos no computador local e compartilha a pasta de saída para o contêiner usando a montagem de volume. Um build com essa otimização habilitada é chamado de build de modo Rápido.

No modo Rápido, o Visual Studio chama docker build com um argumento que informa ao Docker para criar apenas o primeiro estágio no Dockerfile (normalmente, o estágio base). Você pode alterá-lo definindo a propriedade do MSBuild, DockerfileFastModeStage, descrita nas propriedades do MSBuild das Ferramentas de Contêiner. O Visual Studio manipula o restante do processo, sem considerar o conteúdo do Dockerfile. Portanto, ao modificar o Dockerfile, como personalizar o ambiente de contêiner ou instalar dependências adicionais, você deve colocar suas modificações na primeia fase. As etapas personalizadas colocadas as fasesbuild, publish ou final do Dockerfile não serão executadas.

Essa otimização de desempenho só ocorre quando você compila na configuração de Depuração. Na configuração de Versão, o build ocorre no contêiner, conforme especificado no Dockerfile.

Se você quiser desabilitar a otimização de desempenho e compilar como o Dockerfile especifica, defina a propriedade ContainerDevelopmentMode como Regular no arquivo de projeto da seguinte maneira:

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

Para restaurar a otimização de desempenho, remova a propriedade do arquivo de projeto.

Quando você inicia a depuração (F5), um contêiner iniciado anteriormente é reutilizado, se possível. Se você não quiser reutilizar o contêiner anterior, pode usar comandos os Rebuild ou Clean no Visual Studio para forçar o Visual Studio a usar um novo contêiner.

O processo de execução do depurador depende do tipo de projeto e do sistema operacional de contêiner:

Cenário Processo do depurador
Aplicativos do .NET Core (contêineres do Linux) O Visual Studio baixa vsdbg e mapeia para o contêiner. Em seguida, ele é chamado com o programa e os argumentos (ou seja, dotnet webapp.dll) e depois o depurador anexa ao processo.
Aplicativos do .NET Core (contêineres do Windows) O Visual Studio o usa onecoremsvsmon e mapeia para o contêiner, executa como o ponto de entrada e, em seguida, o Visual Studio se conecta a ele e anexa ao programa. Isso é semelhante a como você normalmente configuraria a depuração remota em outro computador ou máquina virtual.
Aplicativos do .NET Framework O Visual Studio o usa msvsmon e mapeia para o contêiner, executa como o parte do ponto de entrada em que o Visual Studio pode se conectar a ele e anexa ao programa.

Para obter informações sobre vsdbg.exe, confira Offroad debugging of .NET Core on Linux and OS X from Visual Studio.

Modificar a imagem de contêiner para depuração e produção

Para modificar a imagem de contêiner para depuração e produção, modifique a fase base. Adicione suas personalizações ao Dockerfile na seção da fase base, geralmente a primeira seção no Dockerfile. Veja a referência do Dockerfile na documentação do Docker para obter informações sobre os comandos do 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"]

Modificar a imagem de contêiner somente para depuração

Esse cenário se aplica quando você deseja fazer algo com seus contêineres para ajudar no processo de depuração, como instalar algo para fins de diagnóstico, mas você não deseja que seja instalado em builds de produção.

Para modificar o contêiner somente para depuração, crie uma fase e use a propriedade do MSBuild DockerfileFastModeStage para dizer ao Visual Studio para usar a fase personalizada durante a depuração. Veja a referência do Dockerfile na documentação do Docker para obter informações sobre os comandos do Dockerfile.

No exemplo a seguir, instalamos o pacote procps-ng, mas somente no modo de depuração. O pacote fornece o comando pidof, que o Visual Studio exige, mas não está na imagem do Mariner usada aqui. A fase que usamos para depuração de modo rápido é debug, uma fase personalizada definida aqui. A fase de modo rápido não precisa herdar da fase build ou publish, ela pode herdar diretamente da fase base, pois o Visual Studio montará um volume que contém tudo o que for necessário para executar o aplicativo, conforme descrito anteriormente neste artigo.

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

No arquivo de projeto, adicione essa configuração para dizer o Visual Studio para usar a fase personalizada debug durante a depuração.

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

As próximas seções contêm informações que podem ser úteis em determinados casos, como quando você deseja especificar um ponto de entrada diferente ou se o aplicativo estiver habilitado para SSL e você estiver alterando algo que possa afetar a forma como os certificados SSL são tratados.

Compilação a partir da linha de comando

Se você quiser compilar um projeto de contêiner com um Dockerfile fora do Visual Studio, pode usar docker build, MSBuild, dotnet build ou dotnet publish para compilar a partir da linha de comando.

Se você estiver usando o tipo de build do SDK do .NET, não terá um Dockerfile, portanto, não poderá usar docker build; em vez disso, use MSBuild, dotnet build ou dotnet publish para compilar na linha de comando.

Usar o build do Docker

Para compilar uma solução conteinerizada a partir da linha de comando, você geralmente pode usar o comando docker build <context> para cada projeto na solução. Você fornece o argumento de contexto de build. O contexto de build para um Dockerfile é a pasta no computador local que é usada como a pasta de trabalho para gerar a imagem. Por exemplo, é a pasta usada para copiar arquivos quando copia para o contêiner. Em projetos do .NET Core, use a pasta que contém o arquivo de solução (.sln). Expresso como um caminho relativo, esse argumento normalmente é ".." para um Dockerfile em uma pasta de projeto e o arquivo de solução na pasta pai. Para projetos do .NET Framework, o contexto de build é a pasta do projeto, e não a pasta da solução.

docker build -f Dockerfile ..

Aquecimento do projeto

O aquecimento do projeto se refere a uma série de etapas que ocorrem quando o perfil do Docker é selecionado para um projeto (ou seja, quando um projeto é carregado ou o suporte do Docker é adicionado) para melhorar o desempenho das execuções subsequentes (F5 ou Ctrl+F5). Isso pode ser configurado em Ferramentas>Opções>Ferramentas de Contêiner. Estas são as tarefas executadas em segundo plano:

  • Verifique se o Docker Desktop está instalado e em execução.
  • Verifique se o Docker Desktop está definido como o mesmo sistema operacional que o projeto.
  • Extraia as imagens na primeira fase do Dockerfile (a fase base na maioria dos Dockerfiles).
  • Compile o Dockerfile e inicie o contêiner.

O aquecimento ocorre apenas no modo Rápido, portanto, o contêiner em execução tem a pasta do aplicativo montada em volume. Isso significa que qualquer alteração no aplicativo não invalida o contêiner. Portanto, isso melhora significativamente o desempenho de depuração e diminui o tempo de espera para tarefas de execução prolongada, como efetuar pull de imagens grandes.

Mapeamento de volume

Para que a depuração funcione em contêineres, o Visual Studio usa o mapeamento de volume para mapear as pastas do depurador e do NuGet no computador host. O mapeamento de volume é descrito na documentação do Docker aqui. Você pode exibir os mapeamentos de volume de um contêiner usando a janela Contêineres no Visual Studio.

Estes são os volumes montados no seu contêiner:

Volume Descrição
Pasta de aplicativo Contém a pasta do projeto em que o Dockerfile está localizado.
Pastas de pacotes NuGet Contém os pacotes NuGet e as pastas de fallback que são lidas no arquivo obj{project}.csproj.nuget.g.props do projeto.
Depurador remoto Contém os bits necessários para executar o depurador no contêiner, dependendo do tipo de projeto. Consulte a seção Depuração.
Pasta de origem Contém o contexto de build que é passado para os comandos do Docker.

Esse são os volumes montados no seu contêiner. O que você vê em seus contêineres pode ser diferente dependendo da versão secundária do Visual Studio 2022 que você está usando.

Volume Descrição
Pasta de aplicativo Contém a pasta do projeto em que o Dockerfile está localizado.
HotReloadAgent Contém os arquivos do agente de recarregamento frequente.
HotReloadProxy Contém os arquivos necessários para executar um serviço que permite que o agente de recarregamento de host se comunique com o Visual Studio no host.
Pastas de pacotes NuGet Contém os pacotes NuGet e as pastas de fallback que são lidas no arquivo obj{project}.csproj.nuget.g.props do projeto.
Depurador remoto Contém os bits necessários para executar o depurador no contêiner, dependendo do tipo de projeto. Isso será explicado mais detalhadamente na seção Depuração.
Pasta de origem Contém o contexto de build que é passado para os comandos do Docker.
TokenService.Proxy Contém os arquivos necessários para executar um serviço que permite que o VisualStudioCredential se comunique com o Visual Studio no host.

Para o .NET 8, também pode haver pontos de montagem adicionais na raiz e para o usuário do aplicativo que contêm segredos do usuário e o certificado HTTPS. E na visualização do Visual Studio 17.10, o volume Hot Reload e Token Service, juntamente com outro componente, o Distroless Helper, são combinados em um único ponto de montagem, /VSTools.

Observação

Preview do Visual Studio 17.10 Se estiver usando o Docker Engine no Subsistema do Windows para Linux (WSL) sem o Docker Desktop, defina a variável de ambiente VSCT_WslDaemon=1 para que o Visual Studio use caminhos do WSL ao criar montagens de volume. O pacote NuGet Microsoft.VisualStudio.Azure.Containers.Tools.Targets 1.20.0-Preview 1 também é necessário.

Para os aplicativos Web do ASP.NET Core, pode haver duas pastas adicionais para o certificado SSL e os segredos do usuário, o que é explicado mais detalhadamente na próxima seção.

Habilitar logs detalhados das ferramentas de contêiner

Para fins de diagnóstico, você pode habilitar determinados logs das ferramentas de contêiner. Você pode habilitar esses logs definindo determinadas variáveis de ambiente. Para projetos de contêiner único, a variável de ambiente é MS_VS_CONTAINERS_TOOLS_LOGGING_ENABLED, que então faz o logon em %tmp%\Microsoft.VisualStudio.Containers.Tools. Para projetos do Docker Compose, é MS_VS_DOCKER_TOOLS_LOGGING_ENABLED, que, em seguida, registra em %tmp%\Microsoft.VisualStudio.DockerCompose.Tools.

Cuidado

Quando o registro em log está habilitado e você está usando um proxy de token para autenticação do Azure, as credenciais de autenticação podem ser registradas como texto sem formatação. Confira Configurar a autenticação do Azure.

Ponto de entrada do contêiner

O Visual Studio usa um ponto de entrada de contêiner personalizado dependendo do tipo de projeto e do sistema operacional de contêiner. Veja abaixo as diferentes combinações:

Tipo de contêiner Ponto de entrada
Contêineres do Linux O ponto de entrada é tail -f /dev/null, que é uma espera infinita para manter o contêiner em execução. Quando o aplicativo é iniciado por meio do depurador, o depurador é o responsável por executar o aplicativo (ou seja, dotnet webapp.dll). Se iniciada sem depuração, o ferramental executa um docker exec -i {containerId} dotnet webapp.dll para executar o aplicativo.
Contêineres do Windows O ponto de entrada é semelhante a C:\remote_debugger\x64\msvsmon.exe /noauth /anyuser /silent /nostatus que executa o depurador. Portanto, está escutando conexões. Esse método se aplica quando o depurador executa o aplicativo. Quando iniciado sem depuração, um comando docker exec é usado. Para aplicativos Web do .NET Framework, o ponto de entrada é um pouco diferente, onde ServiceMonitor é adicionado ao comando.

O ponto de entrada do contêiner só pode ser modificado em projetos do Docker Compose e não em projetos de contêiner único.

Aplicativos do ASP.NET Core habilitados para SSL

As ferramentas de contêiner no Visual Studio permitem a depuração de um aplicativo do ASP.NET Core habilitado para SSL com um certificado de desenvolvimento, da mesma maneira que você esperaria que ele funcionasse sem contêineres. Para fazer isso acontecer, o Visual Studio adiciona mais algumas etapas para exportar o certificado e disponibilizá-lo para o contêiner. Este é o fluxo que o Visual Studio manipula para você ao depurar no contêiner:

  1. Garante que o certificado de desenvolvimento local esteja presente e confiável no computador host por meio da ferramenta dev-certs.

  2. Exporta o certificado para %APPDATA%\ASP.NET\Https com uma senha segura armazenada no repositório de segredos do usuário para esse aplicativo específico.

  3. Montagens de volume nos seguintes diretórios:

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

O ASP.NET Core procura um certificado que corresponda ao nome do assembly na pasta Https, por isso, ele é mapeado para o contêiner nesse caminho. O caminho do certificado e a senha podem ser definidos como alternativa usando variáveis de ambiente (ou seja, ASPNETCORE_Kestrel__Certificates__Default__Path e ASPNETCORE_Kestrel__Certificates__Default__Password) ou no arquivo json de segredos do usuário, por exemplo:

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

Se sua configuração permite builds conteinerizados e não conteinerizados, você deve usar as variáveis de ambiente, pois os caminhos são específicos para o ambiente de contêiner.

Para saber como usar o SSL com aplicativos ASP.NET Core em contêineres, confira Hospedagem de imagens do ASP.NET Core com o Docker via HTTPS.

Para obter um exemplo de código que demonstra a criação de certificados personalizados para um aplicativo de vários serviços que são confiáveis no host e nos contêineres para a comunicação HTTPS serviço a serviço, confira CertExample.