Lo stato delle librerie SSH Python

In questo post una nuova opzione per le librerie SSH Python, ssh2-python, sarà confrontata con Paramiko per dimostrare le rispettive prestazioni con particolare enfasi sulla concorrenza e sulle richieste non bloccanti.

Nella ricerca di una libreria SSH Python da usare in un’applicazione, non esistono molte opzioni. Infatti, l’unica libreria di uso generale che è stata, fino ad ora, disponibile è Paramiko, che implementa l’API SSH2 nel codice Python.

Nel bene e nel male – è senza dubbio che la libreria ha aiutato un gran numero di persone con esigenze simili, incluso questo autore, poiché è stata storicamente l’unica opzione – Paramiko è stato lo standard de-facto per le librerie SSH Python finora.

Tuttavia, lascia molto a desiderare dal punto di vista delle prestazioni, della stabilità e del consumo di risorse.

Per fortuna, ora c’è un’altra opzione sotto forma di ssh2-python che è basata sulla libreria libssh2 C.

Molte applicazioni di automazione come Ansible e Fabric fanno uso di Paramiko e sarebbero probabilmente interessate a sapere che ora esistono altre opzioni. Speriamo che questo post aiuti queste e altre applicazioni a determinare quale libreria sia più adatta.

Confronto delle prestazioni

ssh2-python è una nuova libreria SSH Python basata sulla libreria libssh2 C. Non ha dipendenze e fornisce ruote binarie per Linux, OSX e Windows con libssh2 incluso.

paramiko è scritto in Python e fa uso di dipendenze di estensione native come la crittografia. Supporta Linux, OSX e Windows e le sue dipendenze native forniscono le ruote binarie.

Impostazione del test

Il test utilizza la threading libreria standard di Python per la concorrenza. Un successivo post sul blog esaminerà le prestazioni non bloccanti nelle librerie attraverso la libreria di co-routine gevent.

Mentre il threading non è un buon modello per scalare la concorrenza dell’I/O di rete, è l’unica modalità di concorrenza che Paramiko supporta nativamente ed è quella utilizzata da applicazioni come Ansible. Altri progetti come parallel-ssh, di cui sono anche autore, usano il monkey patching di gevent per rendere Paramiko cooperativamente concorrente. Anche questo ha degli svantaggi significativi che il client nativamente non bloccante basato su ssh2-python attualmente in sviluppo mira a risolvere. Si veda la richiesta di pull work in progress per maggiori dettagli al riguardo.

Lo script di test crea sessioni SSH in parallelo ad un server SSH (OpenSSH) tramite un dispositivo di loop back (localhost), partendo da uno e aumentando di uno ad ogni iterazione fino al completamento. Questo mostra come le due librerie scalano all’aumentare del numero di sessioni parallele.

Il numero massimo di thread e quindi di sessioni parallele è impostato a 50. Tutti i test sono eseguiti su una CPU quad core fisica.

In tutti i test viene utilizzata l’ultima versione disponibile di ciascuna libreria, 2.2.1 e 0.5.3 rispettivamente per Paramiko e ssh2-python. Per ssh2-python è stato usato un libssh2 incorporato, ultima versione disponibile 1.8.0. Tutti i test sono stati eseguiti sotto Python 2.7.

Le librerie sono confrontate in sei operazioni SSH separate, così come il tempo totale trascorso dall’inizio alla fine per sessione SSH.

Le sei operazioni confrontate sono:

  • Inizializzazione della sessione e autenticazione con l’agente SSH (auth)
  • Canale aperto (channel_open)
  • Canale eseguito (execute)
  • Canale letto – (channel_read)
  • Channel close and get exit status (close_and_exit_status)
  • SFTP read (sftp_read)

Una rapida spiegazione di cosa fanno queste operazioni:

  • Inizializzazione della sessione e autenticazione – esegue l’handshake con il server SSH e si autentica tramite un agente SSH di sistema.
  • Apertura canale – è necessario aprire un nuovo canale SSH su una sessione autenticata per ogni comando remoto da eseguire.
  • Esecuzione canale – eseguire il comando di shell cat su un file statico
  • Lettura canale – recuperare l’output del comando.
  • Chiusura canale – eseguito quando il comando è finito per raccogliere lo stato di uscita.
  • Lettura SFTP – inizializza la sessione SFTP, apre l’handle del file remoto, legge i dati. I dati letti non vengono scritti da nessuna parte.

Il file statico che viene usato per il cat comando remoto è un 26KB file di licenza dal repository ssh2-python.

Il file usato per la lettura SFTP è un archivio tar compresso di 11MB.

Tutte le durate sono in millisecondi (ms).

Risultati del test

I grafici mostrano valori mediani per intervalli di trenta secondi.

Grafico di tutte le operazioni

Ecco come le due librerie si confrontano sulle cinque operazioni separate più il tempo totale impiegato.

Clicca sull’immagine per una versione più grande.

confronto paramikossh2

Come si può vedere sopra, le durate di Paramiko sono dominate dalla lettura di SFTP, dall’autenticazione e dall’apertura del canale che continuano ad aumentare con l’aumentare della concorrenza.

Per ssh2-python, il tempo più grande speso è in SFTP read seguito da auth e channel open.

Entrambe le librerie mostrano picchi intermittenti nella durata che sono attesi dato il numero di thread e chiamate bloccanti usate.

Operazioni individuali

Tempi per operazioni che non includono SFTP e totale per mostrare una visione più vicina del resto dei tempi.

Clicca sull’immagine per una versione più grande.

confronto paramikossh2

Prestazioni relative

Prestazioni relative delle medie delle operazioni mediane delle due librerie per la durata del test. Le durate di lettura totale e SFTP sono per le medie.

Per esempio, se un’operazione Paramiko fosse due volte più veloce dell’equivalente ssh2-python operazione, la sua prestazione relativa sarebbe x0.5 di ssh2-python mentre una durata identica risulterebbe in x1 prestazione relativa.

Operazione Paramiko ssh2 Paramiko/ssh2-differenza relativa python
auth 1.16 sec 675 ms x1.71
channel open 1.248 sec 141 ms x8.85
channel read 78 ms 29 ms x2.68
close and exit status 4 ms 1 ms x4
execute 24 ms 3 ms x8
sftp_read 19.35 sec 1.13 sec x17.12
total 20.82 sec 2.04 s x10.2

Postface

In tutto, ssh2-python si dimostra notevolmente più veloce, in particolare nelle operazioni pesanti come SFTP, per le quali la lettura è circa x17 volte più veloce in media rispetto a Paramiko. Altre operazioni che beneficiano particolarmente sono l’apertura del canale, x8 più veloce, e l’esecuzione, anch’essa x8. In totale nel caso di test di cui sopra ssh2-python è mostrato essere circa un ordine di grandezza (x10) più veloce in media.

Nota anche che il consumo di memoria non è graficato, ma è significativamente più basso con ssh2-python come previsto in quanto è basato su una libreria nativa.

Per quanto riguarda il confronto tra una libreria SSH in puro codice Python e una basata su una libreria C che è “ingiusta” – la libreria in codice Python “puro” usa anche estensioni del codice nativo per le operazioni di crittografia, rendendo l’argomento irrilevante. Si noti come l’operazione con la minor differenza di prestazioni sia l’autenticazione, che è per lo più gestita da estensioni di codice nativo in Paramiko.

Non c’è nemmeno l’obbligo di scrivere un’implementazione API SSH di basso livello puramente in Python. Ci sono molte ragioni per non farlo, come questi risultati dimostrano.

Appendice

Lo script di test utilizzato può essere trovato qui sotto:

  • Script di test delle prestazioni

Lo script di test scrive dati di durata su un InfluxDB locale attraverso il suo servizio Graphite utilizzando un template measurement.field*.

I grafici di cui sopra sono stati generati da Grafana, interrogati da InfluxDB da InfluxGraph utilizzando la stessa configurazione di template di InfluxDB.

Per replicare i risultati, fate attenzione ad utilizzare le versioni esatte delle librerie qui utilizzate, compreso libssh2. Versioni più vecchie di libssh2 non sono completamente thread safe e causeranno crash.

Disclaimer – Mentre sono l’autore di ssh2-python, non ho alcun interesse personale in libssh2 o Paramiko. Il mio interesse nelle loro rispettive prestazioni è per l’uso in parallel-ssh.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *