deploy

En 2015 j'avais développé un outil de déploiement pour me faciliter la vie au travail. J'en présente ici son développement.

Analyse et conception

L'outil deploy permet d'exécuter des commandes ou scripts sur des serveurs distants. Il permet aussi de transférer des répertoires. Il a la possibilité d'appliquer un déploiement sur un, plusieurs ou tout les serveurs distants.

J'ai choisi d'implémenter mon programme en Bash pour continuer à monter en compétence sur cette technologie. deploy se base sur ssh pour fonctionner :

  • ssh pour se connecter aux serveurs distants
  • scp pour transférer les fichiers
  • le fichier de configuration des hôtes ssh pour identifier facilement les hôtes via un alias (c'est à dire un nom au lieu d'un ensemble login/ip/port)
  • l'échange de clé public/privée pour ne pas à avoir à saisir un login et mot de passe

Codage

Pour gérer les options j'utilise getopts qui est nativement intégré au bash. Je passe outre l'explication de ce dernier car j'ai déjà rédigé un article à ce sujet : "La gestion des options sous Bash". Je ne traite pas non plus des options -h et -V (respectivement help et Version) car celles-ci se contentent d'afficher du texte sur la sortie standard.

Pour obtenir la liste des hôtes à utiliser par défaut, je les extraient du fichier de configuration ssh ~/.ssh/config :

HOSTS=`grep "^Host " ~/.ssh/config | grep -E -v "(\*|\?|\!)" | cut -c 6-`

Il y a trois commandes exécutées les unes à la suite des autres grâce aux pipes :

  • la première commande grep "^Host " ~/.ssh/config se charge de récupérer toute les définitions d'hôtes présente dans le fichier de configuration ~/.ssh/config.
  • la deuxième commande grep -E -v "(\*|\?|\!)" se charge d'exclure toute les définitions pouvant agir sur plusieurs hôtes (usage des regex), il s'agit d'une sécurité
  • la troisième commande cut -c 6- enlève les 5 premiers caractères de chaque ligne précedement récupérées (c'est à dire Host) afin de ne garder que l'alias

Pour exécuter une commande distante -c <command> :

ssh <alias> <command>
# exemple:
ssh foo 'ls -al'

Pour exécuter un script (argument par défaut) :

ssh <alias> '/bin/bash -s' < <script>
# exemple:
# ssh foo '/bin/bash -s' < my_script.bash

Pour effectuer une copie distante :

scp <local_file> <alias>:<remote_file>
# exemple:
# scp my_file.txt foo:/tmp/my_file.txt

Pour aller plus vite j'ai mis en place l'exécution des commandes en parrallèles grâce au jobs. Sur le principe :

# tableau contenant les PID de chaque commande
declare -A pid

# execution des commandes en parrallèles pour chaque hôtes
# et stoquage du PID dans le tableau precedement defini
for host in ${hosts}; do
  <command> & pid[${host}]=${!}
done

for host in $hosts; do
  # en attente de la fin d'execution de chaque commande
  wait ${pid[${host}]}
  # stoque le code de retour de l'execution de la commande
  rc=${?}
  # si il y a une erreur, on affiche :
  # l'hote;son PID;le code d'erreur
  if [[ ${return_code} -ne 0 ]]; then
    echo "${host};${pid[${host}]};${return_code}"
  fi
done

Après des options ont étaient ajoutées pour faciliter l'usage et/ou pour palier à des contraintes techniques :

  • -l : afficher la liste des hôtes disponnible
  • -e : pour exclure des hôtes à déployer
  • -F : pour spécifier le fichier de configuration ssh à utiliser (par défaut ~/.ssh/config)
  • -r <source> :<destination> : pour effectuer un scp récursif

Tout les paramètres des commandes précedente sont variabilisés afin d'être intégrer au programme final.

Usage

Exemple de fichier de configuration ssh ~/.ssh/config :

Host foo
    HostName foo.tld
    User foo

Host bar
    HostName 192.168.10.92
    User bar

Host baz
    HostName baz.tld
    User baz

Exemple d'utilisation, partant du principe que l'échange de clé ssh à déjà était fait :

# get help
deploy -h
# deploy all hosts with a Bash script                                                                                                
deploy script.bash                                                                                                                   
# or deploy with a command                                                                                                           
deploy -c 'echo "Hello World" >> /tmp/hw.txt'                                                                                        
# deploy both foo and bar hosts                                                                                                      
deploy -u foo,bar script.bash                                                                                                        
# deploy all hosts, excepting foo and bar                                                                                            
deploy -e foo,bar script.bash
# list all hosts
deploy -l

Conclusion

Ce projet était techiniquement très enrichissant et m'a rendu un grand service dans mon quotidien professionnel pendant plus d'un an. J'avais pour objectif de migrer mon outil en Python pour gérer plus facilement les erreurs mais j'ai fini par l'abandonné au profit d'un autre outil de déploiement existant déjà : Ansible. En effet ce dernier étant plus avancé que mon outil (groupement d'hôtes, inventaire dynamique, ergonomie, modules, playbook ...) et étant très largement utilisé et maintenu je n'avais plus de raison de continuer avec le mien.

Si vous souhaitez jetter un coup d'oeil sur le code source je vous invite à aller voir le projet sur GitHub.

By @Mikael FLORA in
Tags : #dev, #bash, #script, #shell, #network, #ssh,