Test di carico come servizio CI per gli sviluppatori

Test di carico come servizio CI per gli sviluppatori

Uno dei problemi con cui si trovano spesso a fare i conti i fornitori di software multi-prodotto è la duplicazione delle competenze degli ingegneri — sviluppatori, tester e amministratori di infrastrutture — in quasi ogni team. Questo vale anche per gli ingegneri costosi — specialisti nel campo del testing di carico.

Invece di concentrarsi sui propri compiti diretti e utilizzare la propria esperienza unica per costruire il processo di testing di carico, scegliere metodologie, valori ottimali delle metriche e scrivere test automatici in base ai profili di carico, spesso gli ingegneri si trovano a dover sviluppare da zero l'infrastruttura di test, configurare gli strumenti di carico, integrarli nei sistemi CI, impostare il monitoraggio e pubblicare report.

Le soluzioni di alcuni problemi organizzativi nel testing, che applichiamo in Positive Technologies, le puoi trovare in un altro articolo. In questo articolo parlerò della possibilità di integrare i test di carico nel pipeline CI generale attraverso il concetto di «load testing as a service» (testing di carico come servizio). Scoprirai come e quali immagini Docker dei generatori di carico possono essere utilizzate nel pipeline CI; come collegare i generatori di carico al tuo progetto CI mediante un modello di build; come appare un demo pipeline per avviare i test di carico e pubblicare i risultati. L'articolo può essere utile per gli ingegneri nel testing del software e per gli ingegneri di automazione in CI che si stanno interrogando sull'architettura del proprio sistema di carico.

Il concetto fondamentale

Il concetto di load testing as a service prevede la possibilità di integrare gli strumenti di carico Apache JMeter, Yandex.Tank e framework propri in qualsiasi sistema di integrazione continua. L'esempio dimostrativo sarà per GitLab CI, ma i principi esposti sono generali e valgono per tutti i sistemi CI.

Load testing as a service è un servizio centralizzato per l'esecuzione di test di carico. I test di carico vengono eseguiti in pool di agenti dedicati, e la pubblicazione dei risultati avviene automaticamente in GitLab Pages, Influx DB e Grafana o in sistemi di reportistica (TestRail, ReportPortal, ecc.). L'automazione e la scalabilità sono realizzate in modo molto semplice, attraverso l'aggiunta e la parametrizzazione in un progetto GitLab CI di un normale modello gitlab-ci.yml.

Il vantaggio di questo approccio risiede nel fatto che l'intera infrastruttura CI, gli agenti di carico, le immagini Docker delle fonti di carico, i pipeline di testing e la pubblicazione dei report sono supportati dalle forze del dipartimento centralizzato di automazione (ingegneri DevOps), e gli ingegneri di test di carico possono concentrarsi sullo sviluppo dei test e sull'analisi dei loro risultati, senza doversi occupare di questioni infrastrutturali.

Per semplificare la descrizione, considereremo che l'applicazione o il server di destinazione da testare siano già stati distribuiti e configurati in anticipo (per questo possono essere utilizzati script automatizzati in Python, SaltStack, Ansible, ecc.). Pertanto, l'intera concezione del testing di carico come servizio si articola in tre fasi: preparazione, testing, pubblicazione dei report. Ulteriori dettagli sono forniti nello schema (tutte le immagini sono cliccabili):

Test di carico come servizio CI per gli sviluppatori

Concetti e definizioni fondamentali nel testing di carico

Nel condurre test di carico, cerchiamo di aderire agli standard e alla metodologia ISTQB, utilizzando la terminologia appropriata e le metriche raccomandate. Ecco un breve elenco dei concetti e delle definizioni fondamentali nel testing di carico.

Agente di carico (load agent) — una macchina virtuale su cui verrà eseguita l'applicazione — fonte di carico (Apache JMeter, Yandex.Tank o un modulo di carico personalizzato).

Obiettivo del test (target) — server o applicazione installata su un server, che sarà soggetta a carico.

Scenario di test (test case) — un insieme di passaggi parametrizzati: azioni degli utenti e reazioni attese a queste azioni, con richieste e risposte di rete registrate, a seconda dei parametri specificati.

Profilo o piano di carico (profile) — in metodologia ISTQB (p. 4.2.4, p. 43) i profili di carico definiscono le metriche critiche per il test specifico e le variazioni dei parametri di carico durante il test. Esempi di profili possono essere visti nell'immagine.

Test di carico come servizio CI per gli sviluppatori

Test (test) — scenario con un insieme di parametri predefiniti.

Piano di test (test-plan) — insieme di test e profilo di carico.

Espletamento del test (testrun) — un'iterazione dell'esecuzione di un test con uno scenario di carico completamente svolto e un report ottenuto.

Richiesta di rete (request) — richiesta HTTP inviata dall'agente all'obiettivo.

Risposta di rete (response) — Risposta HTTP inviata dalla destinazione all'agente.
Il codice di stato della risposta HTTP (HTTP responses status) è un codice standard di risposta dal server delle applicazioni.
La transazione (transaction) è il ciclo completo "richiesta - risposta". Una transazione è considerata a partire dall'invio della richiesta (request) fino al completamento della ricezione della risposta (response).

Stato della transazione (transactions status) — se è stato possibile completare con successo il ciclo "richiesta - risposta". Se si è verificato un errore in questo ciclo, l'intera transazione è considerata non riuscita.

Tempo di risposta (latency) — tempo dalla fine dell'invio della richiesta (request) all'inizio della ricezione della risposta (response).

Metriche di carico (metrics) — caratteristiche del servizio sotto carico e dell'agente di carico definite durante il processo di testing.

Metriche principali per misurare i parametri di carico

Alcune delle più comuni e raccomandate nella metodologia ISTQB (p. 36, 52) le metriche sono riportate nella tabella sottostante. Metriche simili per l'agente e la destinazione sono indicate nella stessa riga.

Metriche per l'agente di carico
Metriche del sistema o applicazione target in fase di testing sotto carico

Numero  vCPU e memoria RAM,
Disco — caratteristiche "hardware" dell'agente di carico
CPU, Uso della memoria, del disco — dinamica del carico della CPU, della memoria e del disco
durante il testing. Di solito misurato in percentuale rispetto a
i valori massimi disponibili

Larghezza di banda di rete (sull'agente di carico) — larghezza di banda
dell'interfaccia di rete sul server,
dove è installato l'agente di carico.
Di solito misurato in byte al secondo (bps)
Larghezza di banda di rete(sulla destinazione) — larghezza di banda dell'interfaccia di rete
sul server target. Di solito misurato in byte al secondo (bps)

Utenti virtuali— numero di utenti virtuali,
che eseguono scenari di carico e
imitano azioni reali degli utenti
Stato degli utenti virtuali, Passati/Falliti/Totali — numero degli stati di lavoro degli utenti virtuali che hanno avuto successo e non,
così come il loro numero totale.
Di solito ci si aspetta che tutti gli utenti siano in grado di completare

tutte le loro attività indicate nel profilo di carico.
Qualsiasi errore indicherebbe che anche un utente reale non riuscirebbe
a completare il proprio compito utilizzando il sistema.
Richieste al secondo (minuto)

— numero di richieste di rete al secondo (o al minuto).Un'importante caratteristica dell'agente di carico: quante richieste può generare.

Caratteristica importante dell'agente di carico: quante richieste può generare.
In effetti, si tratta di un'imitazione delle interazioni con l'applicazione da parte di utenti virtuali
Risposte al secondo (minuto)
— numero di risposte di rete al secondo (o al minuto).

Una caratteristica importante del servizio target: quanto è riuscito
a generare e inviare risposte alle richieste da
un agente di carico

Stato delle risposte HTTP— numero di diversi codici di risposta
dal server dell'applicazione, ricevuti dall'agente di carico.
Ad esempio, 200 OK indica un'interazione riuscita,
mentre 404 significa che la risorsa non è stata trovata

Latenza (tempo di risposta) — tempo dalla fine
dell'invio della richiesta (request) all'inizio della ricezione della risposta (response).
Di solito è misurato in millisecondi (ms)

Tempo di risposta della transazione— tempo di una transazione completa,
fine del ciclo "richiesta — risposta".
Questo tempo va dall'inizio dell'invio della richiesta (request)
alla fine della ricezione della risposta (response).

Il tempo di transazione può essere misurato in secondi (o minuti)
in vari modi: si considerano i valori minimo,
massimo, medio e, ad esempio, il 90° percentile.
Le letture minima e massima rappresentano gli estremi
delle prestazioni del sistema.
Il novantesimo percentile viene utilizzato più frequentemente,
poiché mostra la maggior parte degli utenti,
che lavorano comodamente al limite delle prestazioni del sistema

Transazioni al secondo (minuto) — numero totale di
transazioni al secondo (minuto),
ossia quanto è riuscita ad accettare e
elaborare richieste e fornire risposte.
In effetti, si tratta della capacità del sistema

Stato delle transazioni , Passato / Fallito / Totale — numero di
transazioni riuscite, non riuscite e totale.

Per gli utenti reali, una transazione non riuscita
significherebbe realmente
l'impossibilità di lavorare con il sistema sotto carico

Schema generico del test di carico

Lo schema generico del test di carico è molto semplice e si compone di tre fasi principali, di cui ho già parlato: Preparare — Testare — Rapportare, ovvero preparazione degli obiettivi del test e impostazione dei parametri per le fonti di carico, quindi esecuzione dei test di carico e, alla fine, creazione e pubblicazione del rapporto sul test.

Test di carico come servizio CI per gli sviluppatori

Annotazioni allo schema:

  • QA.Tester — esperto in test di carico,
  • Target — applicazione target, per la quale è necessario conoscere il suo comportamento sotto pressione.

Classificatore di entità, fasi e passi nello schema

Fasi e passi
Cosa succede
Cosa in ingresso
Cosa in uscita

Prepare: fase di preparazione al test

LoadParameters
Compito e inizializzazione
da parte dell'utente
parametri di carico,
scelta delle metriche e
preparazione del piano di test
(profilo di carico)
Parametri utente per
l'inizializzazione dell'agente di carico
Piano di test
Obiettivo del test

VM
Dispositivo nel cloud
macchina virtuale con
caratteristiche richieste
Parametri della VM per l'agente di carico
Script di automazione per
creazione della VM
VM configurata nel
cloud

Env
Configurazione del sistema operativo e preparazione
dell'ambiente per
lavorare con l'agente di carico
Parametri dell'ambiente per
l'agente di carico
Script di automazione per
configurazione dell'ambiente
Ambiente preparato:
Sistema operativo, servizi e applicazioni,
necessari per l'operatività
l'agente di carico

LoadAgents
Installazione, configurazione e parametrizzazione
dell'agente di carico.
Oppure download dell'immagine Docker con
sorgente di carico preconfigurata
Immagine Docker della sorgente di carico
(JMeter, JM o framework personalizzato)
Parametri di configurazione
l'agente di carico
Agente carico configurato e pronto
per l'operatività

Test: fase di esecuzione dei test di carico. Le fonti sono agenti di carico, distribuiti in pool dedicati di agenti per GitLab CI

Carico
Avvio dell'agente di carico
con il piano di test selezionato
e i parametri di carico
Parametri utente
per l'inizializzazione
l'agente di carico
Piano di test
Obiettivo del test
Log di esecuzione
dei test di carico
Registri di sistema
Dinamica della variazione delle metriche dell'obiettivo e dell'agente di carico

RunAgents
Esecuzione da parte dell'agente
di scenari di test di carico
in conformità con
profilo di carico
Interazione dell'agente di carico
con l'obiettivo del test
Piano di test
Obiettivo del test

Log
Raccolta di log 'grezzi'
durante i test di carico:
registrazioni delle azioni dell'agente di carico,
stato dell'obiettivo del test
e della VM su cui è in esecuzione l'agente

Log di esecuzione
dei test di carico
Registri di sistema

Metrics
Raccolta di metriche 'grezze' durante il test

Dinamica della variazione delle metriche dell'obiettivo
e dell'agente di carico

Report: fase di preparazione del report di testing

Generator
Elaborazione delle metriche e dei log 'grezzi' raccolti
dal sistema di carico e
dal sistema di monitoraggio
Formazione del report in
forma leggibile da un essere umano,
possibilmente con elementi
di analisi
Dinamica della variazione delle metriche
Log di esecuzione
dei test di carico
Registri di sistema
dell'obiettivo e dell'agente di carico
Log 'grezzi' elaborati
in un formato idoneo per
l'esportazione verso archivi esterni
Report statico sul carico,
idoneo per l'analisi da parte di un umano
Pubblica

Pubblicazione del report
sul test di carico in un servizio esterno
sul testing di carico
in un servizio esterno
servizio
Loghi "grezzi" elaborati
in un formato adatto
per l'esportazione verso esterni
archivio
Rapporti salvati in esterna
archiviazione sulle
cariche, adatti
per l'analisi umana

Collegamento delle fonti di carico nel template CI

Passiamo alla parte pratica. Voglio mostrare come in alcuni progetti della compagnia Positive Technologies abbiamo implementato il concetto di testing delle prestazioni come servizio.

Inizialmente, con i nostri ingegneri DevOps, abbiamo creato in GitLab CI un pool dedicato di agenti per eseguire test di carico. Per non confonderli nei template con altri, ad esempio quelli di build, abbiamo aggiunto etichette a questi agenti, tags: load. È possibile utilizzare altre etichette comprensibili. Queste vengono impostate durante la registrazione GitLab CI Runners.

Come determinare la potenza richiesta in termini di "hardware"? Le caratteristiche degli agenti di carico — numero sufficiente di vCPU, RAM e Disco — possono essere calcolate considerando che sull'agente dovrebbero essere in esecuzione Docker, Python (per Yandex.Tank), l'agente GitLab CI, Java (per Apache JMeter). Per Java con JMeter si raccomanda inoltre di utilizzare almeno 512 MB di RAM e, come limite superiore, 80% della memoria disponibile.

Pertanto, sulla base della nostra esperienza, raccomandiamo di utilizzare per gli agenti di carico almeno: 4 vCPU, 4 GB di RAM, 60 GB di SSD. La larghezza di banda della scheda di rete è determinata in base ai requisiti del profilo di carico.

Noi utilizziamo principalmente due fonti di carico — immagini Docker di Apache JMeter e Yandex.Tank.

Yandex.Tank è uno strumento open source di Yandex per il test delle prestazioni. Alla base della sua architettura modulare c'è un generatore HTTP asincrono ad alte prestazioni e basato su hit, Phantom. Il Tank ha monitoraggio integrato delle risorse del server testato tramite il protocollo SSH, può interrompere automaticamente il test in base a condizioni specifiche, è in grado di visualizzare i risultati sia nella console che come grafici, e si possono collegare moduli personalizzati per ampliare le funzionalità. A proposito, abbiamo utilizzato il Tank quando non era ancora di moda. Nell'articolo "Yandex.Tank e l'automazione del testing delle prestazioni" è possibile leggere la storia di come nel 2013 abbiamo condotto test di carico utilizzando PT Application Firewall — uno dei prodotti della nostra azienda.

Apache JMeter — è uno strumento open source per eseguire test di carico sviluppato da Apache. Può essere utilizzato efficacemente per testare sia applicazioni web statiche che dinamiche. JMeter supporta un'ampia varietà di protocolli e metodi di interazione con le applicazioni: HTTP, HTTPS (Java, NodeJS, PHP, ASP.NET, ecc.), SOAP / REST Webservices, FTP, TCP, LDAP, SMTP(S), POP3(S) e IMAP(S), database tramite JDBC, può eseguire comandi shell e lavorare con oggetti Java. JMeter dispone di un IDE per la creazione, il debug e l'esecuzione di piani di test. Offre anche una CLI per lavorare nella riga di comando di qualsiasi OS compatibile con Java (Linux, Windows, Mac OS X). Lo strumento è in grado di generare dinamicamente un rapporto HTML sui test effettuati.

Per facilitare l'uso all'interno della nostra azienda, permettendo ai tester di modificare e aggiungere l'ambiente, abbiamo creato build di immagini Docker dei carichi di lavoro su GitLab CI con pubblicazione nel nostro registry Docker su Artifactory. In questo modo risulta più veloce e semplice collegarli nei pipeline per i test di carico. Come eseguire un docker push nel registry tramite GitLab CI — consulta il guida.

File docker di base per Yandex.Tank, lo abbiamo preso qui:

Dockerfile 
1 | FROM direvius/yandex-tank
2 | ENTRYPOINT [""]

E per Apache JMeter questo:

Dockerfile 
1 | FROM vmarrazzo/jmeter
2 | ENTRYPOINT [""]

Puoi leggere come è strutturato il nostro sistema di integrazione continua nell'articolo “Automazione dei processi di sviluppo: come abbiamo implementato le idee DevOps in Positive Technologies».

Modello e pipeline

Un esempio di modello per eseguire test di carico è disponibile nel progetto demo-load. In nel file readme puoi trovare le istruzioni per l'uso del modello. Nel modello stesso (file .gitlab-ci.yml) sono presenti note su cosa risponde ciascun passaggio.

Il modello è molto semplice e dimostra tre fasi di test di carico, descritte in precedenza nello schema: preparazione, test e pubblicazione dei rapporti. Queste sono gestite da stages: Prepare, Test e Report.

  1. Fase Prepare dovrebbe essere utilizzata per la configurazione preliminare degli obiettivi di test o per verificarne la disponibilità. Non è necessario configurare l'ambiente per i carichi di lavoro, poiché sono già stati creati come immagini Docker e resi disponibili nel registry Docker: è sufficiente specificare la versione desiderata nella fase Test. Ma è possibile ricompilarli e creare le proprie immagini modificate.
  2. Fase Test viene utilizzato per specificare la fonte di carico, avviare i test e salvare gli artefatti di test. È possibile scegliere qualsiasi fonte di carico: Yandex.Tank, Apache JMeter, la propria o tutte insieme. Per disabilitare le fonti non necessarie, è sufficiente commentare o rimuovere il job. Punti di ingresso per le fonti di carico:

    Nota: il modello di configurazione di build è utilizzato per configurare l'interazione con il sistema CI e non prevede l'inserimento della logica dei test. Per i test viene specificato un punto di ingresso, dove si trova lo script bash di controllo. Il modo in cui vengono avviati i test, la generazione dei report e gli stessi scenari di test devono essere implementati dagli ingegneri QA. Nell'esempio dimostrativo, per entrambe le fonti di carico viene utilizzato come test più semplice il request della pagina principale di Yandex. Gli scenari e i parametri dei test si trovano nella cartella ./tests.

  3. Nella fase Report è necessario descrivere i modi di pubblicazione dei risultati dei test ottenuti nella fase Test, su archivi esterni, ad esempio in GitLab Pages o in sistemi di reporting specializzati. Per GitLab Pages è necessario che, al termine dei test, la cartella ./public sia non vuota e contenga almeno il file index.html. Puoi leggere ulteriori dettagli sul funzionamento del servizio GitLab Pages al link.

    Esempi di come esportare i dati:

    Istruzioni per configurare la pubblicazione:

Nell'esempio dimostrativo, la pipeline con test di carico e due fonti di carico (una può essere disabilitata) appare così:

Test di carico come servizio CI per gli sviluppatori

Apache JMeter è in grado di generare autonomamente un report HTML, quindi è più vantaggioso salvarlo in GitLab Pages con i mezzi standard. Ecco come appare il report di Apache JMeter:

Test di carico come servizio CI per gli sviluppatori

Nell'esempio dimostrativo per Yandex.Tank vedrete solo un report testuale fittizio nella sezione per GitLab Pages. Durante il processo di test, Tank è in grado di salvare i risultati in un database InfluxDB, e da lì possono essere visualizzati, ad esempio, in Grafana (la configurazione viene eseguita nel file ./tests/example-yandextank-test.yml). Ecco come appare il report di Tank in Grafana:

Test di carico come servizio CI per gli sviluppatori

Riepilogo

Nell'articolo ho parlato del concetto di "load testing as a service". L'idea principale consiste nell'utilizzare un'infrastruttura di pool di agenti di carico preconfigurati, immagini Docker delle fonti di carico, sistemi di reportistica e una pipeline unificante in GitLab CI basata su un semplice template .gitlab-ci.yml (esempio al link). Tutto ciò è supportato da un piccolo team di ingegneri automatizzatori e viene replicato su richiesta dei team di prodotto. Spero che questo vi aiuti nella preparazione e implementazione di uno schema simile nella vostra azienda. Grazie per l'attenzione!

P. S. Vorrei ringraziare di cuore i miei colleghi, Sergey Kurbanov e Nikolay Yusev, per l'aiuto tecnico nell'implementazione del concetto di load testing as a service nella nostra azienda.

Autore: Timur Gilmullin — vicedirettore del dipartimento di tecnologie e processi di sviluppo (DevOps) presso Positive Technologies

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster