facebook

Blog

Resta aggiornato

Scopriamo quanto sono efficienti le nostre applicazioni
Load Test con Azure App Service
mercoledì 28 Ottobre 2020

Negli ultimi mesi abbiamo aiutato diversi clienti a migrare da una infrastruttura on-premise a Microsoft Azure. In molti ci hanno fatto la seguente domanda: qual è il carico che l’attuale configurazione regge?

Naturalmente, dare una risposta a questa domanda non è sempre semplice, soprattutto perché dipende da diversi fattori. Un classico esempio può essere il modo in cui è scritta l’applicazione, o come sono gestite le connessioni, come viene usata la memoria o persino a quante risorse esterne fa riferimento.

Una risposta con maggiore fondamento la si può avere eseguendo dei performance test: questa tipologia di test tenta di verificare come la nostra applicazione si comporta in base al carico di utenti che la utilizza.

I performance test si dividono in diverse tipologie, e tra le più comuni vi sono i load e gli stress test. La prima tipologia ha l’obiettivo di raggiungere il limite di carico della nostra applicazione, simulando un alto numero di utenti e determinando la sua capacità operativa. La seconda invece, identifica la stabilità della nostra applicazione verificando come si comporta durante e dopo un forte carico.

Introdotto l’argomento, occorre capire come effettuare questa tipologia di test dal punto di vista tecnico: esistono oggi tanti framework e servizi che consentono di raggiungere questo scopo.

Personalmente, ne ho utilizzati diversi e tra questi figurano:

  • Web Application Load and Performance Testing
  • Cloud-Based Load Testing da Azure DevOps o Azure App Service
  • Apache JMeter
  • BlazeMeter
  • Artillery

Per alcuni nostri utenti, abbiamo utilizzato i Web Application Load and Performance Testing di Visual Studio. Questo framework è al momento deprecato, infatti Visual Studio 2019 sarà l’ultima versione a supportarlo ancora. Nonostante tutto però si tratta di un framework ben fatto, che ha un buon grado di configurazione, un tool per la registrazione delle richieste web e persino il supporto alla configurazione di un test rig per i propri test.

Per identificare i problemi nell’applicazione di esempio, faremo uso di alcune metriche disponibili sia come risultato dei load test, che all’interno delle Azure App Service. Tra queste, ci sono alcune fondamentali da dover sempre considerare:

  • Time to First Byte (FTTB): rappresenta il tempo di attesa del client per recepire il primo byte di contenuto dal server. Questo include il tempo di connessione al socket, il tempo per inviare la richiesta HTTP e il tempo per ricevere il primo byte;
  • Page Response Time: rappresenta il tempo di attesa del client per recepire l’intera risposta (comprensiva di tutte le risorse) da parte del server. Questa metrica include anche il FTTB;
  • Numero di errori e warning: verificare il numero messaggi di warning ed errori durante la fase di testing. Ad esempio quando usiamo un Azure App Service, dovremmo osservare il numero di errori legati a 503 Service Unavailable o SocketException.

In questo articolo dimostreremo:

  • Come preparare una Azure App Service per i test;
  • Come registrare gli scenari;
  • Come configurare un test rig per l’esecuzione degli scenari;
  • Come identificare e risolvere i bottlenecks.

L’applicazione di esempio è scaricabile da qui ed è ospitata in una Azure App Service associata ad un piano S1.

Per effettuare un load test, abbiamo bisogno di una serie di scenari che possono corrispondere alle pagine del sito maggiormente sollecitate dagli utenti. Tramite Visual Studio 2019, possiamo utilizzare un nuovo progetto di tipo Web Performance and Load Test Project [Deprecated]. Come si evince dal nome questa tipologia di progetto è stata deprecata e pertanto non è presente tra i template disponibili nel wizard di creazione. Occorre infatti utilizzare il Visual Studio Installer per abilitare il componente come nella figura sottostante:

Creiamo il progetto usando come template quello elencato sopra e al termine avremo già un primo web performance test pronto per essere registrato. Cliccando il pulsante “Add Recording”, si aprirà il browser con il “Web Test Recorder” e tutte le richieste che facciamo saranno tracciate. Una volta terminato lo scenario, premere il pulsante “Stop”.

Come prossimo step occorre creare il load test. Fare click con il pulsante destro del mouse sul progetto di performance test nel solution explorer e aggiungere un nuovo elemento di tipo “Load Test”. Una volta creato, appare il wizard di configurazione che si suddivide nei seguenti passi:

  • Tipologia di esecuzione: le scelte possibili sono “Cloud-based Load Test with Azure DevOps” (non più disponibile) o “On-premises Load Test”. In questo caso occorre scegliere la seconda;
  • Lunghezza del test: durata temporale oppure numero di iterazioni, in questo caso dipende molto da quanto vogliamo stressare l’applicazione. Generalmente un test della durata di 10 minuti è sufficiente per riscontrare dei colli di bottiglia;
  • Nome dello scenario e think time (per simulare l’interazione utente);
  • Pattern di carico: costante o graduale;
  • Modalità di esecuzione dei test (se più di uno), si può scegliere come miscelare i test fra loro;
  • Elenco dei web performance test da eseguire;
  • Condizioni della rete (LAN, rete cellulare);
  • Browser su cui simulare i test;
  • Macchine su cui raccogliere le metriche.

Una volta impostate queste informazioni, siamo pronti per eseguire il nostro test. Il problema che però adesso sorge è chi dovrebbe eseguirlo. Volendo, possiamo lanciare il test da Visual Studio ed eseguirlo sulla macchina corrente ma questo sarebbe uno scenario poco realistico perché le chiamate sono generate da un’unica sorgente. Inoltre, se il numero di richieste da dover eseguire è molto alto, questo porterebbe la nostra macchina ai limiti imposti dall’hardware.

Quello che invece si può utilizzare è un test rig, ovvero un insieme di macchine collegate fra loro il cui obiettivo è distribuire il carico delle richieste da effettuare.

Usando gli strumenti messi a disposizione da Microsoft, creare un test rig è abbastanza semplice. Basta infatti installare su una macchina un Test Controller che gestisce uno o più Test Agent. Generalmente, non esiste un numero preciso di quanti test agent occorrano, molto dipende dal carico che ci serve.

La topologia di un test rig può essere simile alla seguente immagine:

Per installare un test controller o un test agent, si può utilizzare questo eseguibile scegliendo cosa occorre installare. Per il controller serve anche un’ istanza di SQL Server per memorizzare tutte le informazioni collegate ai test. Una volta installate le componenti, è possibile metterle in comunicazione registrando gli agent al controller.

Nel mio caso, ho cinque macchine virtuali create su Microsoft Azure. La principale ha installato sia Visual Studio che il Test Controller, le altre sono configurate come Test Agent. L’ambiente può essere poi collegato al load test creato precedentemente tramite il pulsante di configurazione del Test Controller.

Siamo pronti per far partire la nostra prima esecuzione.

Esecuzione di un primo test di carico

Per eseguire il test di carico, basta utilizzare il tasto “Run Load Test”. Nel mio caso, ho configurato il test per essere eseguito per 10 minuti utilizzando un carico di iniziale di 100 utenti che aumentano ogni 5 secondi fino a raggiungere 4000 utenti concorrenti.

All’avvio, Visual Studio apre una pagina di monitoraggio in tempo reale dei test, con tutti i performance counter che ci aiutano a identificare l’utilizzo delle risorse. La prossima immagine riepiloga l’andamento delle richieste nell’arco dei 10 minuti. È interessante notare come il Page Response Time aumenti con il progredire dei test.

Una volta completato il test, trovo circa 1000 errori (limite configurabile) con 20391 test falliti su un totale di 23482. Il numero di richieste totali fallite è di 146725. Il tutto viene dettagliato nella sezione Tables e in quella Summary:

Da notare che le richieste fallite ricevono un errore HTTP di tipo 503 – ServiceUnavailable, che identifica il superamento della quota di utilizzo del mio App Service Plan. Ho quindi trovato un limite di chiamate con l’attuale configurazione. Cosa posso fare a tal punto?

Dando un’occhiata alle metriche dell’App Service notiamo anche qui un alto numero di chiamate:

Poiché il fallimento delle richieste ha il codice HTTP 503, sappiamo che andando ad aumentare il numero di istanze della nostra App Service possiamo sicuramente migliorare la situazione.

Nella sezione di Scale-Out nell’App Service,possiamo, ad esempio, incrementare il numero di istanze:

Siamo pronti per rieseguire il test e verificare gli effetti di questa nuova configurazione.

Secondo test di carico

Lanciamo quindi un secondo test e verifichiamo cosa accade. In questo caso, siamo fortunati perché la situazione è nettamente migliorata facendo fallire solo un test con un errore HTTP 500. Ma nello stesso tempo, abbiamo eseguito un numero più alto di richieste con un numero più alto di test, proprio perché i tempi di risposta si sono accorciati. Il numero totale di test è di 54255.

Naturalmente, questo è un caso molto semplice di individuazione del problema. Ci sono scenari con maggiori impatti sulle performance che richiedono maggiori investigazioni utilizzando dei memory profiler come ANTS Profiler.

Le App Service integrano inoltre una serie di utility interessanti per poter investigare problemi di performance. Ad esempio, è possibile raccogliere un dump della memoria per investigare poi con PerfView un possibile memory leak.

Infine, il processo di identificazione dei problemi di performance è un processo continuo in cui occorre monitorare sia in maniera proattiva che reattiva le proprie applicazioni.

In questo articolo, abbiamo visto come poter identificare problemi di performance nelle nostre applicazioni usando i tool messi a disposizione da Microsoft. La soluzione adottata per migliorare le performance dell’applicazione di esempio, ovvero lo scaling delle istanze, è in genere la prima soluzione che si adotta per fronteggiare emergenze. Nel caso in cui i problemi di performance persistono, occorre investigare più in dettaglio come l’applicazione gestisce le risorse. Ma esamineremo questo aspetto in un futuro articolo.