本文介绍如何通过在应用主机项目中编写代码来进一步自定义资源的行为。 其中 Aspire, 资源 是云原生应用程序的依赖部分。 资源类型包括:
- .NET 项目:一个自定义微服务,负责云原生应用程序中的特定功能,通常由单独的开发人员团队生成。
- 可执行文件:如果需要使用工具构建微服务,例如 Node.js 或 Orleans,它们会作为可执行资源运行。
- 容器:可以基于特定映像添加Docker容器到您的Aspire解决方案。
- 集成资源:集成通常向应用程序添加数据库、缓存和消息传送服务等资源。
- 外部服务:表示应用程序依赖但不受管理 Aspire的第三方 API 或服务。 将该功能用于公共 API 或 SaaS 端点等资源。
有关Aspire编排的基础知识及其管理资源的方式,请参阅Aspire编排概述。
资源命名约定
资源 Aspire 必须遵循资源 Aspire 所代表的命名限制以及资源所代表的技术。 例如, Aspire 资源的最大名称长度为 64 个字符,但 Azure 容器应用的最大长度为 32。 发布 Aspire 容器资源 Azure时,名称长度不得超过 32 个字符。
Aspire 资源名称必须遵循以下基本规则:
- 长度必须为 1 到 64 个字符。
- 必须 以 ASCII 字母开头。
- 必须 仅包含 ASCII 字母、数字和连字符。
- 不能 以连字符结尾。
- 不得 包含连续连字符。
设置显式资源启动
默认情况下,项目、可执行文件和容器资源会自动启动分布式应用程序。 可以将资源配置为使用 WithExplicitStart 方法等待显式启动指令。 使用 WithExplicitStart 配置的资源使用 KnownResourceStates.NotStarted进行初始化。
var builder = DistributedApplication.CreateBuilder(args);
var postgres = builder.AddPostgres("postgres");
var postgresdb = postgres.AddDatabase("postgresdb");
builder.AddProject<Projects.AspireApp_DbMigration>("dbmigration")
.WithReference(postgresdb)
.WithExplicitStart();
在前面的代码中,资源 "dbmigration"
配置为不自动启动分布式应用程序。
可以从 Aspire 仪表板通过单击“开始”命令启动具有明确启动条件的资源。 有关详细信息,请参阅 Aspire 仪表板:停止或启动资源。
正在等待资源
在某些情况下,可能需要等待资源准备就绪,然后再启动另一个资源。 例如,可能需要等待数据库准备就绪,然后再启动依赖于它的 API。 若要表达此依赖项,请使用 WaitFor 方法:
var builder = DistributedApplication.CreateBuilder(args);
var postgres = builder.AddPostgres("postgres");
var postgresdb = postgres.AddDatabase("postgresdb");
builder.AddProject<Projects.AspireApp_ApiService>("apiservice")
.WithReference(postgresdb)
.WaitFor(postgresdb);
在前面的代码中,“apiservice”项目资源等待“postgresdb”数据库资源进入 KnownResourceStates.Running 状态。 示例代码显示了 AspirePostgreSQL 集成,但相同的模式可以应用于其他资源。
其他情况可能需要等待某个资源在 KnownResourceStates.Exited 或 KnownResourceStates.Finished 完成运行后,再启动从属资源。 若要等待资源运行完成,请使用 WaitForCompletion 方法:
var builder = DistributedApplication.CreateBuilder(args);
var postgres = builder.AddPostgres("postgres");
var postgresdb = postgres.AddDatabase("postgresdb");
var migration = builder.AddProject<Projects.AspireApp_Migration>("migration")
.WithReference(postgresdb)
.WaitFor(postgresdb);
builder.AddProject<Projects.AspireApp_ApiService>("apiservice")
.WithReference(postgresdb)
.WaitForCompletion(migration);
在前面的代码中,“apiservice”项目资源会等待“迁移”项目资源运行完成之后才开始。 “迁移”项目资源等待“postgresdb”数据库资源进入 KnownResourceStates.Running 状态。 例如,在启动 API 服务之前,在想要运行数据库迁移的情况下,这非常有用。
强制启动仪表板中的资源
可以使用仪表板中的 “开始” 命令绕过等待资源。 在仪表板中选择等待资源上的开始指示它立即开始,无需等待资源健康或完成。 如果想要立即测试资源,并且不想等待应用处于正确状态,这非常有用。
用于添加和表达资源的 API
Aspire 托管集成 和 客户端集成 都作为 NuGet 包提供,但它们用于不同的目的。 虽然 客户端集成 提供客户端库配置,以供在应用主机范围之外使用的应用程序使用,托管集成 则提供 API,以便在应用主机内表达资源和依赖项。 有关详细信息,请参阅 Aspire 集成概述:集成责任。
声明容器资源
若要表达 ContainerResource,请通过调用 IDistributedApplicationBuilder 方法将其添加到 AddContainer 实例:
var builder = DistributedApplication.CreateBuilder(args);
var ollama = builder.AddContainer("ollama", "ollama/ollama")
.WithBindMount("ollama", "/root/.ollama")
.WithBindMount("./ollamaconfig", "/usr/config")
.WithHttpEndpoint(port: 11434, targetPort: 11434, name: "ollama")
.WithEntrypoint("/usr/config/entrypoint.sh")
.WithContainerRuntimeArgs("--gpus=all");
有关详细信息,请参阅
前面的代码通过镜像 ollama/ollama
添加了一个名为“ollama”的容器资源。 容器资源配置了多个绑定挂载、一个命名的 HTTP 端点、一个解析到 Unix shell 脚本的入口点,以及通过 WithContainerRuntimeArgs 方法指定的容器运行参数。
自定义容器资源
可以自定义所有 ContainerResource 子类以满足特定要求。 使用 托管集成 为容器资源建模但需要修改时,这非常有用。 当你拥有一个 IResourceBuilder<ContainerResource>
时,可以对任何可用的 API 进行链式调用,以修改该容器资源。
Aspire 容器资源通常指向固定的标记,但可能需要改用标记 latest
。
为了更好地说明这个问题,请想象一个使用 AspireRedis 集成的场景。 如果 Redis 集成依赖于 7.4
标记,并且想要改用 latest
标记,则可以串连调用 WithImageTag API:
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("cache")
.WithImageTag("latest");
// Instead of using the "7.4" tag, the "cache"
// container resource now uses the "latest" tag.
有关更多信息和其他可用的 API,请参阅 ContainerResourceBuilderExtensions。
容器资源生命周期
运行应用主机时,ContainerResource 用于确定要创建和启动的容器映像。 在底层,Aspire 使用定义的容器镜像,通过将调用委托给合适的符合 OCI 的容器运行时(Docker 或 Podman),来运行容器。 使用以下命令:
首先,使用 docker container create
命令创建容器。 然后,使用 docker container start
命令启动容器。
- docker 容器创建:从指定的映像创建新容器,而无需启动它。
- docker 容器启动:启动一个或多个已停止的容器。
这些命令用于代替 docker run
管理关联的容器网络、卷和端口。 按照此顺序调用这些命令,可以在初始启动时使任何 IP(网络配置)已经准备好。
除了基本资源类型,ProjectResourceContainerResource还提供ExecutableResourceAspire扩展方法,用于向应用模型添加常见资源。 有关详细信息,请参阅 托管集成。
容器资源生存期
默认情况下,容器资源使用 会话 容器生存期。 这意味着,每次启动应用主机进程时,都会创建并启动容器。 当应用主机停止时,容器将停止并删除。 容器资源可以选择使用 持久性 生命周期,以避免不必要的重启并利用持久化的容器状态。 为此,请链式调用 ContainerResourceBuilderExtensions.WithLifetime API 并传递 ContainerLifetime.Persistent:
var builder = DistributedApplication.CreateBuilder(args);
var ollama = builder.AddContainer("ollama", "ollama/ollama")
.WithLifetime(ContainerLifetime.Persistent);
前面的代码添加名为“ollama”的容器资源,其中包含图像“ollama/ollama”和持久生存期。
表示外部服务资源
外部服务是应用程序依赖但存在于解决方案外部 Aspire 的第三方 API 和服务。 这些服务已在其他位置运行,并且不受Aspire管理。 要表达ExternalServiceResource
,可以通过调用AddExternalService
方法将其添加到IDistributedApplicationBuilder实例中:
var builder = DistributedApplication.CreateBuilder(args);
var nuget = builder.AddExternalService("nuget", "https://api.nuget.org/")
.WithHttpHealthCheck(path: "/v3/index.json");
var frontend = builder.AddProject<Projects.Frontend>("frontend")
.WithReference(nuget);
前面的代码添加一个名为“nuget”的外部服务资源,该资源指向 NuGet API。 外部服务配置为使用 HTTP 运行状况检查来监视其可用性。 然后,前端项目可以引用此外部服务进行服务发现。
外部服务支持多种配置方法:
静态 URL 配置
可以使用字符串或 URI 使用静态 URL 配置外部服务:
var builder = DistributedApplication.CreateBuilder(args);
// Using a string URL
var nuget = builder.AddExternalService("nuget", "https://api.nuget.org/");
// Using a URI
var uri = new Uri("https://api.example.com/");
var api = builder.AddExternalService("external-api", uri);
基于参数的 URL 配置
对于外部服务 URL 在环境之间可能有所不同或需要配置的场景,可以使用参数:
var builder = DistributedApplication.CreateBuilder(args);
var externalServiceUrl = builder.AddParameter("external-service-url");
var externalService = builder.AddExternalService("external-service", externalServiceUrl);
var frontend = builder.AddProject<Projects.Frontend>("frontend")
.WithReference(externalService);
使用基于参数的配置时,可以通过配置、环境变量或用户机密设置 URL 值。 在开发过程中, Aspire 可能会提示你提供 URL 值。 有关详细信息,请参阅 外部参数。
外部服务 URL 要求
外部服务 URL 必须满足特定要求:
- 必须是绝对 URI(包括方案、主机和可选端口)。
- 必须将 绝对路径设置为“/”(没有其他路径段)。
- 不得 包含查询参数或片段。
Valid examples:
https://api.example.com/
http://localhost:8080/
https://service.example.com:9443/
Invalid examples:
-
https://api.example.com/v1/api
(包含路径) -
https://api.example.com/?version=1
(包含查询) -
https://api.example.com/#section
(包含片段)
外部服务的运行状况检查
可以使用 HTTP 运行状况检查来配置外部服务,以监视其可用性:
var builder = DistributedApplication.CreateBuilder(args);
var api = builder.AddExternalService("api", "https://api.example.com/")
.WithHttpHealthCheck(path: "/health", statusCode: 200);
该WithHttpHealthCheck
方法添加了一项健康检查,用于定期轮询外部服务。 可以指定:
-
path
:运行状况检查终结点的相对路径(可选,默认为无其他路径)。 -
statusCode
:预期的 HTTP 状态代码(可选,默认为 200)。
使用外部服务进行服务发现
从另一资源引用外部服务时, Aspire 通过注入标准格式的环境变量来自动配置服务发现:
var builder = DistributedApplication.CreateBuilder(args);
var api = builder.AddExternalService("api", "https://api.example.com/");
var frontend = builder.AddProject<Projects.Frontend>("frontend")
.WithReference(api);
此配置将 services__api__https__0=https://api.example.com/
环境变量注入前端项目,从而通过标准 .NET 服务发现机制促进服务发现。
外部服务生命周期
外部服务实现 IResourceWithoutLifetime,这意味着它们不是由 Aspire生命周期系统管理的。 它们预计将独立运行。 在开发过程中,如果外部服务可访问,它们会以“正在运行”状态出现在 Aspire 仪表板中;如果不可用,则显示健康检查失败。
Resource relationships
资源关系将资源链接在一起。 关系是信息性的,不会影响应用的运行时行为。 而是在仪表板中显示有关资源的详细信息时使用它们。 例如,关系显示在 仪表板的资源详细信息中,而 Parent
关系控制资源嵌套在资源页面上。
关系由某些应用模型 API 自动创建。 For example:
-
WithReference 使用类型
Reference
向目标资源添加关系。 -
WaitFor 使用类型
WaitFor
向目标资源添加关系。 - 将数据库添加到 DB 容器将创建从数据库到具有类型
Parent
的容器的关系。
可以显式地将关系添加到应用模型中,使用 WithRelationship 和 WithParentRelationship。
var builder = DistributedApplication.CreateBuilder(args);
var catalogDb = builder.AddPostgres("postgres")
.WithDataVolume()
.AddDatabase("catalogdb");
builder.AddProject<Projects.AspireApp_CatalogDbMigration>("migration")
.WithReference(catalogDb)
.WithParentRelationship(catalogDb);
builder.Build().Run();
前面的示例使用 WithParentRelationship 将 catalogdb
数据库配置为 migration
项目的父级。
Parent
关系很特殊,因为它控制资源页面上的资源嵌套。 在此示例中,migration
嵌套在 catalogdb
下。
Note
有父子关系的验证可以防止资源具有多个父对象或创建循环引用。 无法在 UI 中呈现这些配置,应用模型将引发错误。