L’état des bibliothèques SSH Python

Dans ce billet, une nouvelle option pour les bibliothèques SSH Python, ssh2-python, sera comparée à Paramiko pour démontrer leurs performances respectives avec un accent particulier sur la concurrence et les requêtes non bloquantes.

Lorsqu’on cherche une bibliothèque SSH Python à utiliser dans une application, peu d’options existent. En effet, la seule bibliothèque à usage général qui a, jusqu’à présent, été disponible est Paramiko, qui implémente l’API SSH2 dans le code Python.

Pour le meilleur ou pour le pire – il ne fait aucun doute que la bibliothèque a aidé un grand nombre de personnes ayant des exigences similaires, cet auteur inclus car elle a historiquement été la seule option – Paramiko a été la norme de facto pour les bibliothèques SSH Python jusqu’à présent.

Cependant, il laisse beaucoup à désirer du point de vue des performances, de la stabilité et de la consommation de ressources.

Heureusement, il existe maintenant une autre option sous la forme de ssh2-python qui est basée sur la bibliothèque C libssh2.

De nombreuses applications d’automatisation comme Ansible et Fabric utilisent Paramiko et seraient probablement intéressées de savoir que d’autres options existent maintenant. Espérons que ce billet aidera ces applications et d’autres à déterminer quelle bibliothèque est la mieux adaptée.

Comparaison des performances

ssh2-python est une nouvelle bibliothèque SSH Python basée sur la bibliothèque libssh2 C. Elle n’a pas de dépendances et fournit des binaires Linux, OSX et Windows avec libssh2 inclus.

paramiko est écrite en Python et fait usage de dépendances d’extension natives comme la cryptographie. Il supporte Linux, OSX et Windows et ses dépendances natives fournissent des roues binaires.

Mise en place du test

Le test utilise la bibliothèque standard de Python threading pour la concurrence. Un billet de blog ultérieur examinera les performances non bloquantes dans les bibliothèques via la bibliothèque de co-routine gevent.

Bien que le threading ne soit pas un bon modèle pour mettre à l’échelle la simultanéité des E/S réseau, c’est le seul mode de simultanéité pris en charge nativement par Paramiko et c’est ce qui est utilisé par des applications comme Ansible. D’autres projets comme parallel-ssh, dont je suis également l’auteur, utilisent le monkey patching de gevent pour rendre Paramiko concurrent de manière coopérative. Cela aussi présente des inconvénients importants que le client non bloquant basé sur ssh2-python en cours de développement vise à résoudre. Voir la demande de pull en cours pour plus de détails à ce sujet.

Le script de test crée des sessions SSH en parallèle à un serveur SSH (OpenSSH) via un dispositif de retour de boucle (localhost), en commençant par un et en augmentant d’un à chaque itération jusqu’à la fin. Cela montre comment les deux bibliothèques s’échelonnent lorsque le nombre de sessions parallèles augmente.

Le nombre maximal de threads et donc de sessions parallèles est fixé à 50. Tous les tests sont effectués sur un processeur quadruple cœur physique.

Dans tous les tests, la dernière version disponible de chaque bibliothèque est utilisée, 2.2.1 et 0.5.3 pour Paramiko et ssh2-python respectivement. Pour ssh2-python un libssh2 embarqué a été utilisé, dernière version disponible 1.8.0. Tous les tests effectués sous Python 2.7.

Les bibliothèques sont comparées dans six opérations SSH distinctes, ainsi que le temps total passé du début à la fin par session SSH.

Les six opérations comparées sont :

  • Initialisation de la session et authentification avec l’agent SSH (auth)
  • Ouverture du canal (channel_open)
  • Exécution du canal (execute)
  • Lecture du canal -. (channel_read)
  • Canal fermer et obtenir le statut de sortie (close_and_exit_status)
  • SFTP lire (sftp_read)

Une explication rapide de ce que font ces opérations :

  • Initialisation de la session et authentification – effectuer une poignée de main avec le serveur SSH et s’authentifier via un agent SSH système.
  • Ouverture d’un canal – un nouveau canal SSH sur une session authentifiée doit être ouvert par commande distante à exécuter.
  • Exécution d’un canal – exécuter la commande shell cat sur un fichier statique
  • Lecture d’un canal – récupérer la sortie de la commande.
  • Channel close – effectuée lorsque la commande est terminée afin de recueillir le statut de sortie.
  • SFTP read – initialiser la session SFTP, ouvrir le handle du fichier distant, lire les données. Les données lues ne sont écrites nulle part.

Le fichier statique qui est utilisé pour la cat commande à distance est un 26KB fichier de licence du dépôt ssh2-python.

Le fichier utilisé pour la lecture SFTP est une archive tar compressée de 11 Mo.

Toutes les durées sont en millisecondes (ms).

Résultats du test

Les graphiques montrent les valeurs médianes par intervalles de trente secondes.

Graphique de toutes les opérations

Voici comment les deux bibliothèques se comparent sur les cinq opérations distinctes plus le temps total passé.

Cliquez sur l’image pour une version plus grande.

comparaison de paramikossh2

Comme on peut le voir ci-dessus, les durées de Paramiko sont dominées par la lecture SFTP, l’authentification et l’ouverture de canal qui ne cessent d’augmenter à mesure que la concurrence monte en puissance.

Pour ssh2-python, le plus gros temps passé est dans la lecture SFTP suivie par l’authentification et l’ouverture du canal.

Les deux bibliothèques montrent des pics intermittents de durée qui sont attendus étant donné le nombre de threads et d’appels bloquants utilisés.

Opérations individuelles

Temps pour les opérations n’incluant pas SFTP et le total pour montrer une vue plus proche du reste des timings.

Cliquez sur l’image pour une version plus grande.

comparaison paramikossh2

Performances relatives

Performances relatives des moyennes des opérations médianes des deux bibliothèques pour la durée du test. Les durées de lecture totale et SFTP sont pour les moyennes.

Par exemple, si une opération Paramiko était deux fois plus rapide que l’opération équivalente ssh2-python, sa performance relative serait x0.5 de ssh2-python alors que des durées identiques donneraient x1 de performance relative.

.

.

Opération Paramiko ssh2 Paramiko/ssh2-.python différence relative
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 s x17.12
total 20,82 s 2.04 s x10.2

Postface

Au total, ssh2-python se révèle considérablement plus rapide, notamment dans les opérations lourdes comme SFTP, pour lesquelles la lecture est quelque x17 fois plus rapide en moyenne par rapport à Paramiko. Les autres opérations qui en bénéficient particulièrement sont l’ouverture de canal, x8 plus rapide, et l’exécution, également x8. Au total, dans le cas de test ci-dessus, ssh2-python s’avère être environ un ordre de grandeur (x10) plus rapide en moyenne.

Notez également que la consommation de mémoire n’est pas représentée graphiquement, mais qu’elle est significativement plus faible avec ssh2-python comme prévu puisqu’elle est basée sur une bibliothèque native.

Pour ce qui est des comparaisons entre une bibliothèque SSH en code Python pur et une bibliothèque basée sur une bibliothèque C qui seraient  » injustes  » – la bibliothèque en code Python  » pur  » utilise également des extensions en code natif pour les opérations de cryptographie, ce qui rend l’argument discutable. Notez comment l’opération avec la moindre différence de performance est l’authentification qui est principalement gérée par des extensions de code natif dans Paramiko.

Il n’y a également aucune obligation d’écrire une implémentation de l’API SSH de bas niveau purement en Python. De nombreuses raisons de ne pas le faire en fait, comme le montrent en partie ces résultats.

Annexe

Le script de test utilisé se trouve ci-dessous :

  • Scriptum de test de performance

Le script de test écrit des données de durée dans une InfluxDB locale via son service Graphite en utilisant un modèle measurement.field*.

Les graphiques ci-dessus ont été générés par Grafana, tels qu’interrogés depuis InfluxDB par InfluxGraph en utilisant la même configuration de template qu’InfluxDB.

Pour reproduire les résultats, veillez à utiliser les versions exactes des bibliothèques utilisées ici, notamment libssh2. Les anciennes versions de libssh2 ne sont pas complètement thread safe et provoqueront des plantages.

Disclaimer – Bien que je sois l’auteur de ssh2-python, je n’ai aucun intérêt direct dans libssh2 ou Paramiko. Mon intérêt pour leurs performances respectives est pour une utilisation dans parallel-ssh.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *