Tutorial: Contentorizar uma aplicação .NET
Neste tutorial, você aprenderá a colocar em contêiner um aplicativo .NET com o Docker. Os contêineres têm muitos recursos e benefícios, como ser uma infraestrutura imutável, fornecer uma arquitetura portátil e permitir escalabilidade. A imagem pode ser usada para criar contêineres para seu ambiente de desenvolvimento local, nuvem privada ou nuvem pública.
Neste tutorial, você:
- Criar e publicar um aplicativo .NET simples
- Criar e configurar um Dockerfile para .NET
- Criar uma imagem do Docker
- Criar e executar um contêiner do Docker
Você explora o contêiner do Docker, cria e implanta tarefas para um aplicativo .NET. A plataforma Docker usa o motor Docker para criar e empacotar aplicações rapidamente como imagens Docker. Essas imagens são escritas no formato Dockerfile a ser implantado e executado num contêiner em camadas.
Dica
Se estiveres interessado em publicar o teu aplicativo .NET como um contêiner sem a necessidade de Docker ou Podman, consulta Containerizar um aplicativo .NET com dotnet publish.
Observação
Este tutorial não é para aplicativos ASP.NET Core. Se você estiver usando o ASP.NET Core, consulte o tutorial Saiba como colocar em contêiner um aplicativo ASP.NET Core.
Pré-requisitos
Instale os seguintes pré-requisitos:
-
SDK do .NET 8+.
Se você tiver o .NET instalado, use o comandodotnet --info
para determinar qual SDK você está usando. - Docker Community Edition.
- Uma pasta de trabalho temporária para o Dockerfile e o aplicativo de exemplo .NET. Neste tutorial, o nome docker-working é usado como a pasta de trabalho.
Criar aplicativo .NET
Você precisa de um aplicativo .NET que o contêiner do Docker execute. Abra o seu terminal, crie uma pasta de trabalho, se ainda não o fez, e introduza-a. Na pasta de trabalho, execute o seguinte comando para criar um novo projeto em um subdiretório chamado App:
dotnet new console -o App -n DotNet.Docker
Sua árvore de pastas é semelhante à seguinte estrutura de diretórios:
📁 docker-working
└──📂 App
├──DotNet.Docker.csproj
├──Program.cs
└──📂 obj
├── DotNet.Docker.csproj.nuget.dgspec.json
├── DotNet.Docker.csproj.nuget.g.props
├── DotNet.Docker.csproj.nuget.g.targets
├── project.assets.json
└── project.nuget.cache
O comando dotnet new
cria uma nova pasta chamada App e gera um aplicativo de console "Hello World". Agora, você altera os diretórios e navega para a pasta App a partir da sessão do terminal. Use o comando dotnet run
para iniciar o aplicativo. O aplicativo é executado e imprime Hello World!
abaixo do comando:
cd App
dotnet run
Hello World!
O modelo padrão cria um aplicativo que é impresso no terminal e, em seguida, termina imediatamente. Para este tutorial, você usa um aplicativo que faz loops indefinidamente. Abra o arquivo Program.cs em um editor de texto.
Dica
Se você estiver usando o Visual Studio Code, na sessão de terminal anterior, digite o seguinte comando:
code .
Este comando abre a pasta App que contém o projeto no Visual Studio Code.
O Program.cs deve ser semelhante ao seguinte código C#:
Console.WriteLine("Hello World!");
Substitua o arquivo pelo seguinte código que conta números a cada segundo:
var counter = 0;
var max = args.Length is not 0 ? Convert.ToInt32(args[0]) : -1;
while (max is -1 || counter < max)
{
Console.WriteLine($"Counter: {++counter}");
await Task.Delay(TimeSpan.FromMilliseconds(1_000));
}
var counter = 0;
var max = args.Length is not 0 ? Convert.ToInt32(args[0]) : -1;
while (max is -1 || counter < max)
{
Console.WriteLine($"Counter: {++counter}");
await Task.Delay(TimeSpan.FromMilliseconds(1_000));
}
Salve o arquivo e teste o programa novamente com dotnet run
. Lembre-se que este aplicativo é executado indefinidamente. Use o comando cancel Ctrl+C para pará-lo. Considere o seguinte exemplo de saída:
dotnet run
Counter: 1
Counter: 2
Counter: 3
Counter: 4
^C
Se você passar um número na linha de comando para o aplicativo, ele limita a contagem a esse valor e, em seguida, sai. Experimente com dotnet run -- 5
para contar até cinco.
Importante
Todos os parâmetros após --
não são passados para o comando dotnet run
e, em vez disso, são passados para seu aplicativo.
Publicar aplicativo .NET
Para que o aplicativo seja adequado para uma criação de imagem, ele tem que compilar. O comando dotnet publish
é mais adequado para isso, pois cria e publica o aplicativo. Para uma referência detalhada, consulte a documentação dos comandos dotnet build e dotnet publish.
dotnet publish -c Release
Dica
Se estiveres interessado em publicares a tua aplicação .NET como um contêiner sem precisares do Docker, consulta Containerizar uma aplicação .NET com dotnet publish.
O comando dotnet publish
compila seu aplicativo para a pasta publicar. O caminho para o publicar pasta da pasta de trabalho deve ser ./App/bin/Release/<TFM>/publish/:
Na pasta App, obtenha uma listagem de diretório da pasta de publicação para verificar se o arquivo DotNet.Docker.dll foi criado.
dir .\bin\Release\net9.0\publish\
Directory: C:\Users\default\docker-working\App\bin\Release\net9.0\publish
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 1/6/2025 10:11 AM 431 DotNet.Docker.deps.json
-a---- 1/6/2025 10:11 AM 6144 DotNet.Docker.dll
-a---- 1/6/2025 10:11 AM 145408 DotNet.Docker.exe
-a---- 1/6/2025 10:11 AM 11716 DotNet.Docker.pdb
-a---- 1/6/2025 10:11 AM 340 DotNet.Docker.runtimeconfig.json
dir .\bin\Release\net8.0\publish\
Directory: C:\Users\default\docker-working\App\bin\Release\net8.0\publish
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 9/22/2023 9:17 AM 431 DotNet.Docker.deps.json
-a--- 9/22/2023 9:17 AM 6144 DotNet.Docker.dll
-a--- 9/22/2023 9:17 AM 157696 DotNet.Docker.exe
-a--- 9/22/2023 9:17 AM 11688 DotNet.Docker.pdb
-a--- 9/22/2023 9:17 AM 353 DotNet.Docker.runtimeconfig.json
Criar o Dockerfile
O arquivo Dockerfile
Crie um arquivo chamado Dockerfile no diretório que contém o .csproj e abra-o em um editor de texto. Este tutorial usa a imagem de tempo de execução do ASP.NET Core (que contém a imagem de tempo de execução do .NET) e corresponde ao aplicativo de console do .NET.
FROM mcr.microsoft.com/dotnet/sdk:9.0@sha256:3fcf6f1e809c0553f9feb222369f58749af314af6f063f389cbd2f913b4ad556 AS build
WORKDIR /App
# Copy everything
COPY . ./
# Restore as distinct layers
RUN dotnet restore
# Build and publish a release
RUN dotnet publish -o out
# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:9.0@sha256:b4bea3a52a0a77317fa93c5bbdb076623f81e3e2f201078d89914da71318b5d8
WORKDIR /App
COPY --from=build /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]
Observação
A imagem de tempo de execução do ASP.NET Core é usada intencionalmente aqui, embora a imagem mcr.microsoft.com/dotnet/runtime:9.0
possa ser usada em vez disso.
FROM mcr.microsoft.com/dotnet/sdk:8.0@sha256:35792ea4ad1db051981f62b313f1be3b46b1f45cadbaa3c288cd0d3056eefb83 AS build
WORKDIR /App
# Copy everything
COPY . ./
# Restore as distinct layers
RUN dotnet restore
# Build and publish a release
RUN dotnet publish -o out
# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:8.0@sha256:6c4df091e4e531bb93bdbfe7e7f0998e7ced344f54426b7e874116a3dc3233ff
WORKDIR /App
COPY --from=build /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]
Observação
A imagem de tempo de execução do ASP.NET Core é usada intencionalmente aqui, embora a imagem mcr.microsoft.com/dotnet/runtime:8.0
possa ser usada em vez disso.
Importante
Incluir um algoritmo de hash seguro (SHA) após a marca de imagem em um Dockerfile é uma prática recomendada. Isso garante que a imagem não seja adulterada e que a imagem seja a mesma que você espera. O SHA é um identificador exclusivo para a imagem. Para obter mais informações, consulte Docker Docs: Extrair uma imagem por resumo.
Dica
Este Dockerfile usa compilações de vários estágios, que otimizam o tamanho final da imagem colocando a compilação em camadas e deixando apenas os artefatos necessários. Para obter mais informações, consulte Docker Docs: compilações de vários estágios.
A palavra-chave FROM
requer um nome de imagem de contêiner do Docker totalmente qualificado. O Microsoft Container Registry (MCR, mcr.microsoft.com) é um sindicato do Docker Hub, que hospeda contêineres acessíveis publicamente. O segmento dotnet
é o repositório de contêiner, enquanto o segmento sdk
ou aspnet
é o nome da imagem do contêiner. A imagem é marcada com 9.0
, que é usado para versionamento. Assim, mcr.microsoft.com/dotnet/aspnet:9.0
é o tempo de execução do .NET 9.0. Certifique-se de obter a versão do runtime que corresponde ao runtime direcionado pelo seu SDK. Por exemplo, o aplicativo criado na seção anterior usava o SDK do .NET 9.0 e a imagem base mencionada no Dockerfile do
Importante
Ao usar imagens de contêiner baseadas no Windows, você precisa especificar a marca de imagem além de simplesmente 9.0
, por exemplo, mcr.microsoft.com/dotnet/aspnet:9.0-nanoserver-1809
em vez de mcr.microsoft.com/dotnet/aspnet:9.0
. Selecione um nome de imagem com base no fato de você estar usando o Nano Server ou o Windows Server Core e qual versão desse sistema operacional. Você pode encontrar uma lista completa de todas as tags suportadas no . Página do Docker Hub da NET.
Salve o Dockerfile arquivo. A estrutura de diretórios da pasta de trabalho deve ter a seguinte aparência. Alguns dos arquivos e pastas de nível mais profundo são omitidos para economizar espaço no artigo:
📁 docker-working
└──📂 App
├── Dockerfile
├── DotNet.Docker.csproj
├── Program.cs
├──📂 bin
│ └───📂 Release
│ └───📂 net9.0
│ ├───📂 publish
│ │ ├─── DotNet.Docker.deps.json
│ │ ├─── DotNet.Docker.dll
│ │ ├─── DotNet.Docker.exe
│ │ ├─── DotNet.Docker.pdb
│ │ └─── DotNet.Docker.runtimeconfig.json
│ ├─── DotNet.Docker.deps.json
│ ├─── DotNet.Docker.dll
│ ├─── DotNet.Docker.exe
│ ├─── DotNet.Docker.pdb
│ └─── DotNet.Docker.runtimeconfig.json
└──📁 obj
└──...
A palavra-chave FROM
requer um nome de imagem de contêiner do Docker totalmente qualificado. O Microsoft Container Registry (MCR, mcr.microsoft.com) é um sindicato do Docker Hub, que hospeda contêineres acessíveis publicamente. O segmento dotnet
é o repositório de contêiner, enquanto o segmento sdk
ou aspnet
é o nome da imagem do contêiner. A imagem é marcada com 8.0
, que é usado para versionamento. Assim, mcr.microsoft.com/dotnet/aspnet:8.0
é o tempo de execução do .NET 8.0. Certifique-se de obter a versão de runtime que corresponde ao runtime visado pelo seu SDK. Por exemplo, o aplicativo criado na seção anterior usava o SDK do .NET 8.0 e a imagem base mencionada no Dockerfile do
Importante
Ao usar imagens de contêiner baseadas no Windows, você precisa especificar a marca de imagem além de simplesmente 8.0
, por exemplo, mcr.microsoft.com/dotnet/aspnet:8.0-nanoserver-1809
em vez de mcr.microsoft.com/dotnet/aspnet:8.0
. Selecione um nome de imagem com base no fato de você estar usando o Nano Server ou o Windows Server Core e qual versão desse sistema operacional. Você pode encontrar uma lista completa de todas as tags suportadas na página do Docker Hub da .NET .
Salve o Dockerfile arquivo. A estrutura de diretórios da pasta de trabalho deve ter a seguinte aparência. Alguns dos arquivos e pastas de nível mais profundo são omitidos para economizar espaço no artigo:
📁 docker-working
└──📂 App
├── Dockerfile
├── DotNet.Docker.csproj
├── Program.cs
├──📂 bin
│ └──📂 Release
│ └──📂 net8.0
│ └──📂 publish
│ ├── DotNet.Docker.deps.json
│ ├── DotNet.Docker.exe
│ ├── DotNet.Docker.dll
│ ├── DotNet.Docker.pdb
│ └── DotNet.Docker.runtimeconfig.json
└──📁 obj
└──...
A instrução ENTRYPOINT
define dotnet
como o host para o DotNet.Docker.dll
. No entanto, é possível definir o ENTRYPOINT
como o próprio executável do aplicativo, confiando no sistema operacional como host do aplicativo:
ENTRYPOINT ["./DotNet.Docker"]
Isso faz com que o aplicativo seja executado diretamente, sem dotnet
e, em vez disso, depende do host do aplicativo e do sistema operacional subjacente. Para obter mais informações sobre como implantar binários multiplataforma, consulte Produzir um binário multiplataforma.
Para construir o contêiner, a partir do seu terminal, execute o seguinte comando:
docker build -t counter-image -f Dockerfile .
O Docker processa cada linha no Dockerfile. O .
no comando docker build
define o contexto de compilação da imagem. O interruptor -f
é o caminho para o Dockerfile. Este comando cria a imagem e cria um repositório local chamado de contraimagem que aponta para essa imagem. Depois que esse comando terminar, execute docker images
para ver uma lista de imagens instaladas:
REPOSITORY TAG IMAGE ID CREATED SIZE
counter-image latest 1c1f1433e51d 32 seconds ago 223MB
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
counter-image latest 2f15637dc1f6 10 minutes ago 217MB
O repositório counter-image
é o nome da imagem. Além disso, a tag da imagem, o identificador da imagem, o tamanho e quando ela foi criada fazem parte da saída. As etapas finais do Dockerfile são criar um contêiner a partir da imagem e executar o aplicativo, copiar o aplicativo publicado para o contêiner e definir o ponto de entrada:
FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /App
COPY --from=build /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /App
COPY --from=build /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]
O comando FROM
especifica a imagem base e a tag a serem usadas. O comando WORKDIR
altera o diretório atual dentro do contêiner para App.
O comando COPY
diz ao Docker para copiar o diretório de origem especificado para uma pasta de destino. Neste exemplo, os publicar conteúdo na camada build
são enviados para a pasta chamada App/out, portanto, é a fonte da qual copiar. Todos os conteúdos publicados no diretório App/out são copiados para o diretório de trabalho atual (App).
O próximo comando, ENTRYPOINT
, diz ao Docker para configurar o contêiner para ser executado como um executável. Quando o contêiner é iniciado, o comando ENTRYPOINT
é executado. Quando esse comando termina, o contêiner é interrompido automaticamente.
Dica
Antes do .NET 8, os contêineres configurados para serem executados como somente leitura podiam falhar com Failed to create CoreCLR, HRESULT: 0x8007000E
. Para resolver esse problema, especifique uma variável de ambiente DOTNET_EnableDiagnostics
como 0
(imediatamente antes da etapa ENTRYPOINT
):
ENV DOTNET_EnableDiagnostics=0
Para obter mais informações sobre várias variáveis de ambiente .NET, consulte variáveis de ambiente .NET.
Observação
O .NET 6 padroniza o prefixo DOTNET_
em vez de COMPlus_
para variáveis de ambiente que configuram o comportamento em tempo de execução do .NET. No entanto, o prefixo COMPlus_
continuará a funcionar. Se você estiver usando uma versão anterior do tempo de execução do .NET, ainda deverá usar o prefixo COMPlus_
para variáveis de ambiente.
Criar um contêiner
Agora que você tem uma imagem que contém seu aplicativo, você pode criar um contêiner. Você pode criar um contêiner de duas maneiras. Primeiro, crie um novo contentor que esteja parado.
docker create --name core-counter counter-image
Este comando docker create
mostra o CONTAINER ID do contêiner (seu identificador será diferente):
d0be06126f7db6dd1cee369d911262a353c9b7fb4829a0c11b4b2eb7b2d429cf
Para ver uma lista de todos os contêineres, use o comando docker ps -a
:
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d0be06126f7d counter-image "dotnet DotNet.Docke…" 12 seconds ago Created core-counter
Gerir o contentor
O contêiner foi criado com um nome específico core-counter
. Esse nome é usado para gerenciar o contêiner. O exemplo a seguir usa o comando docker start
para iniciar o contêiner e, em seguida, usa o comando docker ps
para mostrar apenas os contêineres que estão em execução:
docker start core-counter
core-counter
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cf01364df453 counter-image "dotnet DotNet.Docke…" 53 seconds ago Up 10 seconds core-counter
Da mesma forma, o comando docker stop
para o contentor. O exemplo a seguir usa o comando docker stop
para parar o contêiner e, em seguida, usa o comando docker ps
para mostrar que nenhum contêiner está em execução:
docker stop core-counter
core-counter
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Conectar a um contentor
Depois que um contêiner estiver em execução, você poderá se conectar a ele para ver a saída. Use os comandos docker start
e docker attach
para iniciar o contêiner e espiar o fluxo de saída. Neste exemplo, o pressionamento de tecla Ctrl+C é usado para desanexar do contêiner em execução. Esse pressionamento de tecla termina o processo no contêiner, a menos que especificado de outra forma, o que pararia o contêiner. O parâmetro --sig-proxy=false
garante que Ctrl+C não pare o processo no contêiner.
Depois de desanexar do contêiner, reanexe para verificar se ele ainda está em execução e contando.
docker start core-counter
core-counter
docker attach --sig-proxy=false core-counter
Counter: 7
Counter: 8
Counter: 9
^C
docker attach --sig-proxy=false core-counter
Counter: 17
Counter: 18
Counter: 19
^C
Excluir um contêiner
Para este artigo, não queres contentores sem utilidade. Exclua o contêiner criado anteriormente. Se o contêiner estiver em execução, pare-o.
docker stop core-counter
O exemplo a seguir lista todos os contêineres. Em seguida, ele usa o comando docker rm
para excluir o contêiner e, em seguida, verifica uma segunda vez se há contêineres em execução.
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2f6424a7ddce counter-image "dotnet DotNet.Dock…" 7 minutes ago Exited (143) 20 seconds ago core-counter
docker rm core-counter
core-counter
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Corrida única
O Docker fornece o comando docker run
para criar e executar o contêiner como um único comando. Este comando elimina a necessidade de executar docker create
e, em seguida, docker start
. Você também pode definir esse comando para excluir automaticamente o contêiner quando ele parar. Por exemplo, use docker run -it --rm
para fazer duas coisas: primeiro, use automaticamente o terminal atual para se conectar ao contêiner e, quando o contêiner terminar, remova-o:
docker run -it --rm counter-image
Counter: 1
Counter: 2
Counter: 3
Counter: 4
Counter: 5
^C
O contêiner também passa parâmetros para a execução do aplicativo .NET. Para instruir o aplicativo .NET a contar apenas até três, insira 3.
docker run -it --rm counter-image 3
Counter: 1
Counter: 2
Counter: 3
Com docker run -it
, o comando Ctrl+C interrompe o processo em execução no contentor, o que, por sua vez, faz parar o contentor. Como o parâmetro --rm
foi fornecido, o contêiner é excluído automaticamente quando o processo é interrompido. Verifique se ele não existe:
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Alterar o ENTRYPOINT
O comando bash
ou cmd.exe
. Edite o comando conforme necessário.
Neste exemplo, ENTRYPOINT
é alterado para cmd.exe
.
Ctrl+C é pressionado para finalizar o processo e parar o contêiner.
docker run -it --rm --entrypoint "cmd.exe" counter-image
Microsoft Windows [Version 10.0.17763.379]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\>dir
Volume in drive C has no label.
Volume Serial Number is 3005-1E84
Directory of C:\
04/09/2019 08:46 AM <DIR> app
03/07/2019 10:25 AM 5,510 License.txt
04/02/2019 01:35 PM <DIR> Program Files
04/09/2019 01:06 PM <DIR> Users
04/02/2019 01:35 PM <DIR> Windows
1 File(s) 5,510 bytes
4 Dir(s) 21,246,517,248 bytes free
C:\>^C
Observação
Este exemplo só funciona em contêineres do Windows. Os contêineres Linux não têm cmd.exe
.
Comandos essenciais
O Docker tem muitos comandos diferentes que criam, gerenciam e interagem com contêineres e imagens. Esses comandos do Docker são essenciais para gerenciar seus contêineres:
- de compilação do docker
- docker executar
- docker ps
- docker parar
- docker rm
- docker rmi
- imagem do docker
Limpar recursos
Durante este tutorial, você criou contêineres e imagens. Se desejar, exclua esses recursos. Use os seguintes comandos para
Listar todos os contêineres
docker ps -a
Pare os contêineres que estão sendo executados pelo nome.
docker stop core-counter
Excluir o contêiner
docker rm core-counter
Em seguida, exclua todas as imagens que você não deseja mais em sua máquina. Exclua a imagem criada pelo seu
docker rmi counter-image:latest
docker rmi mcr.microsoft.com/dotnet/aspnet:9.0
docker rmi counter-image:latest
docker rmi mcr.microsoft.com/dotnet/aspnet:8.0
Use o comando docker images
para ver uma lista de imagens instaladas.
Dica
Os arquivos de imagem podem ser grandes. Normalmente, você removeria contêineres temporários criados durante o teste e o desenvolvimento do aplicativo. Você geralmente mantém as imagens base com o tempo de execução instalado se planeja criar outras imagens com base nesse tempo de execução.