Tutorial: Contentorizar uma aplicação .NET
Neste tutorial, você aprenderá como colocar em contêineres 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:
- Criar e publicar um aplicativo .NET simples
- Criar e configurar um Dockerfile para .NET
- Compilar uma imagem do Docker
- Criar e executar um contêiner do Docker
Você entenderá as tarefas de compilação e implantação do contêiner do Docker para um aplicativo .NET. A plataforma Docker usa o mecanismo Docker para criar e empacotar aplicativos rapidamente como imagens do Docker. Essas imagens são gravadas no formato Dockerfile para serem implantadas e executadas em um contêiner em camadas.
Nota
Este tutorial não é para aplicativos ASP.NET Core. Se estiver a utilizar o ASP.NET Core, consulte o tutorial Saiba como contentorizar uma aplicação ASP.NET Core.
Pré-requisitos
Instale os seguintes pré-requisitos:
- SDK do .NET 8+
Se você tiver o .NET instalado, use odotnet --info
comando 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.
- SDK do .NET 7+
Se você tiver o .NET instalado, use odotnet --info
comando 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 aplicação .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 tem a seguinte aparência:
📁 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 dotnet new
comando cria uma nova pasta chamada App e gera um aplicativo de console "Hello World". Altere os diretórios e navegue até a pasta Aplicativo, a partir da sessão do terminal. Use o comando para iniciar o dotnet run
aplicativo. O aplicativo é executado e imprima 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.
Gorjeta
Se você estiver usando o Visual Studio Code, na sessão de terminal anterior, digite o seguinte comando:
code .
Isso abrirá a pasta App que contém o projeto no Visual Studio Code.
O Program.cs deve se parecer com o 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
o . Lembre-se que este aplicativo é executado indefinidamente. Use o comando cancelar Ctrl+C para pará-lo. Segue-se um 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 só contará até esse valor e, em seguida, sair. Experimente contar até dotnet run -- 5
cinco.
Importante
Quaisquer parâmetros depois --
não são passados para o comando e, em vez disso, são passados para o dotnet run
seu aplicativo.
Publicar aplicativo .NET
Antes de adicionar o aplicativo .NET à imagem do Docker, primeiro ele deve ser publicado. É melhor fazer com que o contêiner execute a versão publicada do aplicativo. Para publicar o aplicativo, execute o seguinte comando:
dotnet publish -c Release
Este comando compila seu aplicativo para a pasta de publicação . O caminho para a pasta de publicação da pasta de trabalho deve ser .\App\bin\Release\net8.0\publish\
.
Este comando compila seu aplicativo para a pasta de publicação . O caminho para a pasta de publicação da pasta de trabalho deve ser .\App\bin\Release\net7.0\publish\
.
Na pasta Aplicativo , obtenha uma listagem de diretório da pasta de publicação para verificar se o arquivo DotNet.Docker.dll foi criado.
dir .\bin\Release\net8.0\publish\
Directory: C:\Users\default\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
dir .\bin\Release\net7.0\publish\
Directory: C:\Users\default\App\bin\Release\net7.0\publish
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2/13/2023 1:52 PM 431 DotNet.Docker.deps.json
-a--- 2/13/2023 1:52 PM 6144 DotNet.Docker.dll
-a--- 2/13/2023 1:52 PM 153600 DotNet.Docker.exe
-a--- 2/13/2023 1:52 PM 11052 DotNet.Docker.pdb
-a--- 2/13/2023 1:52 PM 253 DotNet.Docker.runtimeconfig.json
Criar o Dockerfile
O arquivo Dockerfile é usado pelo docker build
comando para criar uma imagem de contêiner. Este arquivo é um arquivo de texto chamado Dockerfile que não tem uma extensão.
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:8.0 AS build-env
WORKDIR /App
# Copy everything
COPY . ./
# Restore as distinct layers
RUN dotnet restore
# Build and publish a release
RUN dotnet publish -c Release -o out
# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /App
COPY --from=build-env /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]
Nota
A imagem de tempo de execução do ASP.NET Core é usada intencionalmente aqui, embora a mcr.microsoft.com/dotnet/runtime:8.0
imagem possa ter sido usada.
Gorjeta
Esse Dockerfile usa compilações de vários estágios, o que otimiza 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 FROM
palavra-chave 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 dotnet
segmento é o repositório de contêiner, enquanto o ou aspnet
segmento é o sdk
nome da imagem do contêiner. A imagem é marcada com 8.0
, que é usado para controle de versão. Assim, mcr.microsoft.com/dotnet/aspnet:8.0
é o tempo de execução do .NET 8.0. Certifique-se de puxar a versão de tempo de execução que corresponde ao tempo de execução direcionado pelo seu SDK. Por exemplo, o aplicativo criado na seção anterior usava o SDK do .NET 8.0 e a imagem base referida no Dockerfile é marcada com 8.0.
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 no . Página do Docker Hub da NET.
Salve o arquivo Dockerfile . 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 foram 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
└──...
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build-env
WORKDIR /App
# Copy everything
COPY . ./
# Restore as distinct layers
RUN dotnet restore
# Build and publish a release
RUN dotnet publish -c Release -o out
# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:7.0
WORKDIR /App
COPY --from=build-env /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]
Nota
A imagem de tempo de execução do ASP.NET Core é usada intencionalmente aqui, embora a mcr.microsoft.com/dotnet/runtime:7.0
imagem possa ter sido usada.
Gorjeta
Esse Dockerfile usa compilações de vários estágios, o que otimiza 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 FROM
palavra-chave 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 dotnet
segmento é o repositório de contêiner, enquanto o ou aspnet
segmento é o sdk
nome da imagem do contêiner. A imagem é marcada com 7.0
, que é usado para controle de versão. Assim, mcr.microsoft.com/dotnet/aspnet:7.0
é o tempo de execução do .NET 7.0. Certifique-se de puxar a versão de tempo de execução que corresponde ao tempo de execução direcionado pelo seu SDK. Por exemplo, o aplicativo criado na seção anterior usou o SDK do .NET 7.0 e a imagem base mencionada no Dockerfile é marcada com 7.0.
Salve o arquivo Dockerfile . 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 foram omitidos para economizar espaço no artigo:
📁 docker-working
└──📂 App
├── Dockerfile
├── DotNet.Docker.csproj
├── Program.cs
├──📂 bin
│ └──📂 Release
│ └──📂 net7.0
│ └──📂 publish
│ ├── DotNet.Docker.deps.json
│ ├── DotNet.Docker.exe
│ ├── DotNet.Docker.dll
│ ├── DotNet.Docker.pdb
│ └── DotNet.Docker.runtimeconfig.json
└──📁 obj
└──...
No seu terminal, execute o seguinte comando:
docker build -t counter-image -f Dockerfile .
O Docker processará cada linha no Dockerfile. O .
comando in define docker build
o contexto de compilação da imagem. O -f
switch é o caminho para o Dockerfile. Este comando cria a imagem e cria um repositório local chamado counter-image que aponta para essa imagem. Após a conclusão deste comando, execute docker images
para ver uma lista de imagens instaladas:
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
counter-image latest 2f15637dc1f6 10 minutes ago 217MB
O counter-image
repositório é o nome da imagem. A latest
tag é a tag usada para identificar a imagem. O 2f15637dc1f6
é o ID da imagem. O 10 minutes ago
é o tempo em que a imagem foi criada. O 217MB
é o tamanho da imagem. 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:8.0
WORKDIR /App
COPY --from=build-env /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
counter-image latest 2f15637dc1f6 10 minutes ago 208MB
O counter-image
repositório é o nome da imagem. A latest
tag é a tag usada para identificar a imagem. O 2f15637dc1f6
é o ID da imagem. O 10 minutes ago
é o tempo em que a imagem foi criada. O 208MB
é o tamanho da imagem. 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:7.0
WORKDIR /App
COPY --from=build-env /App/out .
ENTRYPOINT ["dotnet", "DotNet.Docker.dll"]
O COPY
comando diz ao Docker para copiar a pasta especificada no seu computador para uma pasta no contêiner. Neste exemplo, a pasta de publicação é copiada para uma pasta chamada App/out no contêiner.
O WORKDIR
comando altera o diretório atual dentro do contêiner para 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 ENTRYPOINT
comando é executado. Quando esse comando terminar, o contêiner será interrompido automaticamente.
Gorjeta
Antes do .NET 8, os contêineres configurados para serem executados como somente leitura podem falhar com Failed to create CoreCLR, HRESULT: 0x8007000E
o . Para resolver esse problema, especifique uma variável de DOTNET_EnableDiagnostics
ambiente como 0
(imediatamente antes da ENTRYPOINT
etapa):
ENV DOTNET_EnableDiagnostics=0
Para obter mais informações sobre várias variáveis de ambiente .NET, consulte Variáveis de ambiente .NET.
Nota
O .NET 6 padroniza o prefixo DOTNET_
em vez de variáveis de ambiente que configuram o comportamento em tempo de execução do COMPlus_
.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 contentor
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 contêiner que seja interrompido.
docker create --name core-counter counter-image
Este docker create
comando cria um contêiner com base na imagem da contraimagem . A saída desse comando mostra o ID do CONTAINER (o seu será diferente) do contêiner criado:
d0be06126f7db6dd1cee369d911262a353c9b7fb4829a0c11b4b2eb7b2d429cf
Para ver uma lista de todos os contêineres, use o docker ps -a
comando:
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, esse nome core-counter
é usado para gerenciar o contêiner. O exemplo a seguir usa o comando para iniciar o contêiner e, em seguida, usa o docker start
docker ps
comando 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 para o docker stop
contêiner. O exemplo a seguir usa o comando para parar o contêiner e, em seguida, usa o docker stop
docker ps
comando 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
Ligar 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 docker start
comandos e para iniciar o contêiner e docker attach
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 --sig-proxy=false
parâmetro garante que Ctrl+C não interrompa 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
Eliminar um contentor
Para este artigo, você não quer contêineres pendurados que não fazem nada. 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 para excluir o docker rm
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 para criar e executar o docker run
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 para fazer duas coisas, primeiro, use docker run -it --rm
automaticamente o terminal atual para se conectar ao contêiner e, em seguida, 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 três, passe em 3.
docker run -it --rm counter-image 3
Counter: 1
Counter: 2
Counter: 3
Com docker run -it
o , o comando Ctrl+C interrompe o processo que está sendo executado no contêiner, que, por sua vez, para o contêiner. Como o parâmetro foi fornecido, o contêiner é excluído automaticamente quando o --rm
processo é interrompido. Verifique se ele não existe:
docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Alterar o ENTRYPOINT
O docker run
comando também permite modificar o ENTRYPOINT
comando do Dockerfile e executar outra coisa, mas apenas para esse contêiner. Por exemplo, use o seguinte comando para executar 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
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:
- compilação do docker
- execução do docker
- Docker PS
- Parada do Docker
- Docker RM
- RMI do Docker
- Imagem do Docker
Clean up resources (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
Eliminar o contentor
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 Dockerfile e, em seguida, exclua a imagem .NET na qual o Dockerfile foi baseado. Você pode usar o ID da imagem ou a cadeia de caracteres formatada REPOSITORY:TAG.
docker rmi counter-image:latest
docker rmi mcr.microsoft.com/dotnet/aspnet:8.0
docker rmi counter-image:latest
docker rmi mcr.microsoft.com/dotnet/aspnet:7.0
Use o docker images
comando para ver uma lista de imagens instaladas.
Gorjeta
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.