/health, що у реальному часі показує стан системи та всіх її залежностей: бази даних, Redis, зовнішніх API, черг повідомлень.У сучасних хмарних середовищах (Kubernetes, Azure App Service) оркестратори постійно перевіряють здоров'я контейнерів через два типи проб:
Health Checks в ASP.NET Core реалізують обидві концепції через єдину систему.
var builder = WebApplication.CreateBuilder(args);
// Реєстрація Health Checks
builder.Services.AddHealthChecks()
// Вбудована перевірка БД через EF Core
.AddDbContextCheck<AppDbContext>()
// Перевірка доступності URL
.AddUrlGroup(new Uri("https://api.example.com/ping"), "external-api");
var app = builder.Build();
// Базовий ендпоінт
app.MapHealthChecks("/health");
// Розширений ендпоінт з деталями (для внутрішнього моніторингу)
app.MapHealthChecks("/health/detail", new HealthCheckOptions
{
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
app.Run();
{
"status": "Healthy"
}
Пакет AspNetCore.HealthChecks.* надає готові перевірки для популярних технологій:
dotnet add package AspNetCore.HealthChecks.SqlServer
dotnet add package AspNetCore.HealthChecks.NpgSql
dotnet add package AspNetCore.HealthChecks.Redis
dotnet add package AspNetCore.HealthChecks.Uris
dotnet add package AspNetCore.HealthChecks.UI
dotnet add package AspNetCore.HealthChecks.UI.Client
dotnet add package AspNetCore.HealthChecks.UI.InMemory.Storage
builder.Services.AddHealthChecks()
// SQL Server / PostgreSQL
.AddNpgSql(
connectionString: builder.Configuration.GetConnectionString("Default")!,
name: "postgresql",
tags: ["database", "ready"])
// Redis
.AddRedis(
redisConnectionString: builder.Configuration.GetConnectionString("Redis")!,
name: "redis",
tags: ["cache", "ready"])
// Зовнішнє API
.AddUrlGroup(
uri: new Uri("https://payment-api.example.com/health"),
name: "payment-api",
tags: ["external", "ready"])
// Доступне місце на диску
.AddDiskStorageHealthCheck(
setup: s => s.AddDrive("C:\\", minimumFreeMegabytes: 1024),
name: "disk-storage",
tags: ["infrastructure"]);
// Health Check UI
builder.Services.AddHealthChecksUI(setup =>
{
setup.SetEvaluationTimeInSeconds(30); // Перевірка кожні 30 секунд
setup.MaximumHistoryEntriesPerEndpoint(50);
setup.AddHealthCheckEndpoint("Production API", "/health/detail");
}).AddInMemoryStorage();
// Liveness: лише чи живий процес (без зовнішніх залежностей)
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
Predicate = _ => false // Жодних перевірок — просто «я живий»
});
// Readiness: готовність приймати трафік (із залежностями)
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = hc => hc.Tags.Contains("ready"),
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
// Детальний звіт для внутрішнього моніторингу
app.MapHealthChecks("/health/detail", new HealthCheckOptions
{
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
})
.RequireAuthorization(); // Захищаємо від публічного доступу
// UI Dashboard
app.MapHealthChecksUI(config => config.UIPath = "/health-ui");
Для перевірок специфічних для вашої бізнес-логіки:
using Microsoft.Extensions.Diagnostics.HealthChecks;
public class DatabaseMigrationHealthCheck : IHealthCheck
{
private readonly AppDbContext _db;
private readonly ILogger<DatabaseMigrationHealthCheck> _logger;
public DatabaseMigrationHealthCheck(
AppDbContext db,
ILogger<DatabaseMigrationHealthCheck> logger)
{
_db = db;
_logger = logger;
}
public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken ct = default)
{
try
{
// Перевіряємо, чи всі міграції застосовані
var pendingMigrations = await _db.Database
.GetPendingMigrationsAsync(ct);
var pendingList = pendingMigrations.ToList();
if (pendingList.Count == 0)
{
return HealthCheckResult.Healthy(
"Всі міграції застосовані.");
}
return HealthCheckResult.Degraded(
description: $"Є {pendingList.Count} незастосованих міграцій.",
data: new Dictionary<string, object>
{
["pendingMigrations"] = pendingList
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Помилка перевірки міграцій БД.");
return HealthCheckResult.Unhealthy(
description: "Не вдалося підключитися до БД.",
exception: ex);
}
}
}
public class LicenseValidityHealthCheck : IHealthCheck
{
private readonly ILicenseService _licenseService;
public LicenseValidityHealthCheck(ILicenseService licenseService)
=> _licenseService = licenseService;
public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken ct = default)
{
var license = await _licenseService.GetCurrentAsync();
if (license is null)
return HealthCheckResult.Unhealthy("Ліцензія не знайдена.");
var daysUntilExpiry = (license.ExpiresAt - DateTime.UtcNow).TotalDays;
return daysUntilExpiry switch
{
< 0 => HealthCheckResult.Unhealthy(
$"Ліцензія прострочена {Math.Abs(daysUntilExpiry):F0} днів тому."),
< 7 => HealthCheckResult.Degraded(
$"Ліцензія спливає через {daysUntilExpiry:F0} днів!"),
_ => HealthCheckResult.Healthy(
$"Ліцензія дійсна. Спливає через {daysUntilExpiry:F0} днів.")
};
}
}
builder.Services.AddHealthChecks()
.AddCheck<DatabaseMigrationHealthCheck>(
name: "db-migrations",
tags: ["database", "ready"])
.AddCheck<LicenseValidityHealthCheck>(
name: "license",
failureStatus: HealthStatus.Degraded,
tags: ["business"]);
Стандартний UIResponseWriter.WriteHealthCheckUIResponse повертає детальний JSON:
{
"status": "Degraded",
"totalDuration": "00:00:00.045",
"entries": {
"postgresql": {
"status": "Healthy",
"duration": "00:00:00.012",
"tags": ["database", "ready"]
},
"redis": {
"status": "Healthy",
"duration": "00:00:00.008",
"tags": ["cache", "ready"]
},
"payment-api": {
"status": "Degraded",
"description": "Service responding slowly (1234ms)",
"duration": "00:00:01.234",
"tags": ["external", "ready"]
},
"license": {
"status": "Degraded",
"description": "Ліцензія спливає через 5 днів!",
"duration": "00:00:00.003",
"tags": ["business"]
}
}
}
Три стани:
Healthy → HTTP 200Degraded → HTTP 200 (система працює, але не оптимально)Unhealthy → HTTP 503 (система недоступна)/health/live (без перевірок) та /health/ready (з перевіркою PostgreSQL та Redis). Переконайтеся, що при недоступному Redis статус ready стає Unhealthy.QueueHealthCheck, що перевіряє розмір черги повідомлень: Healthy якщо < 100 елементів, Degraded якщо 100-500, Unhealthy якщо > 500./health/detail та /health-ui авторизацією.Liveness vs Readiness
Готові інтеграції
AspNetCore.HealthChecks.* покриває SQL Server, PostgreSQL, Redis, RabbitMQ та десятки інших.UI Dashboard
Посилання:
Відмовостійкість з Polly в ASP.NET Core
Глибокий огляд Polly: Retry, Circuit Breaker, Timeout, Fallback, Hedging. Інтеграція з HttpClient та Resilience Pipelines у .NET 8+.
Feature Management та Feature Flags в ASP.NET Core
Feature Flags у ASP.NET Core: вмикання/вимикання фіч без деплою, Feature Filters (часові, відсоткові, для груп), Azure App Configuration та поступовий rollout.