【.NET Aspire入門】第1回:クラウドネイティブ開発の新時代

はじめに

クラウドネイティブアプリケーションの開発は、現代のソフトウェア開発において避けて通れない道となりました。しかし、マイクロサービスアーキテクチャの複雑性、分散システムの運用、ローカル開発環境の構築など、多くの課題があります。

.NET Aspireは、これらの課題を解決するためにMicrosoftが開発した革新的なフレームワークです。本シリーズでは、.NET Aspireを使ったクラウドネイティブアプリケーション開発を基礎から実践まで、段階的に解説していきます。

.NET Aspireとは?

.NET Aspireは、クラウドネイティブアプリケーションの開発を簡素化するためのオープンソースフレームワークです。分散アプリケーションの構築、デバッグ、デプロイを効率化し、開発者の生産性を大幅に向上させます。

主な特徴

  1. 統合開発体験: ローカル開発からクラウドデプロイまでシームレスに対応
  2. サービスディスカバリー: 自動的なサービス検出と接続
  3. 観測可能性: 組み込みのロギング、メトリクス、トレーシング
  4. 開発者ツール: Visual StudioやVS Codeとの深い統合
  5. 柔軟なデプロイメント: Docker、Kubernetes、Azure Container Appsなど多様な環境に対応

なぜ.NET Aspireが必要なのか

従来の課題

# 従来の docker-compose.yml の例
version: '3.8'
services:
  api:
    build: ./api
    ports:
      - "5000:80"
    environment:
      - ConnectionStrings__DefaultConnection=Server=db;Database=MyApp;User=sa;Password=MyPassword123!
      - Redis__ConnectionString=redis:6379
    depends_on:
      - db
      - redis
  
  frontend:
    build: ./frontend
    ports:
      - "3000:80"
    environment:
      - API_URL=http://api:80
    depends_on:
      - api
  
  db:
    image: mcr.microsoft.com/mssql/server:2022-latest
    environment:
      - ACCEPT_EULA=Y
      - SA_PASSWORD=MyPassword123!
    ports:
      - "1433:1433"
  
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

このような構成では以下の問題があります:

  • 環境変数の管理が複雑
  • サービス間の依存関係の定義が煩雑
  • ローカル開発時のデバッグが困難
  • 観測可能性の実装が別途必要

.NET Aspireによる解決

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

// インフラストラクチャの定義
var cache = builder.AddRedis("cache");
var database = builder.AddSqlServer("sql")
    .AddDatabase("myapp");

// APIサービスの追加
var api = builder.AddProject<Projects.MyApp_Api>("api")
    .WithReference(cache)
    .WithReference(database);

// フロントエンドの追加
var frontend = builder.AddProject<Projects.MyApp_Frontend>("frontend")
    .WithReference(api);

builder.Build().Run();

開発環境のセットアップ

前提条件

  • .NET 8.0 以上
  • Docker Desktop
  • Visual Studio 2022 17.9+ または VS Code
  • .NET Aspire ワークロード

インストール手順

# .NET Aspire ワークロードのインストール
dotnet workload update
dotnet workload install aspire

# インストールの確認
dotnet workload list

Visual Studio での設定

Visual Studio を使用する場合は、以下の手順でセットアップします:

  1. Visual Studio Installer を開く
  2. 「変更」をクリック
  3. 「ASP.NET と Web 開発」ワークロードを選択
  4. 「.NET Aspire SDK (プレビュー)」にチェック
  5. インストール

最初の.NET Aspireアプリケーション

プロジェクトの作成

# ソリューションの作成
dotnet new aspire-starter -n MyFirstAspireApp
cd MyFirstAspireApp

# プロジェクト構造の確認
tree /F

生成されるプロジェクト構造:

MyFirstAspireApp/
├── MyFirstAspireApp.sln
├── MyFirstAspireApp.ApiService/
│   ├── Program.cs
│   ├── appsettings.json
│   └── MyFirstAspireApp.ApiService.csproj
├── MyFirstAspireApp.AppHost/
│   ├── Program.cs
│   ├── appsettings.json
│   └── MyFirstAspireApp.AppHost.csproj
├── MyFirstAspireApp.ServiceDefaults/
│   ├── Extensions.cs
│   └── MyFirstAspireApp.ServiceDefaults.csproj
└── MyFirstAspireApp.Web/
    ├── Program.cs
    ├── Components/
    └── MyFirstAspireApp.Web.csproj

各プロジェクトの役割

  1. AppHost: オーケストレーター(全体の構成を定義)
  2. ServiceDefaults: 共通設定(ロギング、ヘルスチェックなど)
  3. ApiService: バックエンドAPI
  4. Web: フロントエンドアプリケーション

基本的なサービス構成

AppHost の設定

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

// Redisキャッシュの追加
var cache = builder.AddRedis("cache");

// APIサービスの追加(Redisを参照)
var apiService = builder.AddProject<Projects.MyFirstAspireApp_ApiService>("apiservice")
    .WithReference(cache);

// Webフロントエンドの追加(APIを参照)
builder.AddProject<Projects.MyFirstAspireApp_Web>("webfrontend")
    .WithReference(apiService);

builder.Build().Run();

ServiceDefaults の実装

// MyFirstAspireApp.ServiceDefaults/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.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Trace;

namespace Microsoft.Extensions.Hosting;

public static class Extensions
{
    public static IHostApplicationBuilder AddServiceDefaults(
        this IHostApplicationBuilder builder)
    {
        // OpenTelemetryの設定
        builder.Logging.AddOpenTelemetry(logging =>
        {
            logging.IncludeFormattedMessage = true;
            logging.IncludeScopes = true;
        });

        builder.Services.AddOpenTelemetry()
            .WithMetrics(metrics =>
            {
                metrics.AddAspNetCoreInstrumentation()
                    .AddHttpClientInstrumentation()
                    .AddRuntimeInstrumentation();
            })
            .WithTracing(tracing =>
            {
                tracing.AddAspNetCoreInstrumentation()
                    .AddHttpClientInstrumentation();
            });

        // デフォルトのヘルスチェック
        builder.Services.AddDefaultHealthChecks();

        // サービスディスカバリーの設定
        builder.Services.AddServiceDiscovery();
        builder.Services.ConfigureHttpClientDefaults(http =>
        {
            http.AddStandardResilienceHandler();
            http.AddServiceDiscovery();
        });

        return builder;
    }

    public static IHostApplicationBuilder ConfigureOpenTelemetryLogging(
        this IHostApplicationBuilder builder)
    {
        var otlpEndpoint = builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"];

        if (!string.IsNullOrEmpty(otlpEndpoint))
        {
            builder.Logging.AddOpenTelemetry(logging =>
            {
                logging.AddOtlpExporter();
            });
        }

        return builder;
    }

    private static IServiceCollection AddDefaultHealthChecks(
        this IServiceCollection services)
    {
        services.AddHealthChecks()
            .AddCheck("self", () => HealthCheckResult.Healthy(), 
                ["live"]);

        return services;
    }

    public static WebApplication MapDefaultEndpoints(
        this WebApplication app)
    {
        // ヘルスチェックエンドポイント
        app.MapHealthChecks("/health");
        app.MapHealthChecks("/alive", new HealthCheckOptions
        {
            Predicate = r => r.Tags.Contains("live")
        });

        return app;
    }
}

実行とデバッグ

アプリケーションの起動

# AppHostプロジェクトから起動
cd MyFirstAspireApp.AppHost
dotnet run

起動すると、Aspire Dashboardが自動的に開きます:

  • URL: https://localhost:17209 (ポートは自動割り当て)
  • ダッシュボード機能:
    • サービスの状態監視
    • ログの集約表示
    • 分散トレーシング
    • メトリクスの可視化

Visual Studioでのデバッグ

  1. ソリューションエクスプローラーでAppHostプロジェクトを右クリック
  2. 「スタートアッププロジェクトに設定」を選択
  3. F5キーでデバッグ開始
  4. 各サービスに個別にブレークポイントを設定可能

観測可能性の活用

ログの確認

// ApiService でのロギング例
app.MapGet("/weatherforecast", (ILogger<Program> logger) =>
{
    logger.LogInformation("Weather forecast requested at {Time}", 
        DateTimeOffset.UtcNow);
    
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    
    logger.LogInformation("Returning {Count} weather forecasts", 
        forecast.Length);
    
    return forecast;
});

分散トレーシング

// サービス間の呼び出しを自動的にトレース
public class WeatherService
{
    private readonly HttpClient _httpClient;
    private readonly ILogger<WeatherService> _logger;

    public WeatherService(HttpClient httpClient, ILogger<WeatherService> logger)
    {
        _httpClient = httpClient;
        _logger = logger;
    }

    public async Task<WeatherForecast[]> GetForecastAsync()
    {
        // このHTTP呼び出しは自動的にトレースされる
        var response = await _httpClient.GetFromJsonAsync<WeatherForecast[]>(
            "http://apiservice/weatherforecast");
        
        return response ?? Array.Empty<WeatherForecast>();
    }
}

トラブルシューティング

よくある問題と解決方法

  1. Docker Desktopが起動していない

    エラー: Docker daemon is not running
    解決: Docker Desktopを起動してください
    
  2. ポートの競合

    エラー: Address already in use
    解決: 使用中のポートを確認し、必要に応じて変更
    
  3. ワークロードがインストールされていない

    # 再インストール
    dotnet workload clean
    dotnet workload install aspire
    

まとめ

今回は、.NET Aspireの基本概念と開発環境のセットアップ、最初のアプリケーション作成について学びました。重要なポイント:

  1. .NET Aspireの利点: 分散アプリケーション開発の複雑性を大幅に削減
  2. 統合された開発体験: ローカル開発からクラウドデプロイまでシームレス
  3. 組み込みの観測可能性: ロギング、メトリクス、トレーシングが標準装備
  4. 開発者フレンドリー: Visual Studioとの深い統合によるデバッグの容易さ

次回は、データベースやメッセージングなど、より実践的なサービス統合について解説します。

参考リンク


次回予告:「第2回:データベースとキャッシングの統合」では、SQL Server、PostgreSQL、Redisなどのデータストアを.NET Aspireアプリケーションに統合する方法を詳しく解説します。

技術的な課題をお持ちですか専門チームがサポートします

記事でご紹介した技術や実装について、
より詳細なご相談やプロジェクトのサポートを承ります。