Kestrel
ASP.NET Core用の高性能クロスプラットフォームWebサーバー。最高のパフォーマンスとメモリ使用率を提供。コンテナーやエッジデバイス最適化。
アプリケーションサーバー
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