Kestrel

ASP.NET Core用の高性能クロスプラットフォームWebサーバー。最高のパフォーマンスとメモリ使用率を提供。コンテナーやエッジデバイス最適化。

アプリケーションサーバー.NETASP.NET CoreWebサーバークロスプラットフォーム高性能Microsoft

アプリケーションサーバー

Kestrel

概要

Kestrelは、ASP.NET Core用の高性能クロスプラットフォームWebサーバーです。最高のパフォーマンスとメモリ使用率を提供し、コンテナーやエッジデバイスに最適化されています。.NET開発における標準Webサーバーとして、本番環境ではリバースプロキシとの組み合わせが推奨されています。Windows、Linux、macOSで動作するクロスプラットフォーム対応により、多様な環境での展開が可能です。

詳細

Kestrelは.NET Core 1.0とともに2016年にリリースされ、ASP.NET Coreアプリケーションのデフォルトサーバーとして設計されました。libuv(Node.jsと同じI/Oライブラリ)をベースとした非同期I/Oエンジンを使用し、軽量でありながら高いパフォーマンスを実現しています。.NET 6以降では、より高度な最適化とHTTP/2、HTTP/3サポートが強化されています。

主要な技術的特徴

  • 高性能: 非同期I/Oによる高いスループット
  • 軽量: 最小限のメモリフットプリント
  • クロスプラットフォーム: Windows、Linux、macOS対応
  • HTTP/2、HTTP/3サポート: 最新プロトコル対応
  • リバースプロキシ連携: IIS、nginx、Apache連携
  • コンテナ最適化: Docker、Kubernetes環境対応

用途

  • ASP.NET Core Webアプリケーション
  • Web API開発
  • マイクロサービス
  • クラウドネイティブアプリケーション
  • コンテナ化アプリケーション
  • エッジコンピューティング

メリット・デメリット

メリット

  • 高いパフォーマンス: 業界トップクラスのベンチマーク結果
  • 軽量: メモリ使用量が少ない
  • 開発効率: ASP.NET Coreとの完全統合
  • モダンプロトコル: HTTP/2、HTTP/3、WebSocket対応
  • セキュリティ: 最新のセキュリティ機能内蔵
  • クロスプラットフォーム: 多様な環境での動作

デメリット

  • プラットフォーム依存: .NET環境が必須
  • リバースプロキシ必要: 本番環境では前段プロキシが推奨
  • 設定複雑性: 高度な設定には専門知識が必要
  • エコシステム: Node.jsやPythonと比較してエコシステムが限定的
  • 学習コスト: .NET開発経験が必要

インストール・基本設定

前提条件

# .NET SDK の確認
dotnet --version
dotnet --info

# 対応バージョン確認
dotnet --list-sdks
dotnet --list-runtimes

プロジェクト作成とKestrel設定

新規プロジェクト作成

# Web API プロジェクト作成
dotnet new webapi -n MyKestrelApp
cd MyKestrelApp

# Web App プロジェクト作成
dotnet new webapp -n MyWebApp
cd MyWebApp

# 依存関係確認
dotnet restore

Program.cs基本設定

// Program.cs (.NET 6以降)
var builder = WebApplication.CreateBuilder(args);

// Kestrel設定
builder.WebHost.ConfigureKestrel(serverOptions =>
{
    // HTTP ポート設定
    serverOptions.ListenAnyIP(5000);
    
    // HTTPS ポート設定
    serverOptions.ListenAnyIP(5001, listenOptions =>
    {
        listenOptions.UseHttps();
    });
    
    // 限界値設定
    serverOptions.Limits.MaxConcurrentConnections = 100;
    serverOptions.Limits.MaxConcurrentUpgradedConnections = 100;
    serverOptions.Limits.MaxRequestBodySize = 10 * 1024 * 1024; // 10MB
    serverOptions.Limits.MinRequestBodyDataRate = new MinDataRate(
        bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
    serverOptions.Limits.MinResponseDataRate = new MinDataRate(
        bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
});

// サービス設定
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// パイプライン設定
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();

appsettings.json設定

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.AspNetCore.Server.Kestrel": "Information"
    }
  },
  "AllowedHosts": "*",
  "Kestrel": {
    "Endpoints": {
      "Http": {
        "Url": "http://0.0.0.0:5000"
      },
      "Https": {
        "Url": "https://0.0.0.0:5001",
        "Certificate": {
          "Path": "/app/certificates/aspnetapp.pfx",
          "Password": "password123"
        }
      }
    },
    "Limits": {
      "MaxConcurrentConnections": 100,
      "MaxRequestBodySize": 10485760,
      "RequestHeadersTimeout": "00:00:30",
      "MaxRequestLineSize": 8192,
      "MaxRequestHeaderCount": 100,
      "MaxRequestHeadersTotalSize": 32768
    }
  }
}

基本的なコントローラー

// Controllers/HealthController.cs
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class HealthController : ControllerBase
{
    private readonly ILogger<HealthController> _logger;

    public HealthController(ILogger<HealthController> logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public IActionResult Get()
    {
        return Ok(new
        {
            Status = "Healthy",
            Timestamp = DateTime.UtcNow,
            Server = "Kestrel",
            Version = Environment.Version.ToString(),
            MachineName = Environment.MachineName
        });
    }

    [HttpGet("info")]
    public IActionResult GetInfo()
    {
        return Ok(new
        {
            Framework = RuntimeInformation.FrameworkDescription,
            OSDescription = RuntimeInformation.OSDescription,
            ProcessArchitecture = RuntimeInformation.ProcessArchitecture,
            WorkingSet = Environment.WorkingSet,
            ProcessorCount = Environment.ProcessorCount
        });
    }
}

詳細設定とチューニング

エンドポイント設定

// Program.cs - 高度なエンドポイント設定
builder.WebHost.ConfigureKestrel(serverOptions =>
{
    // HTTP/1.1 エンドポイント
    serverOptions.Listen(IPAddress.Any, 5000, listenOptions =>
    {
        listenOptions.Protocols = HttpProtocols.Http1;
    });

    // HTTP/2 エンドポイント
    serverOptions.Listen(IPAddress.Any, 5001, listenOptions =>
    {
        listenOptions.Protocols = HttpProtocols.Http2;
        listenOptions.UseHttps();
    });

    // HTTP/1.1 と HTTP/2 両対応
    serverOptions.Listen(IPAddress.Any, 5002, listenOptions =>
    {
        listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
        listenOptions.UseHttps();
    });

    // Unix ソケット(Linux/macOS)
    if (!OperatingSystem.IsWindows())
    {
        serverOptions.ListenUnixSocket("/tmp/kestrel.sock");
    }

    // 名前付きパイプ(Windows)
    if (OperatingSystem.IsWindows())
    {
        serverOptions.ListenNamedPipe("mypipe");
    }
});

SSL/TLS設定

// SSL証明書設定
builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.Listen(IPAddress.Any, 5001, listenOptions =>
    {
        // ファイルから証明書読み込み
        listenOptions.UseHttps("/path/to/cert.pfx", "password");
        
        // または X509Certificate2 使用
        // var cert = new X509Certificate2("/path/to/cert.pfx", "password");
        // listenOptions.UseHttps(cert);
        
        // TLS 設定
        listenOptions.UseHttps(httpsOptions =>
        {
            httpsOptions.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13;
            httpsOptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate;
            httpsOptions.CheckCertificateRevocation = true;
        });
    });
});

パフォーマンス最適化

// 高性能設定
builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.Limits.MaxConcurrentConnections = 1000;
    serverOptions.Limits.MaxConcurrentUpgradedConnections = 1000;
    serverOptions.Limits.MaxRequestBodySize = 50 * 1024 * 1024; // 50MB
    serverOptions.Limits.MinRequestBodyDataRate = new MinDataRate(
        bytesPerSecond: 240, gracePeriod: TimeSpan.FromSeconds(5));
    serverOptions.Limits.MinResponseDataRate = new MinDataRate(
        bytesPerSecond: 240, gracePeriod: TimeSpan.FromSeconds(5));
    
    // ヘッダー制限
    serverOptions.Limits.RequestHeadersTimeout = TimeSpan.FromSeconds(15);
    serverOptions.Limits.MaxRequestLineSize = 8192;
    serverOptions.Limits.MaxRequestHeaderCount = 100;
    serverOptions.Limits.MaxRequestHeadersTotalSize = 32768;
    
    // HTTP/2 設定
    serverOptions.Limits.Http2.MaxStreamsPerConnection = 100;
    serverOptions.Limits.Http2.HeaderTableSize = 4096;
    serverOptions.Limits.Http2.MaxFrameSize = 16384;
    serverOptions.Limits.Http2.MaxRequestHeaderFieldSize = 8192;
});

// 追加のパフォーマンス設定
builder.Services.Configure<KestrelServerOptions>(options =>
{
    options.AllowSynchronousIO = false;
    options.DisableStringReuse = false;
});

ミドルウェア設定

// ミドルウェアチェーン最適化
var app = builder.Build();

// セキュリティヘッダー
app.Use(async (context, next) =>
{
    context.Response.Headers.Add("X-Frame-Options", "DENY");
    context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
    context.Response.Headers.Add("X-XSS-Protection", "1; mode=block");
    context.Response.Headers.Add("Strict-Transport-Security", 
        "max-age=31536000; includeSubDomains");
    
    await next();
});

// 圧縮
app.UseResponseCompression();

// HTTPS リダイレクト
app.UseHttpsRedirection();

// 静的ファイル(最適化)
app.UseStaticFiles(new StaticFileOptions
{
    OnPrepareResponse = ctx =>
    {
        const int durationInSeconds = 60 * 60 * 24 * 365; // 1年
        ctx.Context.Response.Headers[HeaderNames.CacheControl] =
            "public,max-age=" + durationInSeconds;
    }
});

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

Docker・コンテナ設定

Dockerfile

# マルチステージビルド
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src

# プロジェクトファイルコピーと復元
COPY ["MyKestrelApp.csproj", "."]
RUN dotnet restore "MyKestrelApp.csproj"

# ソースコードコピーとビルド
COPY . .
RUN dotnet build "MyKestrelApp.csproj" -c Release -o /app/build

# 公開
FROM build AS publish
RUN dotnet publish "MyKestrelApp.csproj" -c Release -o /app/publish /p:UseAppHost=false

# ランタイムイメージ
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
WORKDIR /app

# 非rootユーザー作成
RUN useradd -m -u 1001 kestrel
USER kestrel

COPY --from=publish /app/publish .

# 環境変数
ENV ASPNETCORE_URLS=http://+:8080
ENV ASPNETCORE_ENVIRONMENT=Production

EXPOSE 8080

# ヘルスチェック
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:8080/api/health || exit 1

ENTRYPOINT ["dotnet", "MyKestrelApp.dll"]

docker-compose.yml

version: '3.8'

services:
  kestrel-app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - ASPNETCORE_URLS=http://+:8080
      - ConnectionStrings__DefaultConnection=Server=db;Database=MyApp;User=sa;Password=YourPassword123;TrustServerCertificate=true;
    depends_on:
      - db
    restart: unless-stopped
    
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/ssl
    depends_on:
      - kestrel-app
    restart: unless-stopped

  db:
    image: mcr.microsoft.com/mssql/server:2022-latest
    environment:
      - ACCEPT_EULA=Y
      - SA_PASSWORD=YourPassword123
    ports:
      - "1433:1433"
    volumes:
      - sqldata:/var/opt/mssql
    restart: unless-stopped

volumes:
  sqldata:

本番環境設定

nginx リバースプロキシ設定

# /etc/nginx/nginx.conf
upstream kestrel {
    server 127.0.0.1:5000;
    # 複数インスタンスの場合
    # server 127.0.0.1:5001;
    # server 127.0.0.1:5002;
}

server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    client_max_body_size 50M;
    
    location / {
        proxy_pass http://kestrel;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection keep-alive;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
        proxy_buffering off;
        proxy_read_timeout 100s;
        proxy_connect_timeout 5s;
    }
}

Systemd サービス設定

# /etc/systemd/system/kestrel-myapp.service
[Unit]
Description=My Kestrel App
After=network.target

[Service]
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/dotnet /var/www/myapp/MyKestrelApp.dll
Restart=always
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=myapp
User=www-data
Group=www-data
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=ASPNETCORE_URLS=http://0.0.0.0:5000
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false

# セキュリティ設定
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ReadWritePaths=/var/www/myapp/logs
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes

[Install]
WantedBy=multi-user.target

IIS設定(Windows)

<!-- web.config -->
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <handlers>
        <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath="dotnet"
                  arguments=".\MyKestrelApp.dll"
                  stdoutLogEnabled="false"
                  stdoutLogFile=".\logs\stdout"
                  hostingModel="inprocess" />
    </system.webServer>
  </location>
</configuration>

監視・ログ・診断

構造化ログ設定

// Program.cs - ログ設定
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddEventSourceLogger();

// Serilog使用例
builder.Host.UseSerilog((context, configuration) =>
{
    configuration
        .ReadFrom.Configuration(context.Configuration)
        .Enrich.FromLogContext()
        .Enrich.WithMachineName()
        .Enrich.WithThreadId()
        .WriteTo.Console()
        .WriteTo.File("/var/log/myapp/app-.log", 
            rollingInterval: RollingInterval.Day,
            retainedFileCountLimit: 30);
});

// カスタムミドルウェア for リクエストログ
app.Use(async (context, next) =>
{
    var sw = Stopwatch.StartNew();
    try
    {
        await next();
    }
    finally
    {
        sw.Stop();
        var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();
        logger.LogInformation("Request {Method} {Path} responded {StatusCode} in {Elapsed:0.0000} ms",
            context.Request.Method,
            context.Request.Path,
            context.Response.StatusCode,
            sw.Elapsed.TotalMilliseconds);
    }
});

メトリクス・ヘルスチェック

// ヘルスチェック追加
builder.Services.AddHealthChecks()
    .AddCheck("self", () => HealthCheckResult.Healthy())
    .AddSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))
    .AddUrlGroup(new Uri("https://google.com"), "external_service");

// メトリクス
builder.Services.AddApplicationInsightsTelemetry();

// アプリケーション設定
app.MapHealthChecks("/health", new HealthCheckOptions
{
    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});

app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
    Predicate = check => check.Tags.Contains("ready"),
});

app.MapHealthChecks("/health/live", new HealthCheckOptions
{
    Predicate = _ => false,
});

パフォーマンスカウンター

// カスタムメトリクス
public class MetricsMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<MetricsMiddleware> _logger;
    private readonly Counter<int> _requestCounter;

    public MetricsMiddleware(RequestDelegate next, ILogger<MetricsMiddleware> logger, IMeterFactory meterFactory)
    {
        _next = next;
        _logger = logger;
        var meter = meterFactory.Create("MyApp.Kestrel");
        _requestCounter = meter.CreateCounter<int>("requests_total", "Total number of requests");
    }

    public async Task InvokeAsync(HttpContext context)
    {
        _requestCounter.Add(1, new TagList { ["method"] = context.Request.Method, ["endpoint"] = context.Request.Path });
        await _next(context);
    }
}

// ミドルウェア登録
app.UseMiddleware<MetricsMiddleware>();

セキュリティ設定

HTTPS と証明書

// 開発環境での自己署名証明書
builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.Listen(IPAddress.Any, 5001, listenOptions =>
    {
        if (builder.Environment.IsDevelopment())
        {
            listenOptions.UseHttps();
        }
        else
        {
            listenOptions.UseHttps("/etc/ssl/certs/aspnetapp.pfx", "password");
        }
    });
});

// HSTS設定
builder.Services.AddHsts(options =>
{
    options.Preload = true;
    options.IncludeSubDomains = true;
    options.MaxAge = TimeSpan.FromDays(365);
});

app.UseHsts();

セキュリティヘッダー

// セキュリティミドルウェア
app.Use(async (context, next) =>
{
    context.Response.Headers.Add("X-Frame-Options", "DENY");
    context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
    context.Response.Headers.Add("X-XSS-Protection", "1; mode=block");
    context.Response.Headers.Add("Referrer-Policy", "strict-origin-when-cross-origin");
    context.Response.Headers.Add("Content-Security-Policy", 
        "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'");
    
    await next();
});

// CORS設定
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowSpecificOrigins", policy =>
    {
        policy.WithOrigins("https://example.com", "https://app.example.com")
              .AllowAnyHeader()
              .AllowAnyMethod()
              .AllowCredentials();
    });
});

app.UseCors("AllowSpecificOrigins");

デプロイ・CI/CD

GitHub Actions

# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [ main ]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Setup .NET
      uses: actions/setup-dotnet@v3
      with:
        dotnet-version: 8.0.x
        
    - name: Restore dependencies
      run: dotnet restore
      
    - name: Build
      run: dotnet build --no-restore -c Release
      
    - name: Test
      run: dotnet test --no-build --verbosity normal
      
    - name: Publish
      run: dotnet publish -c Release -o ./publish
      
    - name: Build Docker image
      run: docker build -t myapp:${{ github.sha }} .
      
    - name: Deploy to production
      run: |
        # Docker deployment script
        docker stop myapp || true
        docker rm myapp || true
        docker run -d --name myapp -p 5000:8080 myapp:${{ github.sha }}

Azure App Service

# Azure CLI デプロイ
az webapp up --name myapp --resource-group myresourcegroup --plan myappserviceplan

参考ページ