Condividi tramite


Diagnosi dei proxy basati su YARP

Quando si utilizza un proxy inverso, c'è un hop aggiuntivo dal client al proxy e quindi dal proxy alla destinazione, dove possono verificarsi problemi. Questo argomento contiene alcuni suggerimenti e suggerimenti su come eseguire il debug e diagnosticare i problemi quando si verificano. Si presuppone che il proxy sia già in esecuzione e quindi non includa problemi all'avvio, ad esempio errori di configurazione.

Registrazione

Il primo passaggio per sapere cosa sta succedendo con YARP consiste nell'attivare la registrazione. Si tratta di un flag di configurazione che può essere modificato in tempo reale. YARP viene implementato come componente middleware per ASP.NET Core, quindi è necessario abilitare la registrazione sia per YARP che per ASP.NET per ottenere l'immagine completa di ciò che sta succedendo.

Per impostazione predefinita, ASP.NET accederà alla console e il file di configurazione può essere usato per controllare il livello di registrazione.

  //Sets the Logging level for ASP.NET
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      // Uncomment to hide diagnostic messages from runtime and proxy
      // "Microsoft": "Warning",
      // "Yarp" : "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },

Si desiderano informazioni di registro dai provider Microsoft.AspNetCore.\* e Yarp.ReverseProxy.\*. L'esempio precedente emette eventi di livello Information da entrambi i provider alla console. Cambiare il livello a Debug mostra voci aggiuntive. ASP.NET implementa il rilevamento delle modifiche per i file di configurazione, in modo da poter modificare il appsettings.json file (o appsettings.development.json per l'ambiente Development ) mentre il progetto è in esecuzione e osservare le modifiche apportate all'output del log.

Nota

Le impostazioni nel file appsettings.development.json sostituiscono quelle nel file appsettings.json quando si esegue nell'ambiente Development, quindi assicurarsi che, durante la modifica di appsettings.json, i valori non vengano sovrascritti.

Informazioni sulle voci di log

L'output di registrazione è direttamente associato al modo in cui ASP.NET Core elabora le richieste. È importante tenere presente che, come middleware, YARP si basa su gran parte delle funzionalità di ASP.NET per elaborare le richieste, ad esempio quanto segue per l'elaborazione di una richiesta con la modalità "Debug" abilitata:

Livello Registra messaggio Descrizione
dbug Microsoft.AspNetCore.Server.Kestrel. Connessioni[39]
ID di connessione "0HMCD0JK7K51U" è stato accettato.
Le connessioni sono indipendenti da richieste, quindi si tratta di una nuova connessione
dbug Microsoft.AspNetCore.Server.Kestrel. Connessioni[1]
L'ID connessione "0HMCD0JK7K51U" è stato avviato.
Informazioni Microsoft.AspNetCore.Hosting.Diagnostics[1]
Richiesta avviata HTTP/1.1 GET http://localhost:5000/ - -
Si tratta della richiesta in ingresso per ASP.NET
dbug Microsoft.AspNetCore.HostFiltering.HostFilteringMiddleware[0]
Rilevato carattere generico, tutte le richieste che contengono host saranno consentite.
La configurazione non collega gli endpoint a nomi host specifici
dbug Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1001]
1 candidato trovato per il percorso della richiesta '/'
Ciò mostra le possibili corrispondenze per la route
dbug Microsoft.AspNetCore.Routing.Matching.DfaMatcher[1005]
L'endpoint 'minimumroute' con il modello di percorso '{**catch-all}' è valido per il percorso della richiesta '/'
La route minima dalla configurazione YARPs è stata abbinata
dbug Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware[1]
Richiesta abbinata all'endpoint 'minimumroute'
Informazioni Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Esecuzione dell'endpoint "minimumroute"
Informazioni Yarp.ReverseProxy.Forwarder.HttpForwarder[9]
Proxy verso http://www.example.com/
YARP gestisce la richiesta tramite proxy a example.com
Informazioni Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Eseguito l'endpoint 'minimumroute'
dbug Microsoft.AspNetCore.Server.Kestrel. Connessioni[9]
L'identificativo di connessione "0HMCD0JK7K51U" ha completato la risposta di keep-alive.
La risposta è stata completata, ma la connessione può essere mantenuta attiva.
Informazioni Microsoft.AspNetCore.Hosting.Diagnostics[2]
Richiesta completata HTTP/1.1 GET http://localhost:5000/ - - 200 1256 text/html;+charset=utf-8 12.7797ms
La risposta è stata completata con il codice di stato 200, rispondendo con 1256 byte come testo/html in ~13 ms.
dbug Microsoft.AspNetCore.Server.Kestrel. Transport.Sockets[6]
L'ID di connessione "0HMCD0JK7K51U" ha ricevuto FIN.
Informazioni di diagnostica sulla connessione per determinare chi l'ha chiusa e quanto pulitamente.
dbug Microsoft.AspNetCore.Server.Kestrel. Connessioni[10]
Il collegamento con l'ID "0HMCD0JK7K51U" si sta disconnettendo.
dbug Microsoft.AspNetCore.Server.Kestrel. Connessioni[2]
ID connessione "0HMCD0JK7K51U" interrotto.
dbug Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets[7]
ID connessione "0HMCD0JK7K51U" invia FIN perché: "Il ciclo di invio del trasporto Socket è stato completato senza problemi".

Il codice precedente fornisce informazioni generali sulla richiesta e sulla modalità di elaborazione.

Uso della registrazione delle richieste di ASP.NET

ASP.NET include un componente middleware che può essere usato per fornire altri dettagli sulla richiesta e sulla risposta. Il componente UseHttpLogging può essere aggiunto alla pipeline di richiesta, fornendo voci aggiuntive dettagliate al log relative alle intestazioni delle richieste in entrata e in uscita.

app.UseHttpLogging();
// Enable endpoint routing, required for the reverse proxy
app.UseRouting();
// Register the reverse proxy routes
app.MapReverseProxy();

Per esempio:

info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1]
      Request:
      Protocol: HTTP/1.1
      Method: GET
      Scheme: http
      PathBase:
      Path: /
      Accept: */*
      Host: localhost:5000
      User-Agent: curl/7.55.1
info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[2]
      Response:
      StatusCode: 200
      Content-Type: text/html; charset=utf-8
      Date: Tue, 12 Oct 2021 23:29:20 GMT
      Server: ECS,(sec/97A5)
      Age: 113258
      Cache-Control: [Redacted]
      ETag: [Redacted]
      Expires: Tue, 19 Oct 2021 23:29:20 GMT
      Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
      Vary: [Redacted]
      Content-Length: 1256
      X-Cache: [Redacted]

Uso degli eventi di telemetria

È consigliabile leggere Telemetria di rete in .NET come introduzione all'uso dei dati di telemetria di rete in .NET.

L'esempio di metriche mostra come monitorare gli eventi dei diversi fornitori che raccolgono telemetria come parte di YARP. Il più importante dal punto di vista della diagnostica è:

  • ForwarderTelemetryConsumer
  • HttpClientTelemetryConsumer

Per usare uno di questi, creare una classe che implementa un'interfacciaYarp.Telemetry.Consumption, ad esempio IForwarderTelemetryConsumer:

public class ForwarderTelemetry : IForwarderTelemetryConsumer
{
    /// Called before forwarding a request.
    public void OnForwarderStart(DateTime timestamp, string destinationPrefix)
    {
        Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => " +
            $"OnForwarderStart :: Destination prefix: {destinationPrefix}");
    }

    /// Called after forwarding a request.
    public void OnForwarderStop(DateTime timestamp, int statusCode)
    {
        Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => " +
            $"OnForwarderStop :: Status: {statusCode}");
    }

    /// Called before <see cref="OnForwarderStop(DateTime, int)"/> if forwarding the request failed.
    public void OnForwarderFailed(DateTime timestamp, ForwarderError error)
    {
        Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => " +
            $"OnForwarderFailed :: Error: {error.ToString()}");
    }

    /// Called when reaching a given stage of forwarding a request.
    public void OnForwarderStage(DateTime timestamp, ForwarderStage stage)
    {
        Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => " +
            $"OnForwarderStage :: Stage: {stage.ToString()}");
    }

    /// Called periodically while a content transfer is active.
    public void OnContentTransferring(DateTime timestamp, bool isRequest, long contentLength,
        long iops, TimeSpan readTime, TimeSpan writeTime)
    {
        Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => " +
            $"OnContentTransferring :: Is request: {isRequest}, Content length: {contentLength}, " +
            $"IOps: {iops}, Read time: {readTime:s\\.fff}, Write time: {writeTime:s\\.fff}");
    }

    /// Called after transferring the request or response content.
    public void OnContentTransferred(DateTime timestamp, bool isRequest, long contentLength,
        long iops, TimeSpan readTime, TimeSpan writeTime, TimeSpan firstReadTime)
    {
        Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => " +
            $"OnContentTransferred :: Is request: {isRequest}, Content length: {contentLength}, " +
            $"IOps: {iops}, Read time: {readTime:s\\.fff}, Write time: {writeTime:s\\.fff}");
    }

    /// Called before forwarding a request from `ForwarderMiddleware`, therefore is not called for direct forwarding scenarios.
    public void OnForwarderInvoke(DateTime timestamp, string clusterId, string routeId,
        string destinationId)
    {
        Console.WriteLine($"Forwarder Telemetry [{timestamp:HH:mm:ss.fff}] => " +
            $"OnForwarderInvoke:: Cluster id: {clusterId}, Route Id: {routeId}, Destination: {destinationId}");
    }
}

Registrare la classe come parte dei servizi, ad esempio:

services.AddTelemetryConsumer<ForwarderTelemetry>();

// Add the reverse proxy to capability to the server
var proxyBuilder = services.AddReverseProxy();
// Initialize the reverse proxy from the "ReverseProxy" section of configuration
proxyBuilder.LoadFromConfig(Configuration.GetSection("ReverseProxy"));

I dettagli vengono registrati in ogni parte della richiesta, ad esempio:

Forwarder Telemetry [06:40:48.186] => OnForwarderInvoke::
    Cluster id: minimumcluster, Route Id: minimumroute, Destination: example.com
Forwarder Telemetry [06:41:00.269] => OnForwarderStart ::
    Destination prefix: http://www.example.com/
Forwarder Telemetry [06:41:00.298] => OnForwarderStage :: Stage: SendAsyncStart
Forwarder Telemetry [06:41:00.507] => OnForwarderStage :: Stage: SendAsyncStop
Forwarder Telemetry [06:41:00.530] => OnForwarderStage :: Stage:
    ResponseContentTransferStart
Forwarder Telemetry [06:41:03.655] => OnForwarderStop :: Status: 200

Gli eventi per i dati di telemetria vengono attivati man mano che si verificano, quindi è possibile ottenere HttpContext e la funzionalità YARP da essa:

services.AddTelemetryConsumer<ForwarderTelemetry>();
services.AddHttpContextAccessor();
...
public void OnForwarderInvoke(DateTime timestamp, string clusterId, string routeId,
    string destinationId)
{
    var context = new HttpContextAccessor().HttpContext;
    var YarpFeature = context.GetReverseProxyFeature();

    var dests = from d in YarpFeature.AvailableDestinations
        select d.Model.Config.Address;

    Console.WriteLine($"Destinations: {string.Join(", ", dests)}");
}

Uso di middleware personalizzato

Un altro modo per controllare lo stato per le richieste consiste nell'inserire un middleware aggiuntivo nella pipeline di richiesta. È possibile inserire tra le altre fasi per visualizzare lo stato della richiesta.

// We can customize the proxy pipeline and add/remove/replace steps
app.MapReverseProxy(proxyPipeline =>
{
    // Use a custom proxy middleware, defined below
    proxyPipeline.Use(MyCustomProxyStep);
    // Don't forget to include these two middleware when you make a custom proxy pipeline (if you need them).
    proxyPipeline.UseSessionAffinity();
    proxyPipeline.UseLoadBalancing();
});
...
public Task MyCustomProxyStep(HttpContext context, Func<Task> next)
{
    // Can read data from the request via the context
    foreach (var header in context.Request.Headers)
    {
        Console.WriteLine($"{header.Key}: {header.Value}");
    }

    // The context also stores a ReverseProxyFeature which holds proxy specific data such as the cluster, route and destinations
    var proxyFeature = context.GetReverseProxyFeature();
    Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(proxyFeature.Route.Config));

    // Important - required to move to the next step in the proxy pipeline
    return next();
}

È anche possibile usare ASP.NET middleware all'interno di Configura che consentirà di esaminare la richiesta prima della pipeline proxy.

Nota

Il proxy trasmette la risposta dal server di destinazione al client, quindi le intestazioni e il corpo della risposta non sono facilmente accessibili tramite middleware.

Uso del debugger

Un debugger, ad esempio Visual Studio, può essere collegato al processo proxy. Tuttavia, a meno che non sia presente un middleware esistente, non c'è una buona posizione nel codice dell'app per interrompere e controllare lo stato della richiesta. Di conseguenza, il debugger viene usato in combinazione con una delle tecniche precedenti in modo da avere posizioni distinte per inserire punti di interruzione.

Tracciamento della rete

Può essere interessante usare strumenti di traccia di rete come Fiddler o Wireshark per cercare di monitorare ciò che accade su entrambi i lati del proxy. Tuttavia, prestare attenzione usando entrambi gli strumenti:

  • Fiddler si registra come proxy e si basa sulle app che usano il proxy predefinito per poter monitorare il traffico. Ciò funziona per il traffico in ingresso da un browser a YARP, ma non acquisisce le richieste in uscita, perché YARP è configurato per non usare le impostazioni proxy per il traffico in uscita.
  • In Windows Wireshark usa Npcap per acquisire i dati dei pacchetti per il traffico di rete, in modo da acquisire sia il traffico in ingresso che quello in uscita e può essere usato per monitorare il traffico HTTP.
  • Il traffico HTTPS è crittografato e non è decrittografabile automaticamente dagli strumenti di monitoraggio della rete. Ogni strumento include soluzioni alternative che possono consentire il monitoraggio del traffico, ma richiedono l'uso rischioso di certificati e modifiche alle relazioni di trust. Poiché YARP effettua richieste in uscita, le tecniche per ingannare i browser non si applicano al processo YARP.

La scelta del protocollo per il traffico in uscita viene effettuata in base all'URL di destinazione nella configurazione del cluster. Se il monitoraggio del traffico viene usato per la diagnostica, la modifica degli URL http://in uscita su , se possibile, può essere l'approccio più semplice per consentire il funzionamento degli strumenti di monitoraggio, purché i problemi diagnosticati non siano correlati al protocollo di trasporto.