.NET SDK 容器创建概述

虽然可以使用 Dockerfile 容器化 .NET 应用,但使用 .NET SDK 直接容器化应用有令人信服的理由。 本文概述了 .NET SDK 容器创建功能,其中包含与遥测、发布注意事项、生成属性和容器注册表身份验证相关的详细信息。

发布项目注意事项

有了 .NET 应用后,即可将其发布为容器。 在执行此操作之前,需要考虑几个重要注意事项。 在 .NET SDK 版本 8.0.200 之前,需要 📦 Microsoft.NET.Build.Containers NuGet 包。 .NET SDK 版本 8.0.200 及更高版本不需要此包,因为默认情况下包含容器支持。

若要将 .NET 应用发布为容器,需要以下生成属性:

  • IsPublishable:设置为 true。 此属性对于可执行项目类型(例如 consolewebappworker)隐式设置为 true
  • EnableSdkContainerSupport:当项目类型为控制台应用时设置为 true

若要显式启用 SDK 容器支持,请考虑以下项目文件片段:

<PropertyGroup>
  <IsPublishable>true</IsPublishable>
  <EnableSdkContainerSupport>true</EnableSdkContainerSupport>
</PropertyGroup>

发布开关和生成属性

与所有 .NET CLI 命令一样,可以在命令行上指定 MSBuild 属性。 有许多有效的语法形式可用于提供属性,例如:

  • /p:PropertyName=Value
  • -p:PropertyName=Value
  • -p PropertyName=Value
  • --property PropertyName=Value

你可以随意使用你喜欢的语法,但文档演示了使用 -p 窗体的示例。

提示

若要帮助进行故障排除,请考虑使用 MSBuid 日志。 若要生成二进制日志(binlog)文件,请将 -bl 开关添加到 dotnet publish 命令。 Binlog 文件可用于诊断生成问题,并且可以在 MSBuild 结构化日志查看器中打开。 它们提供生成过程的详细跟踪,这对于 MSBuild 分析至关重要。 有关详细信息,请参阅 MSBuild故障排除和创建日志。

发布配置文件和目标

使用 dotnet publish 时,使用 -p PublishProfile=DefaultContainer 指定配置文件可以设置一个属性,使 SDK 在发布过程后触发另一个目标。 这是实现所需结果的间接方式。 另一方面,使用 dotnet publish /t:PublishContainer 直接调用 PublishContainer 目标,以更直接的方式实现相同的结果。

换句话说,以下 .NET CLI 命令:

dotnet publish -p PublishProfile=DefaultContainer

PublishProfile 属性设置为 DefaultContainer,等效于以下命令:

dotnet publish /t:PublishContainer

这两种方法之间的区别在于,前者使用配置文件设置属性,而后者直接调用目标。 重要的是,配置文件是 MSBuild 的一项功能,它们可用于以更复杂的方式设置属性,而不仅仅是直接设置属性。

一个关键问题是,并非所有项目类型都支持配置文件或具有相同的配置文件集。 此外,不同工具(如 Visual Studio 和 .NET CLI)对配置文件的支持级别存在差异。 因此,使用目标通常是一种更清晰、更广受支持的方法,可实现相同的结果。

向容器注册表进行身份验证

与专用容器注册表交互需要对这些注册表进行身份验证。

Docker 通过 docker login 命令建立此模式,这是与 Docker 配置文件进行交互的一种方式,该文件包含用于对特定注册表进行身份验证的规则。 微软的 Microsoft.Net.Build.Containers 支持此文件及其所编码的身份验证类型,以用作注册表身份验证。 这将确保该包可以与 docker pulldocker push 的任何注册表无缝地协同工作。 此文件通常存储在 ~/.docker/config.json,但可以通过 DOCKER_CONFIG 变量(指向包含 config.json 文件的目录)指定该文件。

身份验证类型

config.json 文件包含三种类型的身份验证:

显式用户名/密码

config.json 文件的 auths 部分是注册表名称和 Base64 编码的 username:password 字符串之间的键/值映射。 在常见的 Docker 方案中,运行 docker login <registry> -u <username> -p <password> 在此映射中创建新项。 这些凭据在持续集成(CI)系统中很受欢迎,其中登录是通过运行开始时的令牌完成的。 然而,由于文件中存在裸凭据的安全风险,它们在最终用户开发计算机上不太受欢迎。

凭据帮助程序

config.json 文件的 credHelpers 部分是注册表名称和特定程序的名称之间的键/值映射,可用于创建和检索该注册表的凭据。 当特定注册表具有复杂的身份验证要求时,通常会使用此功能。 若要使此类身份验证正常工作,必须在系统的 PATH上具有名为 docker-credential-{name} 的应用程序。 这些类型的凭据往往是安全的,但很难在开发或 CI 计算机上设置。

系统密钥链

credsStore 部分是一个字符串属性,其值是 docker 凭据帮助程序的名称,该程序知道如何与系统的密码管理器进行交互。 例如,对于 Windows,这可能是 wincred。 这些在 macOS 和 Windows 的 Docker 安装程序中很受欢迎。

通过环境变量进行身份验证

在某些情况下,上述标准 Docker 身份验证机制就是不够用。 此工具具有向注册表提供凭据的其他机制:环境变量。 如果使用环境变量,则不会使用凭据提供机制。 支持以下环境变量:

  • DOTNET_CONTAINER_REGISTRY_UNAME:这应该是注册表的用户名。 如果注册表的密码是令牌,则用户名应 "<token>"
  • DOTNET_CONTAINER_REGISTRY_PWORD:这应该是注册表的密码或令牌。

注意

自 .NET SDK 8.0.400 起,容器操作的环境变量已更新。 SDK_CONTAINER_* 变量现在以 DOTNET_CONTAINER_*为前缀。

此机制可能容易受到凭据泄漏的侵害,因此它应仅在其他机制不可用的情况下使用。 例如,如果你 在Docker 容器本身中使用 SDK 容器工具。 此外,该机制没有命名空间功能——它尝试对 注册表(您的基础镜像所在的地方)和 目标 注册表(您推送最终镜像的地方)使用相同的凭据。

使用不安全的注册表

大多数注册表访问假定是安全的,这意味着 HTTPS 用于与注册表交互。 但是,并非所有注册表都配置了 TLS 证书,尤其是在 VPN 后面的专用公司注册表等情况下。 为了支持这些用例,容器工具提供了声明特定注册表使用不安全通信的方法。

从 .NET 8.0.400 开始,SDK 了解这些配置文件和格式,并会自动使用该配置来确定是否应使用 HTTP 或 HTTPS。 配置注册表以便进行不安全通信会因所选容器工具而异。

Docker

Docker 将其注册表配置存储在 守护程序配置中。 若要添加新的不安全注册表,需要将新主机添加到 "insecure-registries" 数组属性中:

{
  "insecure-registries": [
    "registry.mycorp.net"
  ]
}

注意

必须重启 Docker 守护程序才能对此文件应用任何更改。

Podman

Podman 使用 registries.conf TOML 文件来存储注册表连接信息。 此文件通常位于 /etc/containers/registries.conf。 若要添加新不安全的注册表,将添加 TOML 节以保存注册表的设置,然后必须将 insecure 选项设置为 true

[[registry]]
location = "registry.mycorp.net"
insecure = true

注意

必须重启 Podman 才能将任何更改应用到 registries.conf 文件。

环境变量

从 9.0.100 开始,.NET SDK 可识别通过 DOTNET_CONTAINER_INSECURE_REGISTRIES 环境变量传递的不安全注册表。 此变量采用逗号分隔的域列表,以与上述 Docker 和 Podman 示例相同的方式视为不安全。

$Env:DOTNET_CONTAINER_INSECURE_REGISTRIES=localhost:5000,registry.mycorp.com; dotnet publish -t:PublishContainer -p:ContainerRegistry=registry.mycorp.com -p:ContainerBaseImage=localhost:5000/dotnet/runtime:9.0

遥测

将 .NET 应用发布为容器时,.NET SDK 容器工具收集并发送有关如何使用这些工具的使用情况遥测数据。 收集的数据是对 .NET CLI发送的 遥测数据的补充,但使用相同的机制,重要的是遵循相同的 选择退出控制

收集的遥测数据本质上是一般情况,不会泄露任何个人信息,目的是帮助衡量:

  • 关于 .NET SDK 容器化功能的总体使用情况。
  • 成功和失败率,以及有关最频繁发生的故障类型的一般信息。
  • 使用技术的特定功能,如发布到各种注册表类型,或调用发布的方式。

若要选择退出遥测,请将 DOTNET_CLI_TELEMETRY_OPTOUT 环境变量设置为 true。 有关详细信息,请参阅 .NET CLI 遥测

推理遥测

记录了有关基础映像推理过程如何发生的以下信息:

时间点 解释 示例值
InferencePerformed 如果用户手动指定基础映像而不是使用推理。 true
TargetFramework 执行基础映像推理时选择 TargetFramework net8.0
BaseImage 选择的基础映像的值,但前提是该基础映像是Microsoft生成的映像之一。 如果用户在 mcr.microsoft.com 上指定Microsoft生成的图像以外的任何图像,则此值为 null。 mcr.microsoft.com/dotnet/aspnet
BaseImageTag 所选标记的值,但前提是该标记属于微软制作的图像之一。 如果用户在 mcr.microsoft.com 上指定Microsoft生成的图像以外的任何图像,则此值为 null。 8.0
ContainerFamily 如果用户使用 ContainerFamily 功能选择基础映像中的某个“类型”,则为 ContainerFamily 属性的值。 只有当用户从 mcr.microsoft.com 选择或推测出由 Microsoft 生产的 .NET 映像之一时,才会进行此设置。 jammy-chiseled
ProjectType 正在被容器化的项目类型。 AspNetCoreConsole
PublishMode 应用程序是如何打包的。 AotTrimmedSelfContainedFrameworkDependent
IsInvariant 如果所选映像需要固定全球化,或用户手动选择了此选项。 true
TargetRuntime 发布此应用程序时使用的 RID。 linux-x64

映像创建遥测

下面介绍了如何记录容器创建和发布过程:

时间点 解释 示例值
RemotePullType 如果基础映像来自远程注册表,则它是什么样的注册表? Azure、AWS、Google、GitHub、DockerHub、MRC 或其他
LocalPullType 如果基础映像来自本地源,例如容器守护程序或 tarball。 Docker、Podman、Tarball
RemotePushType 如果映像被推送到远程注册表,则它是什么样的注册表? Azure、AWS、Google、GitHub、DockerHub、MRC 或其他
LocalPushType 如果映像被推送到本地目标,它是什么? Docker、Podman、Tarball

此外,如果在过程中发生各种错误,则收集有关错误类型的数据:

时间点 解释 示例值
Error 发生的错误类型 unknown_repositorycredential_failurerid_mismatchlocal_load
Direction 如果错误是 credential_failure,是推送还是拉取注册表? push
目标 RID 如果错误是 rid_mismatch,则请求的 RID 是什么 linux-x64
可用 RID 如果错误是 rid_mismatch,则基础映像支持哪些 RID? linux-x64,linux-arm64

另请参阅