Aspire 提供強大的 API,可在發佈和部署作業期間從您的資源建置容器映像。 本文涵蓋啟用程式化容器映像檔生成和進度報告的關鍵組成部分。
概觀
在發佈和部署期間,容器映像產生器可用於為需要它們的資源建立映像。 Aspire 當資源需要容器映像檔時 (例如使用 Compose 發佈 Docker 時) 會使用此建構器。 該過程涉及兩個主要組成部分:
- IResourceContainerImageBuilder:將資源定義轉換為可執行容器映像的服務。
-
IPipelineActivityReporter:在長時間執行作業期間提供結構化進度報告的 API。
這些 API 可讓您精細控制映像建置過程,並在冗長的建置操作期間向使用者提供即時回饋。
這很重要
這些 API 目前處於預覽狀態,可能會變更。 它們專為您需要自訂控制容器映像建置和進度報告的進階案例而設計。 若要隱藏這些 API 的警告,請參閱 編譯器錯誤 ASPIREPUBLISHERS001。
何時使用這些 API
請考慮在下列案例中使用容器映像建置和進度報告 API:
- 自訂部署目標:當您需要部署到需要特定映像格式或組建組態的平台時。
- 複雜的建置管線:當您的發佈程式涉及使用者應該看到的多個步驟時。
- 企業案例:當您需要自訂進度報告以與 CI/CD 系統或儀表板整合時。
- 自訂資源類型:實作需要參與發佈和部署流程的自訂資源時。
備註
對於大多數標準 Aspire 應用程式,內建發佈程序會自動建置容器映像,而不需要這些 API。
資源容器映像產生器 API
是 IResourceContainerImageBuilder 層中 Aspire.Hosting.Publishing 的核心服務,可將資源定義轉換為容器映像。 它會分析分散式應用程式模型中的每個資源,並決定是否:
- 重複使用現有影像。
- 使用.NET專案,利用
dotnet publish /t:PublishContainer進行建置。 - 使用本機容器執行環境建置 Dockerfile。
容器建置選項
這個類別提供 ContainerBuildOptions 容器組建的強型別配置。 這個類別可讓您指定:
- 映像格式: Docker 或 Open Container Initiative (OCI) 格式。
- 目標平台: Linux x64、Windows、ARM64等。
- 輸出路徑:儲存建置映像的位置。
容器運行時健康檢查
只有在至少有一個資源需要Docker建置時,建置器才會執行容器執行階段運作狀態檢查 (Podman/Dockerfile)。 此變更可排除直接從元件發佈 .NET 的專案中的偽陽性錯誤。 如果需要容器執行階段但狀況不良,建置器會擲回明確的錯誤 InvalidOperationException,以儘早顯示問題。
管線活動報告器 API
API 可在 PipelineActivityReporter 和 aspire publish 命令期間啟用aspire deploy結構化進度報告。 這減少了長期運行操作期間的不確定性,並儘早發現故障。
API 概觀和行為
進度報告系統使用階層式模型,保證作業順序和具執行緒安全的操作。
| 概念 | Description | CLI 轉譯 | 行為 |
|---|---|---|---|
| Step | 最上層階段,例如「建置映像」或「部署工作負載」。 | 帶有狀態字形和經過時間的步驟訊息。 | 形成嚴格的樹狀結構,不支援任何巢狀步驟。 步驟會在執行管道過程中自動生成。 |
| 任務 | 在步驟下嵌套的獨立工作單元。 | 具有縮排的工作訊息。 | 屬於單一步驟;支援具有確定性完成順序的並行建立。 |
| 完成狀態 | 最終狀態: Completed、 Warning或 Error。 |
✅ (已完成), ⚠ (警告), ❌ (錯誤) | 每個步驟/任務只轉換一次到最終狀態。 |
API 結構和用法
報告者 API 提供進度報告的結構化存取,具有下列特性:
-
取得:在管線步驟中,從
PublishingContext.ActivityReporter擷取或透過屬性PipelineStepContext.ReportingStep擷取。 -
步驟建立:現在會在管線執行期間自動建立步驟。
CreateStepAsync(title, ct)方法會傳回一個IReportingStep。 -
任務建立:
IReportingStep.CreateTaskAsync(title, ct)返回IReportingTask. -
狀態轉換:
SucceedAsync、WarnAsync、FailAsync方法接受摘要訊息。 -
完成:
CompletePublishAsync(message, state, isDeploy, ct)標記整個作業。 - 排序:建立和完成事件會保留呼叫順序;更新會序列化。
- 取消:所有API都接受 CancellationToken 取消並將其傳播至CLI。
- 處置合約:處置步驟如果未完成,將自動完成,以防止階段遺留未處理。
範例:建置容器映像並報告進度
若要使用這些 API,請新增 PublishingCallbackAnnotation、DeployingCallbackAnnotation 或兩者至應用程式模型中的資源。 您可以透過將註解新增至 IResource.Annotations 集合來註解自訂(或內建)資源。
身為開發人員,您可以選擇:
如果您的資源需要在發佈和部署中執行工作,請同時使用這兩個註釋。 例如,在發佈期間建置映像並產生資訊清單,然後在部署期間推送映像或設定部署目標。 發佈一律會在部署之前進行,因此您可以將每個階段的邏輯分開。
在資源只需要在發佈期間執行某些動作時,才使用
PublishingCallbackAnnotation。 當您只需要建置成品或映像,但在部署期間不需要執行任何動作時,這很常見。只有當您的資源僅在部署期間需要執行操作時才使用
DeployingCallbackAnnotation。 這適合您使用預先建置的映像,而且只需要部署或設定它們。
選擇一或多個符合資源職責的註釋,以保持應用程式模型清晰且可維護。 此分隔可讓您清楚定義每個階段的邏輯,但您可以視需要在任一回呼中使用活動報告器和資源容器映像產生器。
具有註釋的資源範例
例如,請考慮 ComputeEnvironmentResource 建構函式:
public ComputeEnvironmentResource(string name) : base(name)
{
Annotations.Add(new PublishingCallbackAnnotation(PublishAsync));
Annotations.Add(new DeployingCallbackAnnotation(DeployAsync));
}
實例化時,它會定義發布與部署的回呼註釋。
假設 ComputeEnvironmentResource 範例 (Resource) 類型,假設您有一個公開的擴充方法,讓取用者能夠新增計算環境:
using System.Diagnostics.CodeAnalysis;
[Experimental("ASPIRECOMPUTE001")]
public static class ComputeEnvironmentResourceExtensions
{
public static IResourceBuilder<ComputeEnvironmentResource> AddComputeEnvironment(
this IDistributedApplicationBuilder builder,
[ResourceName] string name)
{
var resource = new ComputeEnvironmentResource(name);
return builder.AddResource(resource);
}
}
上述 程式碼:
- 在IDistributedApplicationBuilder上定義擴充方法。
- 接受計算環境資源的
name,受ResourceNameAttribute保護。 - 根據給定的
ComputeEnvironmentResource實例化name,並將其新增至builder。
AppHost 範例
在您的 AppHost 中,您可以將 ComputeEnvironmentResource 像這樣新增至應用程式模型:
var builder = DistributedApplication.CreateBuilder(args);
var cache = builder.AddRedis("redis");
builder.AddProject<Projects.Api>("api")
.WithReference(cache);
builder.AddComputeEnvironment("compute-env");
builder.Build().Run();
上述程式碼使用 AddComputeEnvironment 擴充方法將 ComputeEnvironmentResource 新增至應用程式模型。
發佈回呼註解
當您新增ComputeEnvironmentResource時,它會註冊一個PublishingCallbackAnnotation。 回呼使用 PublishAsync 方法:
private static async Task PublishAsync(PublishingContext context)
{
var reporter = context.ActivityReporter;
var imageBuilder = context.Services.GetRequiredService<IResourceContainerImageBuilder>();
// Build container images for all project resources in the application
await using (var buildStep = await reporter.CreateStepAsync(
"Build container images", context.CancellationToken))
{
// Find all resources that need container images
var projectResources = context.Model.Resources
.OfType<ProjectResource>()
.ToList();
if (projectResources.Count > 0)
{
// Configure how images should be built
var buildOptions = new ContainerBuildOptions
{
ImageFormat = ContainerImageFormat.Oci,
TargetPlatform = ContainerTargetPlatform.LinuxAmd64,
OutputPath = Path.Combine(context.OutputPath, "images")
};
var buildTask = await buildStep.CreateTaskAsync(
$"Building {projectResources.Count} container image(s)", context.CancellationToken);
// Build all the container images
await imageBuilder.BuildImagesAsync(
projectResources, buildOptions, context.CancellationToken);
await buildTask.SucceedAsync(
$"Built {projectResources.Count} image(s) successfully", context.CancellationToken);
}
else
{
var skipTask = await buildStep.CreateTaskAsync(
"No container images to build", context.CancellationToken);
await skipTask.SucceedAsync("Skipped - no project resources found", context.CancellationToken);
}
await buildStep.SucceedAsync("Container image build completed", context.CancellationToken);
}
// Generate deployment manifests
await using (var manifestStep = await reporter.CreateStepAsync(
"Generate deployment manifests", context.CancellationToken))
{
var bicepTask = await manifestStep.CreateTaskAsync(
"Write main.bicep", context.CancellationToken);
// Write file to context.OutputPath …
await bicepTask.SucceedAsync(
$"main.bicep at {context.OutputPath}", context.CancellationToken);
await manifestStep.SucceedAsync("Manifests ready", context.CancellationToken);
}
// Complete the publishing operation
await reporter.CompletePublishAsync(
completionMessage: "Publishing pipeline completed successfully",
completionState: CompletionState.Completed,
cancellationToken: context.CancellationToken);
}
上述 程式碼:
- 實作發佈管線,以建置容器映像並產生部署資訊清單。
- 使用
IResourceContainerImageBuilderAPI 來建置容器映像。 - 使用
PipelineActivityReporterAPI 報告進度和完成狀態。
您的發佈回呼可能會用 IResourceContainerImageBuilder 來建置容器映像,而您的部署回呼可能會使用建置的映像,並將其推送至登錄或部署目標。
部署回呼函數註解
與發佈回呼一樣,使用DeployingCallbackAnnotation註冊部署回呼,並呼叫DeployAsync方法:
private static async Task DeployAsync(DeployingContext context)
{
var reporter = context.ActivityReporter;
await using (var deployStep = await reporter.CreateStepAsync(
"Deploy to target environment", context.CancellationToken))
{
var applyTask = await deployStep.CreateTaskAsync(
"Apply Kubernetes manifests", context.CancellationToken);
// Simulate deploying to Kubernetes cluster
await Task.Delay(1_000, context.CancellationToken);
await applyTask.SucceedAsync("All workloads deployed", context.CancellationToken);
await deployStep.SucceedAsync("Deployment to cluster completed", context.CancellationToken);
}
// Complete the deployment operation
await reporter.CompletePublishAsync(
completionMessage: "Deployment completed successfully",
completionState: CompletionState.Completed,
isDeploy: true,
cancellationToken: context.CancellationToken);
}
上述 程式碼:
- 模擬將工作負載部署至 Kubernetes 叢集。
- 使用
PipelineActivityReporterAPI 來建立和管理部署步驟和工作。 - 報告進度並將每個部署階段標記為已完成。
- 使用最終狀態更新完成部署作業。
- 透過提供的
CancellationToken來處理取消。
最佳做法
使用這些 API 時,請遵循下列準則:
形象建立
- 務必為生產環境指定明確的
ContainerBuildOptions。 - 建置部署時,請考量目標平台需求。
- 使用 OCI 格式,以最大程度地與容器登錄相容。
- 當容器執行階段健康情況檢查失敗時,處理
InvalidOperationException。
進度報告
- 將長時間執行的邏輯過程封裝在步驟中,而非直接生成原始任務。
- 保持標題簡潔(少於 60 個字元),因為 CLI 會截斷較長的字串。
- 每個發佈或部署作業只呼叫
CompletePublishAsync一次。 - 將警告視為可復原,並允許後續步驟繼續進行。
- 將錯誤視為致命的,並透過清晰的診斷快速失敗。
- 使用非同步的、具取消感知的操作,以避免阻塞事件處理。
狀態管理
- 每個步驟和工作都會以 「執行中」 狀態開始,並只轉換一次為 「已完成」、「 警告」或 「錯誤」。
- 嘗試多個狀態轉換時拋出例外狀況。
- 利用報告元件來保證事件的順序並防止事件交錯。
- 使用
IPublishingActivityStep自動處理未完成的步驟。