Community코딩 & 개발github.com

aspire-service-defaults

Aspire 애플리케이션을 위한 공유 ServiceDefaults 프로젝트를 생성합니다. 모든 서비스에서 OpenTelemetry, 상태 확인, 복원력, 서비스 검색 구성을 중앙화합니다.

aspire-service-defaults란 무엇인가요?

이 스킬은 Aspire 기반 분산 애플리케이션에서 모든 서비스가 일관된 관찰 가능성(로깅, 추적, 메트릭), 상태 확인(준비성 및 활성 엔드포인트), HTTP 복원력(재시도 및 차단기 정책), 그리고 서비스 검색(자동 서비스 주소 확인)을 갖추도록 하는 공유 프로젝트인 'ServiceDefaults'를 생성합니다. 각 서비스는 이 프로젝트를 참조하고 `AddServiceDefaults()` 메서드를 호출하여 공통 설정을 적용합니다. 주요 기능으로는 OpenTelemetry를 통한 분산 추적, Aspire 대시보드로의 OTLP 내보내기, Kubernetes 프로브에 최적화된 상태 확인 엔드포인트, 그리고 `StandardResilienceHandler`를 활용한 견고한 HTTP 클라이언트 정책이 포함됩니다. 이 스킬은 C# 코드 템플릿과 프로젝트 구조 예제를 제공하며, 사용자 정의 상태 확인 및 추적 소스를 추가하는 방법도 상세히 안내합니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Aaronontheweb/dotnet-skills/tree/main/skills/aspire-service-defaults

Installed? Explore more 코딩 & 개발 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Aspire Service Defaults

이 스킬을 사용해야 하는 경우

다음과 같은 경우 이 스킬을 사용하세요:

  • Aspire 기반 분산 애플리케이션을 구축할 때
  • 서비스 전반에 걸쳐 일관된 관찰 가능성(로깅, 추적, 메트릭)이 필요할 때
  • 공유 상태 확인 구성을 원할 때
  • HttpClient 복원력 및 서비스 검색을 구성할 때

ServiceDefaults란?

ServiceDefaults는 Aspire 애플리케이션의 모든 서비스에 공통 구성을 제공하는 공유 프로젝트입니다:

  • OpenTelemetry - 로깅, 추적, 메트릭
  • Health Checks - 준비성 및 활성 엔드포인트
  • Service Discovery - 자동 서비스 확인
  • HTTP Resilience - 재시도 및 차단기 정책

모든 서비스는 이 프로젝트를 참조하고 AddServiceDefaults()를 호출합니다.


프로젝트 구조

src/
  MyApp.ServiceDefaults/
    Extensions.cs
    MyApp.ServiceDefaults.csproj
  MyApp.Api/
    Program.cs  # AddServiceDefaults() 호출
  MyApp.Worker/
    Program.cs  # AddServiceDefaults() 호출
  MyApp.AppHost/
    Program.cs

ServiceDefaults 프로젝트

프로젝트 파일

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <IsAspireSharedProject>true</IsAspireSharedProject>
  </PropertyGroup>

  <ItemGroup>
    <FrameworkReference Include="Microsoft.AspNetCore.App" />
    <PackageReference Include="Microsoft.Extensions.Http.Resilience" />
    <PackageReference Include="Microsoft.Extensions.ServiceDiscovery" />
    <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" />
    <PackageReference Include="OpenTelemetry.Extensions.Hosting" />
    <PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" />
    <PackageReference Include="OpenTelemetry.Instrumentation.Http" />
    <PackageReference Include="OpenTelemetry.Instrumentation.Runtime" />
  </ItemGroup>
</Project>

Extensions.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Logging;
using OpenTelemetry;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;

namespace Microsoft.Extensions.Hosting;

public static class Extensions
{
    private const string HealthEndpointPath = "/health";
    private const string AlivenessEndpointPath = "/alive";

    /// <summary>
    /// 공통 Aspire 서비스(OpenTelemetry, 상태 확인,
    /// 서비스 검색, HTTP 복원력)를 추가합니다.
    /// </summary>
    public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder builder)
        where TBuilder : IHostApplicationBuilder
    {
        builder.ConfigureOpenTelemetry();
        builder.AddDefaultHealthChecks();

        builder.Services.AddServiceDiscovery();

        builder.Services.ConfigureHttpClientDefaults(http =>
        {
            // 복원력: 재시도, 차단기, 시간 제한
            http.AddStandardResilienceHandler();

            // 서비스 검색: 서비스 이름을 주소로 확인
            http.AddServiceDiscovery();
        });

        return builder;
    }

    public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder)
        where TBuilder : IHostApplicationBuilder
    {
        // 로깅
        builder.Logging.AddOpenTelemetry(logging =>
        {
            logging.IncludeFormattedMessage = true;
            logging.IncludeScopes = true;
        });

        builder.Services.AddOpenTelemetry()
            // 메트릭
            .WithMetrics(metrics =>
            {
                metrics
                    .AddAspNetCoreInstrumentation()
                    .AddHttpClientInstrumentation()
                    .AddRuntimeInstrumentation();
            })
            // 추적
            .WithTracing(tracing =>
            {
                tracing
                    .AddSource(builder.Environment.ApplicationName)
                    .AddAspNetCoreInstrumentation(options =>
                        // 상태 확인을 추적에서 제외
                        options.Filter = context =>
                            !context.Request.Path.StartsWithSegments(HealthEndpointPath) &&
                            !context.Request.Path.StartsWithSegments(AlivenessEndpointPath))
                    .AddHttpClientInstrumentation();
            });

        builder.AddOpenTelemetryExporters();

        return builder;
    }

    private static TBuilder AddOpenTelemetryExporters<TBuilder>(this TBuilder builder)
        where TBuilder : IHostApplicationBuilder
    {
        // OTLP 엔드포인트가 구성된 경우 OTLP 내보내기 사용(Aspire 대시보드, Jaeger 등)
        var useOtlp = !string.IsNullOrWhiteSpace(
            builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);

        if (useOtlp)
        {
            builder.Services.AddOpenTelemetry().UseOtlpExporter();
        }

        return builder;
    }

    public static TBuilder AddDefaultHealthChecks<TBuilder>(this TBuilder builder)
        where TBuilder : IHostApplicationBuilder
    {
        builder.Services.AddHealthChecks()
            .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);

        return builder;
    }

    /// <summary>
    /// 상태 확인 엔드포인트를 매핑합니다. UseRouting() 이후에 호출하세요.
    /// </summary>
    public static WebApplication MapDefaultEndpoints(this WebApplication app)
    {
        // 개발 환경에서만 노출 - 아래 보안 참고 사항 확인
        if (app.Environment.IsDevelopment())
        {
            // 준비성: 모든 상태 확인이 통과해야 함
            app.MapHealthChecks(HealthEndpointPath);

            // 활성: "live" 태그가 있는 확인만
            app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions
            {
                Predicate = r => r.Tags.Contains("live")
            });
        }

        return app;
    }
}

서비스에서의 사용법

API 서비스

var builder = WebApplication.CreateBuilder(args);

// 모든 서비스 기본값 추가
builder.AddServiceDefaults();

// 서비스 추가
builder.Services.AddControllers();

var app = builder.Build();

// 상태 엔드포인트 매핑
app.MapDefaultEndpoints();

app.MapControllers();
app.Run();

Worker 서비스

var builder = Host.CreateApplicationBuilder(args);

// 비웹 호스트에서도 작동
builder.AddServiceDefaults();

builder.Services.AddHostedService<MyWorker>();

var host = builder.Build();
host.Run();

사용자 정의 상태 확인 추가

public static TBuilder AddDefaultHealthChecks<TBuilder>(this TBuilder builder)
    where TBuilder : IHostApplicationBuilder
{
    builder.Services.AddHealthChecks()
        // 기본 활성 확인
        .AddCheck("self", () => HealthCheckResult.Healthy(), ["live"])

        // 데이터베이스 준비성
        .AddNpgSql(
            builder.Configuration.GetConnectionString("postgres")!,
            name: "postgres",
            tags: ["ready"])

        // Redis 준비성
        .AddRedis(
            builder.Configuration.GetConnectionString("redis")!,
            name: "redis",
            tags: ["ready"])

        // 사용자 정의 확인
        .AddCheck<MyCustomHealthCheck>("custom", tags: ["ready"]);

    return builder;
}

사용자 정의 추적 소스 추가

Akka.NET 또는 사용자 정의 ActivitySource의 경우:

public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder)
    where TBuilder : IHostApplicationBuilder
{
    builder.Services.AddOpenTelemetry()
        .WithTracing(tracing =>
        {
            tracing
                .AddSource(builder.Environment.ApplicationName)
                // Akka.NET 추적
                .AddSource("Akka.NET")
                // 사용자 정의 소스
                .AddSource("MyApp.Orders")
                .AddSource("MyApp.Payments")
                .AddAspNetCoreInstrumentation()
                .AddHttpClientInstrumentation();
        });

    return builder;
}

프로덕션 상태 확인

프로덕션에서는 상태 엔드포인트를 보호하거나 다른 경로를 사용하세요:

public static WebApplication MapDefaultEndpoints(this WebApplication app)
{
    // Kubernetes 프로브를 위해 항상 매핑하지만 다음을 고려하세요:
    // - 내부 전용 포트 사용
    // - 인증 추가
    // - 속도 제한

    app.MapHealthChecks("/health", new HealthCheckOptions
    {
        // 상태만 반환, 세부 정보는 반환하지 않음
        ResponseWriter = (context, report) =>
        {
            context.Response.ContentType = "text/plain";
            return context.Response.WriteAsync(report.Status.ToString());
        }
    });

    app.MapHealthChecks("/alive", new HealthCheckOptions
    {
        Predicate = r => r.Tags.Contains("live"),
        ResponseWriter = (context, report) =>
        {
            context.Response.ContentType = "text/plain";
            return context.Response.WriteAsync(report.Status.ToString());
        }
    });

    return app;
}

AppHost와 통합

AppHost는 OTLP 엔드포인트를 자동으로 구성합니다:

// AppHost/Program.cs
var builder = DistributedApplication.CreateBuilder(args);

var postgres = builder.AddPostgres("postgres");
var redis = builder.AddRedis("redis");

var api = builder.AddProject<Projects.MyApp_Api>("api")
    .WithReference(postgres)
    .WithReference(redis);

builder.Build().Run();

서비스는 OTEL_EXPORTER_OTLP_ENDPOINT를 자동으로 수신하여 Aspire 대시보드로 텔레메트리를 전송합니다.


모범 사례

사례이유
하나의 ServiceDefaults 프로젝트모든 서비스에서 일관된 구성
상태 확인을 추적에서 필터링관찰 가능성 데이터의 노이즈 감소
상태 확인에 태그 지정활성 상태와 준비성 구분
StandardResilienceHandler 사용내장된 재시도, 차단기, 시간 제한
사용자 정의 추적 소스 추가도메인별 스팬 캡처

리소스

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

akka-hosting-actor-patterns

Patterns for building entity actors with Akka.Hosting - GenericChildPerEntityParent, message extractors, cluster sharding abstraction, akka-reminders, and ITimeProvider. Supports both local testing and clustered production modes.

akka-net-aspire-configuration

Configure Akka.NET with .NET Aspire for local development and production deployments. Covers actor system setup, clustering, persistence, Akka.Management integration, and Aspire orchestration patterns.

akka-net-best-practices

Critical Akka.NET best practices including EventStream vs DistributedPubSub, supervision strategies, error handling, Props vs DependencyResolver, work distribution patterns, and cluster/local mode abstractions for testability.

akka-net-management

Akka.Management for cluster bootstrapping, service discovery (Kubernetes, Azure, Config), health checks, and dynamic cluster formation without static seed nodes.

akka-net-testing-patterns

Write unit and integration tests for Akka.NET actors using modern Akka.Hosting.TestKit patterns. Covers dependency injection, TestProbes, persistence testing, and actor interaction verification. Includes guidance on when to use traditional TestKit.

api-design

Design stable, compatible public APIs using extend-only design principles. Manage API compatibility, wire compatibility, and versioning for NuGet packages and distributed systems.

aspire-configuration

Configure Aspire AppHost to emit explicit app config via environment variables; keep app code free of Aspire clients and service discovery.

aspire-integration-testing

Write integration tests using .NET Aspire

crap-analysis

Analyze code coverage and CRAP (Change Risk Anti-Patterns) scores to identify high-risk code. Use OpenCover format with ReportGenerator for Risk Hotspots showing cyclomatic complexity and untested code paths.

csharp-concurrency-patterns

Choosing the right concurrency abstraction in .NET - from async/await for I/O to Channels for producer/consumer to Akka.NET for stateful entity management. Avoid locks and manual synchronization unless absolutely necessary.

database-performance

Database access patterns for performance. Separate read/write models, avoid N+1 queries, use AsNoTracking, apply row limits, and never do application-side joins. Works with EF Core and Dapper.

dependency-injection-patterns

Organize DI registrations using IServiceCollection extension methods. Group related services into composable Add* methods for clean Program.cs and reusable configuration in tests.

dotnet-devcert-trust

Diagnose and fix .NET HTTPS dev certificate trust issues on Linux. Covers the full certificate lifecycle from generation to system CA bundle inclusion, with distro-specific guidance for Ubuntu, Fedora, Arch, and WSL2.

dotnet-local-tools

Managing local .NET tools with dotnet-tools.json for consistent tooling across development environments and CI/CD pipelines.

dotnet-project-structure

Modern .NET project structure including .slnx solution format, Directory.Build.props, central package management, SourceLink, version management with RELEASE_NOTES.md, and SDK pinning with global.json.

dotnet-slopwatch

Use Slopwatch to detect LLM reward hacking in .NET code changes. Run after every code modification to catch disabled tests, suppressed warnings, empty catch blocks, and other shortcuts that mask real problems.

efcore-patterns

Entity Framework Core best practices including NoTracking by default, query splitting for navigation collections, migration management, dedicated migration services, and common pitfalls to avoid.

ilspy-decompile

Understand implementation details of .NET code by decompiling assemblies. Use when you want to see how a .NET API works internally, inspect NuGet package source, view framework implementation, or understand compiled .NET binaries.

mailpit-integration

Test email sending locally using Mailpit with .NET Aspire. Captures all outgoing emails without sending them. View rendered HTML, inspect headers, and verify delivery in integration tests.

marketplace-publishing

Workflow for publishing skills and agents to the dotnet-skills Claude Code marketplace. Covers adding new content, updating plugin.json, validation, and release tagging.

관련 스킬