Colocar em contêiner um aplicativo .NET com dotnet publish

Os contêineres têm muitos recursos e benefícios, como o de serem uma infraestrutura imutável, fornecer uma arquitetura portátil e permitir a 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ê aprenderá a colocar em contêiner um aplicativo .NET usando o comando dotnet publish.

Pré-requisitos

Instale os seguintes pré-requisitos:

Além desses pré-requisitos, é recomendável que você esteja familiarizado com os Serviços de trabalho no .NET.

Criar aplicativo .NET

Você precisa de um aplicativo .NET para colocar em contêiner. Por isso, comece criando um novo aplicativo a partir de um modelo. Abra o terminal, crie uma pasta de trabalho (sample-directory) caso ainda não tenha feito isso e altere os diretórios para que você esteja nela. Na pasta de trabalho, execute o seguinte comando para criar um projeto em um subdiretório denominado Trabalho:

dotnet new worker -o Worker -n DotNet.ContainerImage

A árvore de pastas será parecida com a seguinte:

📁 sample-directory
    └──📂 Worker
        ├──appsettings.Development.json
        ├──appsettings.json
        ├──DotNet.ContainerImage.csproj
        ├──Program.cs
        ├──Worker.cs
        └──📂 obj
            ├── DotNet.ContainerImage.csproj.nuget.dgspec.json
            ├── DotNet.ContainerImage.csproj.nuget.g.props
            ├── DotNet.ContainerImage.csproj.nuget.g.targets
            ├── project.assets.json
            └── project.nuget.cache

O comando dotnet new cria uma nova pasta chamada Trabalho e gera um serviço de trabalho que, quando executado, registra uma mensagem a cada segundo. Na sessão do terminal, altere os diretórios e navegue até a pasta Trabalhador. Use o comando dotnet run para iniciar o aplicativo.

dotnet run
Building...
info: DotNet.ContainerImage.Worker[0]
      Worker running at: 10/18/2022 08:56:00 -05:00
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: .\Worker
info: DotNet.ContainerImage.Worker[0]
      Worker running at: 10/18/2022 08:56:01 -05:00
info: DotNet.ContainerImage.Worker[0]
      Worker running at: 10/18/2022 08:56:02 -05:00
info: DotNet.ContainerImage.Worker[0]
      Worker running at: 10/18/2022 08:56:03 -05:00
info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
Attempting to cancel the build...

O modelo de trabalho faz loops indefinidamente. Use o comando de cancelamento Ctrl+C para interrompê-lo.

Adicionar pacote NuGet

No momento, o pacote NuGet Microsoft.NET.Build.Containers é necessário para publicar projetos que não sejam da Web como um contêiner. Para adicionar o pacote NuGet Microsoft.NET.Build.Containers ao modelo de trabalho, execute o seguinte comando dotnet add package:

dotnet add package Microsoft.NET.Build.Containers

Dica

Se estiver criando um aplicativo Web e usando o .NET SDK 7.0.300 ou superior, o pacote não será necessário: o SDK contém a mesma funcionalidade pronta para uso.

Definir o nome da imagem de contêiner

Há várias opções de configuração disponíveis quando você publica um aplicativo como um contêiner.

Por padrão, o nome da imagem de contêiner é o AssemblyName do projeto. Se esse nome for inválido como um nome de imagem de contêiner, substitua-o especificando ContainerRepository, conforme mostrado no seguinte arquivo de projeto:

<Project Sdk="Microsoft.NET.Sdk.Worker">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <UserSecretsId>dotnet-DotNet.ContainerImage-2e40c179-a00b-4cc9-9785-54266210b7eb</UserSecretsId>
    <ContainerRepository>dotnet-worker-image</ContainerRepository>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
  </ItemGroup>
</Project>

Para obter mais informações, confira ContainerRepository.

Por padrão, o nome da imagem de contêiner é o AssemblyName do projeto. Se esse nome for inválido como um nome de imagem de contêiner, você poderá substituí-lo especificando um (ContainerImageName obsoleto) ou o preferido ContainerRepository, conforme mostrado no seguinte arquivo do projeto:

<Project Sdk="Microsoft.NET.Sdk.Worker">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <UserSecretsId>dotnet-DotNet.ContainerImage-2e40c179-a00b-4cc9-9785-54266210b7eb</UserSecretsId>
    <ContainerImageName>dotnet-worker-image</ContainerImageName>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
    <PackageReference Include="Microsoft.NET.Build.Containers" Version="7.0.401" />
  </ItemGroup>
</Project>

Para obter mais informações, confira ContainerImageName.

Publicar aplicativo .NET

Para publicar o aplicativo .NET como um contêiner, use o seguinte comando dotnet publish:

dotnet publish --os linux --arch x64 /t:PublishContainer -c Release

O comando anterior da CLI do .NET publica o aplicativo como um contêiner:

  • Direcionar o Linux como o sistema operacional (--os linux).
  • Especificar uma arquitetura x64 (--arch x64).
  • Usar a configuração de versão (-c Release).

Importante

Para compilar o contêiner localmente, você deve ter o daemon do Docker em execução. Se ele não estiver em execução ao tentar publicar o aplicativo como um contêiner, você verá um erro semelhante ao seguinte:

..\build\Microsoft.NET.Build.Containers.targets(66,9): error MSB4018:
   The "CreateNewImage" task failed unexpectedly. [..\Worker\DotNet.ContainerImage.csproj]

Dica

Dependendo do tipo de aplicativo que você está colocando em contêiner, as opções de linha de comando (opções) podem variar. Por exemplo, o argumento /t:PublishContainer só é necessário para aplicativos .NET não Web, como modelos console e worker. Para modelos da Web, substitua o argumento /t:PublishContainer por -p:PublishProfile=DefaultContainer. Para obter mais informações, confira Builds de contêiner do SDK do .NET, problema 141.

O comando produz uma saída semelhante ao exemplo de saída:

Determining projects to restore...
  All projects are up-to-date for restore.
  DotNet.ContainerImage -> .\Worker\bin\Release\net8.0\linux-x64\DotNet.ContainerImage.dll
  DotNet.ContainerImage -> .\Worker\bin\Release\net8.0\linux-x64\publish\
  Building image 'dotnet-worker-image' with tags latest on top of base image mcr.microsoft.com/dotnet/aspnet:8.0
  Pushed container 'dotnet-worker-image:latest' to Docker daemon
Determining projects to restore...
  All projects are up-to-date for restore.
  DotNet.ContainerImage -> .\Worker\bin\Release\net7.0\linux-x64\DotNet.ContainerImage.dll
  DotNet.ContainerImage -> .\Worker\bin\Release\net7.0\linux-x64\publish\
  Building image 'dotnet-worker-image' with tags 1.0.0 on top of base image mcr.microsoft.com/dotnet/aspnet:7.0
  Pushed container 'dotnet-worker-image:1.0.0' to Docker daemon

Este comando compila seu aplicativo de trabalho para a pasta publish e envia o contêiner por push para o registro local do Docker.

Configurar a imagem de contêiner

Você pode controlar muitos aspectos do contêiner gerado por meio das propriedades do MSBuild. Em geral, se você puder usar um comando em um Dockerfile para definir alguma configuração, poderá fazer o mesmo por meio do MSBuild.

Observação

As únicas exceções a isso são comandos RUN. Devido a forma em que os contêineres são criados, eles não podem ser emulados. Se você precisar dessa funcionalidade, precisará usar um Dockerfile para criar suas imagens de contêiner.

ContainerArchiveOutputPath

A partir do .NET 8, você pode criar um contêiner diretamente como um arquivo tar.gz. Esse recurso será útil se o fluxo de trabalho não for simples e exigir que você, por exemplo, execute uma ferramenta de verificação em suas imagens antes de efetuar push delas. Depois que o arquivo é criado, você pode movê-lo, examiná-lo ou carregá-lo em uma cadeia de ferramentas local do Docker.

Para publicar em um arquivo morto, adicione a propriedade ContainerArchiveOutputPath ao seu comando dotnet publish, por exemplo:

dotnet publish \
  -p PublishProfile=DefaultContainer \
  -p ContainerArchiveOutputPath=./images/sdk-container-demo.tar.gz

Você pode especificar um nome de pasta ou um caminho com um nome de arquivo específico. Se você especificar o nome da pasta, o nome do arquivo gerado para o arquivo de imagem será $(ContainerRepository).tar.gz. Esses arquivos podem conter várias tags dentro deles, apenas como um único arquivo é criado para todos os arquivos ContainerImageTags.

Configuração de nomenclatura de imagem de contêiner

As imagens de contêiner seguem uma convenção de nomenclatura específica. O nome da imagem é composto por várias partes, o registro, a porta opcional, o repositório e a marca e a família opcionais.

REGISTRY[:PORT]/REPOSITORY[:TAG[-FAMILY]]

Por exemplo, considere o nome da imagem totalmente qualificada mcr.microsoft.com/dotnet/runtime:8.0-alpine:

  • mcr.microsoft.com é o registro (e, neste caso, representa o registro de contêiner da Microsoft).
  • dotnet/runtime é o repositório (mas alguns consideram isso o user/repository).
  • 8.0-alpine é a marca, e a família (a família é um especificador opcional que ajuda a desambiguar o empacotamento do sistema operacional).

Algumas propriedades descritas nas seções a seguir correspondem ao gerenciamento de partes do nome da imagem gerada. Considere a tabela a seguir que mapeia a relação entre o nome da imagem e as propriedades de compilação:

Parte do nome da imagem Propriedade do MSBuild Valores de exemplo
REGISTRY[:PORT] ContainerRegistry mcr.microsoft.com:443
PORT ContainerPort :443
REPOSITORY ContainerRepository dotnet/runtime
TAG ContainerImageTag 8.0
FAMILY ContainerFamily -alpine
Parte do nome da imagem Propriedade do MSBuild Valores de exemplo
REGISTRY[:PORT] ContainerRegistry mcr.microsoft.com:443
PORT ContainerPort :443
REPOSITORY ContainerImageName dotnet/runtime
TAG ContainerImageTag 8.0

As seções a seguir descrevem as várias propriedades que podem ser usadas para controlar a imagem de contêiner gerada.

ContainerBaseImage

A propriedade de imagem base do contêiner controla a imagem usada como base para sua imagem. Por padrão, os seguintes valores são inferidos com base nas propriedades do seu projeto:

  • Se o projeto for autocontido, a imagem mcr.microsoft.com/dotnet/runtime-deps será usada como a imagem base.
  • Se o projeto for um projeto ASP.NET Core, a imagem mcr.microsoft.com/dotnet/aspnet será usada como a imagem base.
  • Caso contrário, a imagem mcr.microsoft.com/dotnet/runtime será usada como a imagem base.

A tag da imagem é inferida para ser o componente numérico do TargetFramework escolhido. Por exemplo, um direcionamento de projeto net6.0 resulta na marca 6.0 da imagem base inferida, e um projeto net7.0-linux usa a marca 7.0 etc.

Se você definir um valor aqui, deverá definir o nome totalmente qualificado da imagem para usar como base, incluindo qualquer tag que preferir:

<PropertyGroup>
    <ContainerBaseImage>mcr.microsoft.com/dotnet/runtime:8.0</ContainerBaseImage>
</PropertyGroup>
<PropertyGroup>
    <ContainerBaseImage>mcr.microsoft.com/dotnet/runtime:7.0</ContainerBaseImage>
</PropertyGroup>

ContainerFamily

A partir do .NET 8, você pode usar a propriedade ContainerFamily do MSBuild para escolher uma família diferente de imagens de contêiner fornecidas pela Microsoft como a imagem base para seu aplicativo. Quando definido, esse valor é acrescentado ao final da marca específica do TFM selecionado, alterando a marca fornecida. Por exemplo, para usar as variantes do Alpine Linux das imagens base do .NET, defina ContainerFamily como alpine:

<PropertyGroup>
    <ContainerFamily>alpine</ContainerFamily>
</PropertyGroup>

A configuração do projeto anterior resulta em uma marca final igual a 8.0-alpine para um aplicativo direcionado ao .NET 8.

Esse campo é de forma livre e, em geral, pode ser usado para selecionar diferentes distribuições do sistema operacional, configurações de pacote padrão ou qualquer outra variante de alterações em uma imagem base. Esse campo é ignorado quando ContainerBaseImage é definido. Para obter mais informações, consulte imagens de contêiner do .NET.

ContainerRuntimeIdentifier

A propriedade do identificador de runtime do contêiner controla o sistema operacional e a arquitetura usados pelo contêiner, caso a ContainerBaseImage dê suporte a mais de uma plataforma. Por exemplo, a imagem mcr.microsoft.com/dotnet/runtime atualmente dá suporte todas as imagens linux-x64, linux-arm, linux-arm64e win10-x64 atrás da mesma marca, portanto, o ferramental precisa saber qual dessas versões você pretende usar. Por padrão, isso é definido como o valor de RuntimeIdentifier que você escolheu ao publicar o contêiner. Essa propriedade raramente precisa ser definida explicitamente – em vez disso, use a opção -r para o comando dotnet publish. Se a imagem escolhida não der suporte ao RuntimeIdentifier escolhido, isso resultará em um erro que descreve os RuntimeIdentifiers aos quais a imagem dá suporte.

Você pode definir a propriedade ContainerBaseImage como um nome de imagem totalmente qualificado, incluindo a marca, para evitar a necessidade de usar essa propriedade.

<PropertyGroup>
    <ContainerRuntimeIdentifier>linux-arm64</ContainerRuntimeIdentifier>
</PropertyGroup>

Para obter mais informações sobre os identificadores de runtime com suporte no .NET, confira Catálogo de RIDs.

ContainerRegistry

A propriedade do registro de contêiner controla o registro de destino, o local para o qual a imagem recém-criada será enviada por push. Por padrão, ele é enviado por push para o daemon local do Docker, mas você também pode especificar um registro remoto. Ao usar um registro remoto que requer autenticação, você se autentica usando os mecanismos conhecidos docker login. Para obter mais informações, confira Autenticação em registros de contêiner para ver mais detalhes. Para obter um exemplo concreto de como usar essa propriedade, considere o seguinte exemplo de XML:

<PropertyGroup>
    <ContainerRegistry>registry.mycorp.com:1234</ContainerRegistry>
</PropertyGroup>

Essa ferramenta dá suporte à publicação em qualquer registro que dê suporte à API HTTP do Registro do Docker V2. Isso inclui os seguintes registros explicitamente (e provavelmente muitos mais implicitamente):

Para obter notas sobre como trabalhar com esses registros, confira as notas específicas do registro.

ContainerRepository

O repositório de contêiner é o nome da própria imagem, por exemplo, dotnet/runtime ou my-app. Por padrão, o AssemblyName do projeto é usado.

<PropertyGroup>
    <ContainerRepository>my-app</ContainerRepository>
</PropertyGroup>

ContainerImageName

O nome da imagem de contêiner controla o nome da própria imagem, por exemplo, dotnet/runtime ou my-app. Por padrão, o AssemblyName do projeto é usado.

<PropertyGroup>
    <ContainerImageName>my-app</ContainerImageName>
</PropertyGroup>

Observação

A partir do .NET 8, ContainerImageName é preterido em favor de ContainerRepository.

Os nomes de imagem consistem em um ou mais segmentos delimitados por barra, cada um dos quais pode conter apenas caracteres alfanuméricos minúsculos, pontos, sublinhados e traços e deve começar com uma letra ou número. Qualquer outro caractere resultará em um erro.

ContainerImageTag(s)

A propriedade de tag de imagem de contêiner controla as tags geradas para a imagem. Para especificar uma marca individual, use ContainerImageTag e, para várias marcas, use ContainerImageTags.

Importante

Ao usar ContainerImageTags, você acabará com várias imagens, uma por marca exclusiva.

Em geral, as marcas são usadas para se referir a diferentes versões de um aplicativo, mas também podem se referir a diferentes distribuições do sistema operacional ou até a configurações distintas.

A partir do .NET 8, quando uma marca não é fornecida, o padrão é latest.

Por padrão, o Version do projeto é usado como o valor da tag.

Para substituir o padrão, especifique um dos seguintes:

<PropertyGroup>
    <ContainerImageTag>1.2.3-alpha2</ContainerImageTag>
</PropertyGroup>

Para especificar várias tags, use um conjunto delimitado por ponto e vírgula de tags na propriedade ContainerImageTags, semelhante à configuração de vários TargetFrameworks:

<PropertyGroup>
    <ContainerImageTags>1.2.3-alpha2;latest</ContainerImageTags>
</PropertyGroup>

As tags só podem conter até 127 caracteres alfanuméricos, pontos, sublinhados e traços. Eles devem começar com um caractere alfanumérico ou um sublinhado. Qualquer outro formato resultará em um erro.

Observação

Quando ContainerImageTags são usadas, as marcas são delimitadas por um caractere ;. Se você estiver chamando dotnet publish por meio da linha de comando (como é o caso da maioria dos ambientes de CI/CD), faça o encapsulamento externo dos valores com ' simples e o encapsulamento interno com aspas duplas ", por exemplo (='"tag-1;tag-2"'). Considere o seguinte comando dotnet publish:

dotnet publish -p ContainerImageTags='"1.2.3-alpha2;latest"'

Isso resulta em duas imagens sendo geradas: my-app:1.2.3-alpha2 e my-app:latest.

Dica

Se você tiver problemas com a propriedade ContainerImageTags, considere definir o escopo de uma variável de ambiente ContainerImageTags:

ContainerImageTags='1.2.3;latest' dotnet publish

ContainerLabel

O rótulo do contêiner adiciona um rótulo de metadados ao contêiner. Os rótulos não têm impacto sobre o contêiner em tempo de execução, mas costumam ser usados para armazenar metadados de versão e criação para uso dos verificadores de segurança e outras ferramentas da infraestrutura. Você pode especificar qualquer número de rótulos de contêiner.

O nó ContainerLabel tem dois atributos:

  • Include: a chave do rótulo.
  • Value: o valor do rótulo (pode estar vazio).
<ItemGroup>
    <ContainerLabel Include="org.contoso.businessunit" Value="contoso-university" />
</ItemGroup>

Para obter uma lista de rótulos criados por padrão, confira rótulos de contêiner padrão.

Configurar a execução do contêiner

Para controlar a execução do contêiner, use as propriedades do MSBuild a seguir.

ContainerWorkingDirectory

O nó do diretório de trabalho do contêiner controla o diretório de trabalho do contêiner, o diretório no qual os comandos serão executados se não houver outro comando executado.

Por padrão, o valor do diretório /app é usado como o diretório de trabalho.

<PropertyGroup>
    <ContainerWorkingDirectory>/bin</ContainerWorkingDirectory>
</PropertyGroup>

ContainerPort

A porta do contêiner adiciona portas TCP ou UDP à lista de portas conhecidas para o contêiner. Isso permite que runtimes de contêiner como o Docker mapeiem essas portas para o computador host automaticamente. Isso geralmente é usado como documentação para o contêiner, mas também pode ser usado para habilitar o mapeamento automático de porta.

O nó ContainerPort tem dois atributos:

  • Include: o número de porta a se exposto.
  • Type: o padrão é tcp, os valores válidos são tcp ou udp.
<ItemGroup>
    <ContainerPort Include="80" Type="tcp" />
</ItemGroup>

A partir do .NET 8, a ContainerPort é inferida quando não é fornecida explicitamente com base em diversas variáveis de ambiente conhecidas do ASP.NET:

  • ASPNETCORE_URLS
  • ASPNETCORE_HTTP_PORTS
  • ASPNETCORE_HTTPS_PORTS

Se essas variáveis de ambiente estiverem presentes, os respectivos valores serão analisados e convertidos em mapeamentos de porta TCP. Essas variáveis de ambiente são lidas da imagem base, se houver, ou das variáveis de ambiente definidas no projeto por meio de itens ContainerEnvironmentVariable. Para obter mais informações, confira ContainerEnvironmentVariable.

ContainerEnvironmentVariable

O nó de variável de ambiente de contêiner permite que você adicione variáveis de ambiente ao contêiner. As variáveis de ambiente são acessíveis para o aplicativo em execução no contêiner imediatamente e geralmente são usadas para alterar o comportamento de runtime do aplicativo em execução.

O nó ContainerEnvironmentVariable tem dois atributos:

  • Include: o nome da variável de ambiente.
  • Value: o valor da variável de ambiente.
<ItemGroup>
  <ContainerEnvironmentVariable Include="LOGGER_VERBOSITY" Value="Trace" />
</ItemGroup>

Para obter mais informações, confira Variáveis de ambiente do .NET.

Configurar comandos de contêiner

Por padrão, as ferramentas de contêiner iniciam seu aplicativo usando o binário AppHost gerado para o aplicativo (caso o aplicativo use um AppHost) ou o comando dotnet mais a DLL do aplicativo.

No entanto, você pode controlar como o aplicativo é executado usando uma combinação de ContainerAppCommand, ContainerAppCommandArgs, ContainerDefaultArgs e ContainerAppCommandInstruction.

Esses diferentes pontos de configuração existem porque imagens base diferentes usam combinações distintas do contêiner ENTRYPOINT e das propriedades COMMAND, e o ideal é conseguir dar suporte a todas elas. Os padrões devem ser utilizáveis na maioria dos aplicativos, mas caso deseje personalizar o comportamento de inicialização do aplicativo, você deve:

  • Identificar o binário a ser executado e defini-lo como ContainerAppCommand
  • Identificar quais argumentos são obrigatórios para que o aplicativo seja executado e defini-los como ContainerAppCommandArgs
  • Identificar quais argumentos (se houver) são opcionais e podem ser substituídos por um usuário e defini-los como ContainerDefaultArgs
  • Defina ContainerAppCommandInstruction como DefaultArgs

Para obter mais informações, confira os itens de configuração a seguir.

ContainerAppCommand

O item de configuração do comando do aplicativo é o ponto de entrada lógico do aplicativo. Na maioria dos aplicativos, esse é o AppHost, o binário executável gerado para o aplicativo. Se o aplicativo não gerar um AppHost, esse comando normalmente será dotnet <your project dll>. Esses valores são aplicados após qualquer ENTRYPOINT no contêiner base ou diretamente se nenhum ENTRYPOINT foi definido.

A configuração ContainerAppCommand tem uma propriedade Include individual, que representa o comando, a opção ou o argumento a ser usado no comando de ponto de entrada:

<ItemGroup Label="ContainerAppCommand Assignment">
  <!-- This is how you would start the dotnet ef tool in your container -->
  <ContainerAppCommand Include="dotnet" />
  <ContainerAppCommand Include="ef" />

  <!-- This shorthand syntax means the same thing, note the semicolon separating the tokens. -->
  <ContainerAppCommand Include="dotnet;ef" />
</ItemGroup>

ContainerAppCommandArgs

Esse item de configuração de argumentos do comando do aplicativo representa todos os argumentos logicamente necessários para o aplicativo que devem ser aplicados ao ContainerAppCommand. Por padrão, não é gerado nenhum para um aplicativo. Quando presentes, os argumentos são aplicados ao contêiner quando ele é executado.

A configuração ContainerAppCommandArgs tem uma propriedade Include individual, que representa a opção ou o argumento a ser aplicado ao comando ContainerAppCommand.

<ItemGroup>
  <!-- Assuming the ContainerAppCommand defined above, 
       this would be the way to force the database to update.
  -->
  <ContainerAppCommandArgs Include="database" />
  <ContainerAppCommandArgs Include="update" />

  <!-- This is the shorthand syntax for the same idea -->
  <ContainerAppCommandArgs Include="database;update" />
</ItemGroup>

ContainerDefaultArgs

Esse item de configuração de argumentos padrão representa todos os argumentos substituíveis pelo usuário para o aplicativo. Essa é uma boa maneira de fornecer padrões dos quais o aplicativo poderá precisar para ser executado de uma forma que facilite a inicialização, mas ainda seja fácil de personalizar.

A configuração ContainerDefaultArgs tem uma propriedade Include individual, que representa a opção ou o argumento a ser aplicado ao comando ContainerAppCommand.

<ItemGroup>
  <!-- Assuming the ContainerAppCommand defined above, 
       this would be the way to force the database to update.
  -->
  <ContainerDefaultArgs Include="database" />
  <ContainerDefaultArgs Include="update" />

  <!-- This is the shorthand syntax for the same idea -->
  <ContainerDefaultArgs Include="database;update" />
</ItemGroup>

ContainerAppCommandInstruction

A configuração de instrução do comando do aplicativo ajuda a controlar a maneira como ContainerEntrypoint, ContainerEntrypointArgs, ContainerAppCommand, ContainerAppCommandArgs e ContainerDefaultArgs são combinados para formar o comando final que é executado no contêiner. Isso depende muito de um ENTRYPOINT estar presente na imagem base. Essa propriedade usa um destes três valores: "DefaultArgs", "Entrypoint" ou "None".

  • Entrypoint:
    • Nesse modo, o ponto de entrada é definido por ContainerAppCommand, ContainerAppCommandArgs e ContainerDefaultArgs.
  • None:
    • Nesse modo, o ponto de entrada é definido por ContainerEntrypoint, ContainerEntrypointArgs e ContainerDefaultArgs.
  • DefaultArgs:
    • Esse é o modo mais complexo, caso nenhum dos itens ContainerEntrypoint[Args] esteja presente, ContainerAppCommand[Args] e ContainerDefaultArgs são usados para criar o ponto de entrada e o comando. O ponto de entrada para as imagens base que o têm codificado em dotnet ou /usr/bin/dotnet é ignorado, para que você tenha controle completo.
    • Se ContainerEntrypoint e ContainerAppCommand estiverem presentes, ContainerEntrypoint passará a ser o ponto de entrada e ContainerAppCommand passará a ser o comando.

Observação

Os itens de configuração ContainerEntrypoint e ContainerEntrypointArgs foram preteridos a partir do .NET 8.

Importante

Para usuários avançados: a maioria dos aplicativos não precisa personalizar o ponto de entrada nesse grau. Para obter mais informações e caso você deseje fornecer casos de uso para seus cenários, confira GitHub: Discussões sobre builds de contêiner do SDK do .NET.

ContainerUser

A propriedade de configuração do usuário controla o usuário padrão como o qual o contêiner é executado. Em geral, isso é usado para executar o contêiner como um usuário não raiz, o que é uma melhor prática de segurança. Há algumas restrições a essa configuração das quais você deve estar ciente:

  • Ela pode assumir várias formas: nome de usuário, IDs de usuário do Linux, nome do grupo, ID de grupo do Linux, username:groupname e outras variantes de ID.
  • Não há nenhuma verificação de que o usuário ou o grupo especificado existe na imagem.
  • A alteração o usuário pode alterar o comportamento do aplicativo, especialmente em relação a itens como permissões do sistema de arquivos.

O valor padrão desse campo varia de acordo com o TFM do projeto e o sistema operacional de destino:

  • Se o destino for o .NET 8 ou versão superior e você estiver usando as imagens de runtime da Microsoft:
    • no Linux, o usuário sem raiz app é usado (embora seja referenciado pela identificação de usuário)
    • no Windows, o usuário sem raiz ContainerUser é usado
  • Caso contrário, nenhum ContainerUser padrão é usado
<PropertyGroup>
  <ContainerUser>my-existing-app-user</ContainerUser>
</PropertyGroup>

Dica

A variável de ambiente APP_UID é usada para definir as informações do usuário no contêiner. Esse valor pode ser obtido de variáveis de ambiente definidas na imagem base (como as imagens do Microsoft .NET) ou você mesmo pode defini-lo por meio da sintaxe ContainerEnvironmentVariable.

No entanto, você pode controlar como seu aplicativo é executado usando ContainerEntrypoint e ContainerEntrypointArgs.

ContainerEntrypoint

O ponto de entrada do contêiner pode ser usado para personalizar o ENTRYPOINT do contêiner, que é o executável chamado quando o contêiner é iniciado. Por padrão, para builds que criam um host de aplicativo, ele é definido como o ContainerEntrypoint. Para builds que não criam um executável, o dotnet path/to/app.dll é usado como o ContainerEntrypoint.

O nó ContainerEntrypoint tem um único atributo:

  • Include: o comando, a opção ou o argumento a ser usado no comando ContainerEntrypoint.

Por exemplo, considere o seguinte grupo de itens de projeto .NET de exemplo:

<ItemGroup Label="Entrypoint Assignment">
  <!-- This is how you would start the dotnet ef tool in your container -->
  <ContainerEntrypoint Include="dotnet" />
  <ContainerEntrypoint Include="ef" />

  <!-- This shorthand syntax means the same thing.
       Note the semicolon separating the tokens. -->
  <ContainerEntrypoint Include="dotnet;ef" />
</ItemGroup>

ContainerEntrypointArgs

O nó args do ponto de entrada do contêiner controla os argumentos padrão fornecidos para o ContainerEntrypoint. Isso deve ser usado quando o ContainerEntrypoint é um programa que o usuário pode querer usar por conta própria. Por padrão, nenhum ContainerEntrypointArgs é criado em seu nome.

O nó ContainerEntrypointArg tem um único atributo:

  • Include: a opção ou o argumento a ser aplicado ao comando ContainerEntrypoint.

Considere o seguinte grupo de itens de projeto .NET de exemplo:

<ItemGroup>
  <!-- Assuming the ContainerEntrypoint defined above,
       this would be the way to update the database by
       default, but let the user run a different EF command. -->
  <ContainerEntrypointArgs Include="database" />
  <ContainerEntrypointArgs Include="update" />

  <!-- This is the shorthand syntax for the same idea -->
  <ContainerEntrypointArgs Include="database;update" />
</ItemGroup>

Rótulos do contêiner padrão

Os rótulos geralmente são usados para fornecer metadados consistentes em imagens de contêiner. Esse pacote fornece alguns rótulos padrão para incentivar uma melhor manutenção das imagens geradas.

  • O org.opencontainers.image.created é definido como o formato ISO 8601 do DateTime UTC atual.

Para obter mais informações, confira Implementar rótulos convencionais sobre a infraestrutura de rótulo existente.

Limpar os recursos

Neste artigo, você publicou um trabalho do .NET como uma imagem de contêiner. Se desejar, exclua este recurso. Use o comando docker images para ver uma lista de imagens instaladas.

docker images

Considere a seguinte saída de exemplo:

REPOSITORY            TAG       IMAGE ID       CREATED          SIZE
dotnet-worker-image   1.0.0     25aeb97a2e21   12 seconds ago   191MB

Dica

Arquivos de imagem podem ser grandes. Normalmente, você removeria contêineres temporários criados durante o teste e o desenvolvimento de seu aplicativo. Em geral, mantenha as imagens de base com o runtime instalado se você planeja construir outras imagens com base nesse runtime.

Para excluir a imagem, copie o ID da imagem e execute o comando docker image rm:

docker image rm 25aeb97a2e21

Próximas etapas