💻 Développement

dev-health-check-monitor

Implémentation de health checks ASP.NET Core, readiness/liveness probes et intégration Kubernetes.

⚡ Installation & lancement en 1 commande

Copiez-collez dans votre terminal : le skill s'installe dans ~/.claude/skills et Claude Code se lance directement dessus.

macOS / Linux
curl -fsSL https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.sh | sh -s -- dev-health-check-monitor --launch
Windows (PowerShell)
iex "& { $(iwr -useb https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.ps1) } dev-health-check-monitor -Launch"

🚀 Déjà installé ?

claude "/dev-health-check-monitor"

Ou tapez /dev-health-check-monitor dans une session Claude Code, ou décrivez simplement votre besoin — le skill se déclenche automatiquement via le skill-router.

🔑 Déclencheurs automatiques

Le skill s'active automatiquement quand votre demande contient :

health checkliveness probereadiness probeendpoint santéhealth endpointASP.NET healthmonitoring applicatif

📦 Installation manuelle

git clone https://github.com/khalilbenaz/claude-skills-collection.git cp -r claude-skills-collection/skills/dev-health-check-monitor ~/.claude/skills/

Payload du plugin : skills/dev-health-check-monitor · source éditable : dev-skills/health-check-monitor

📖 Manuel

Health Checks & Monitoring

Workflow en 5 étapes

  1. Inventorier les dépendances — base de données, cache, message broker, services HTTP tiers, système de fichiers.
  2. Classifier les checks — liveness (process vivant), readiness (dépendances critiques up), startup (warm-up initial).
  3. Implémenter les checks — un IHealthCheck par dépendance ; package NuGet dédié quand il existe.
  4. Exposer les endpoints/health/live minimal, /health/ready filtré par tags, /health/full protégé.
  5. Brancher l'observabilité — Kubernetes probes, Prometheus scraping, alertes Grafana.

Packages NuGet recommandés (2026)

dotnet add package AspNetCore.HealthChecks.SqlServer
dotnet add package AspNetCore.HealthChecks.Redis
dotnet add package AspNetCore.HealthChecks.RabbitMQ
dotnet add package AspNetCore.HealthChecks.Uris
dotnet add package AspNetCore.HealthChecks.Npgsql       # PostgreSQL
dotnet add package AspNetCore.HealthChecks.Kafka
dotnet add package AspNetCore.HealthChecks.UI.Client    # UI dashboard optionnel

Configuration ASP.NET Core

Program.cs — setup complet

builder.Services.AddHealthChecks()
    // SQL Server — tag "ready" = inclus dans readiness probe
    .AddSqlServer(
        builder.Configuration.GetConnectionString("Default")!,
        name: "sqlserver",
        tags: ["db", "ready"],
        timeout: TimeSpan.FromSeconds(5))

    // Redis
    .AddRedis(
        builder.Configuration["Redis:ConnectionString"]!,
        name: "redis",
        tags: ["cache", "ready"],
        timeout: TimeSpan.FromSeconds(3))

    // RabbitMQ
    .AddRabbitMQ(
        builder.Configuration["RabbitMQ:Uri"]!,
        name: "rabbitmq",
        tags: ["messaging", "ready"],
        timeout: TimeSpan.FromSeconds(5))

    // URL externe — NON incluse dans liveness ni readiness
    .AddUrlGroup(
        new Uri("https://api.partner.com/ping"),
        name: "partner-api",
        tags: ["external"],
        timeout: TimeSpan.FromSeconds(8))

    // Check custom
    .AddCheck<DiskSpaceHealthCheck>("disk-space",
        tags: ["infra"],
        timeout: TimeSpan.FromSeconds(2));

// --- Endpoints ---

// Liveness : aucun check, répond 200 si le process tourne
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
    Predicate = _ => false,
    ResponseWriter = WriteMinimalResponse
});

// Readiness : seules les dépendances critiques (tag "ready")
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
    Predicate = hc => hc.Tags.Contains("ready"),
    ResponseWriter = WriteDetailedResponse
});

// Full : tous les checks, endpoint protégé
app.MapHealthChecks("/health/full", new HealthCheckOptions
{
    ResponseWriter = WriteDetailedResponse
}).RequireAuthorization("internal");

IHealthCheck personnalisé

public class DiskSpaceHealthCheck(ILogger<DiskSpaceHealthCheck> logger) : IHealthCheck
{
    private const long MinFreeBytes = 1_073_741_824; // 1 GB

    public Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context, CancellationToken ct = default)
    {
        var drive = new DriveInfo(OperatingSystem.IsWindows() ? "C:\\" : "/");
        var free = drive.AvailableFreeSpace;

        var data = new Dictionary<string, object>
        {
            ["free_gb"] = Math.Round((double)free / 1_073_741_824, 2),
            ["drive"]   = drive.Name
        };

        if (free < MinFreeBytes)
        {
            logger.LogWarning("Disk space low: {FreeGb} GB", data["free_gb"]);
            return Task.FromResult(HealthCheckResult.Degraded(
                $"Espace disque faible : {data["free_gb"]} GB", data: data));
        }

        return Task.FromResult(HealthCheckResult.Healthy(null, data));
    }
}

ResponseWriter JSON structuré

static async Task WriteDetailedResponse(HttpContext ctx, HealthReport report)
{
    ctx.Response.ContentType = MediaTypeNames.Application.Json;
    ctx.Response.StatusCode = report.Status == HealthStatus.Unhealthy ? 503 : 200;

    var payload = new
    {
        status   = report.Status.ToString(),
        duration = report.TotalDuration.TotalMilliseconds,
        checks   = report.Entries.Select(e => new
        {
            name        = e.Key,
            status      = e.Value.Status.ToString(),
            duration_ms = e.Value.Duration.TotalMilliseconds,
            description = e.Value.Description,
            data        = e.Value.Data,
            error       = e.Value.Exception?.Message
        })
    };
    await ctx.Response.WriteAsJsonAsync(payload);
}

static Task WriteMinimalResponse(HttpContext ctx, HealthReport report)
{
    ctx.Response.ContentType = MediaTypeNames.Text.Plain;
    ctx.Response.StatusCode  = report.Status == HealthStatus.Unhealthy ? 503 : 200;
    return ctx.Response.WriteAsync(report.Status.ToString());
}

Intégration Kubernetes

Critères de choix des probes

ProbeQuestionConséquence échecEndpoint conseillé
startupProbeL'app a-t-elle fini de démarrer ?liveness/readiness suspendues/health/live
livenessProbeLe process est-il fonctionnel ?Pod redémarré/health/live
readinessProbePeut-on envoyer du trafic ?Pod retiré du LB/health/ready

deployment.yaml

spec:
  containers:
    - name: payment-api
      image: myregistry/payment-api:1.0.0
      ports:
        - containerPort: 8080
      startupProbe:
        httpGet:
          path: /health/live
          port: 8080
        initialDelaySeconds: 0
        periodSeconds: 3
        failureThreshold: 30   # 90 s max pour démarrer
      livenessProbe:
        httpGet:
          path: /health/live
          port: 8080
        initialDelaySeconds: 0
        periodSeconds: 10
        failureThreshold: 3
        timeoutSeconds: 5
      readinessProbe:
        httpGet:
          path: /health/ready
          port: 8080
        initialDelaySeconds: 0
        periodSeconds: 5
        failureThreshold: 3
        timeoutSeconds: 5

Critères de décision


Monitoring Prometheus + Grafana

// Exposer les métriques health check pour Prometheus
builder.Services.AddHealthChecks()
    // ... checks ...
    .ForwardToPrometheus();

app.UseHealthChecksPrometheusExporter("/metrics/health",
    options => options.ResultStatusCodes[HealthStatus.Unhealthy] = 503);

Alerte Grafana alerting rule :

# Alerte si un check est Unhealthy depuis > 1 min
health_check_status{status="Unhealthy"} == 1

Garde-fous / Anti-patterns


Bonnes pratiques 2026