Rundeck : le pont entre Devs et Ops pour une automatisation efficace

Comme il est de coutume dans la carrière de tout OPS, j’ai été amené à gérer l'exploitation d’applications au sein d'une équipe composée essentiellement de développeurs.

Ce travail consistait globalement à :

  • Déployer les applications (war, ear, jar) sur les environnements (Dev, test, Intégration, Prod)
  • Mettre à jour des composants logiciels sur les serveurs (exemple : Tomcat)
  • Effectuer des opération récurrentes (exemple : Nettoyage de logs saturant l'espace disque)
  • Copier des données de base de données d'environnement X vers Y

Au fil du temps, la taille des applications et des composants d'infrastructure grandissant, nous ne pouvions plus assurer le travail demandé car le volume de données était trop important.

Nous nous sommes donc tournés vers une solution permettant d'exécuter des tâches automatisées avec une volonté de donner la main sur certaines tâches d'exploitation aux développeurs : Rundeck.

Voici donc un article décrivant les fonctionnalités de cet outil ainsi que les apports qu’il génère coté DEV & OPS sans rentrer dans les détails, la description complète de chaque fonctionnalité pouvant faire l'objet d'un article à part entière.

Avant de rentrer dans le vif du sujet voici une illustration présentant les différents concepts de rundeck :

empty

Au sein de Rundeck existe une notion de projet. Chaque projet peut être vu comme un regroupement cohérent .

Au sein de ce projet est défini un ou des jobs qui sont des travaux, tâches relatives au projet.

Enfin dans le job se déroule des steps qui sont les étapes nécessaire permettant de réaliser le job sur les noeuds (serveurs / containers etc.).

1. Automatisation des tâches d'exploitation

Dans le but d’arrêter de gérer manuellement les tâches d’exploitation, nous avons dans un premier temps mis en place une phase de scripting.

Nous nous étions imposés une règle dans l’équipe, que nous avons essayé de respecter au maximum (même si en réalité cela est compliqué selon les urgences et le volume des travaux à gérer) :

Si tu fais deux fois la même chose alors automatise-la !

Nativement, il existe plusieurs moyens d'effectuer une tâche d'exploitation :

empty

Ces différents moyens sont décrits ci-dessous :

a. La commande classique

empty

La commande ‘ps -aef | grep java’ sera exécutée sur le serveur correspondant au noeud

b. Utilisation d'un script

empty

Le script décrit dans Rundeck sera exécuté sur le serveur correspondant au noeud

c. Utilisation d'un script distant

Via le file system

Exécution d’un script dans le file system du serveur hébergeant Rundeck sur le serveur correspondant au nœud

Via une URL

Exécution d’un script accessible en http sur le serveur correspondant au nœud

d. Utilisation de la composition de jobs

Un job (ou travail dans la version française) au sens Rundeck représente une ou des actions à effectuer sur un serveur.

Il est défini par un nom, une action et des paramétrages spécifiques.

Ce qui est intéressant d'utiliser c'est la possibilité d'appeler d'autres jobs pour effectuer un job plus global, créant ainsi une sorte de workflow.

Pour illustrer ceci, voici par exemple une installation d'un tomcat, qui pourrait se découper de cette manière :

Côté Rundeck le job se configure ainsi :

Une fois composé, le job ressemble à ceci :

e. Ansible et autres possibilités

Pour les aficionados d’Ansible, il est possible d'utiliser des playbooks pour exécuter ces tâches. C’est plutôt utile quand on a déjà une stack et que l'on souhaite utiliser Rundeck pour ses autres fonctionnalités que je vais évoquer par la suite.

Il existe un outil graphique permettant de gérer et suivre l’exécution des playbooks Ansible: AWX/Tower cependant ce qui est séduisant avec Rundeck c'est qu'il est possible de s'interfacer avec plusieurs outils.

Pour cela, il existe également des plugins permettant d'enrichir les fonctionnalités de Rundeck tels que des connecteurs docker, kubernetes, AWS qui permettent d'exécuter des actions sur les infrastructures ou les containers.

Je vous invite à aller sur le site de Rundeck pour trouver votre bonheur !

2. Utilisation de 'nœuds' distants et la gestion des tags

Une des forces de Rundeck est de pouvoir exécuter les jobs sur des 'nœuds' distants. Il est ainsi possible au sein d'un projet Rundeck de déclarer des nœuds sur lesquels des jobs peuvent s'exécuter.

Un nœud représente un serveur distant.

Pour chaque nœud déclaré il est alors possible d'ajouter des tags qui permettront  par la suite lors de la création d'un job de spécifier sur quel nœud jouer le job.

Exemple

Imaginons l'infrastructure suivante :

On pourrait tagger chaque type de composant, cela pourrait permettre par exemple d'effectuer une action sur toutes les VM taggées 'back' afin de mettre à jour la version de JAVA par exemple.

On est libre de créer les tags sans restriction cependant au sein d’une équipe il est recommandé d’établir une norme de nommage au risque d’avoir plusieurs tags qui nomment un même outils, composant ou autre.

La définition des nœuds se fait dans un fichier ressource qui peut être décrit en xml, json ou encore  yaml.

Pour l'infrastructure ci-dessus, le fichier ressource en yaml pourrait donc se traduire de la manière suivante :vmi.front.1:

nodename: vmi.front.1 # nom du noeud affiché dans Rundeck

hostname: vmi-front-1 # nom de l'hôte / serveur correspondant au noeud

username: front # user pour la connexion ssh

description: Noeud 1 front # description affichée dans Rundeck

osFamily: unix # famille d’os du serveur (facultatif)

osName: alpine # nom de l’os du serveur (facultatif)

osArch: amd64 # Architecture du serveur (facultatif)

tags: 'front,node,apache' # tags décrivant le serveur (facultatif mais recommandé)

[...]

vmi.back.3:

nodename: vmi.back.3

hostname: vmi-back-3

username: back

description: Noeud 3 backend

osFamily: unix

osName: alpine

osArch: amd64

tags: 'back,java,tomcat'

[...]

vmi.bdd.2:

nodename: vmi.bdd.2

hostname: vmi-bdd-2

username: bdd

description: Noeud 2 bdd

osFamily: unix

osName: alpine

osArch: amd64

tags: 'bdd,mysql'

Stratégie d’exécution des tâches sur les nœuds (serveurs)

Lors de l’exécution d'un job sur X nœuds il est possible de définir l'ordre d’exécution de ce job sur les nœuds.

https://www.rundeck.com/blog/howto-controlling-how-and-where-rundeck-jobs-execute

1. Parallèle : Le job sera exécuté en parallèle et en même temps sur les nœuds

2. Node first : Tous les ‘steps’ (étapes à exécuter sur le noeud) d’exécution du job se jouera sur le nœud 1 puis sur le nœud 2 puis sur le nœud X

3. Sequential : Le step 1 sera joué sur le nœud 1 puis nœuds 2 puis nœud X ensuite le step 2 ainsi de suite

Communication vers les nœuds

Pour communiquer sur les nœuds distants il faut mettre en place une authentification ssh par clef et spécifier dans le fichier ressource contenant la définition des nœuds quel username choisir.

3. Mode de déclenchement des jobs

Il existe plusieurs moyens de déclencher des jobs définis pour un projet Rundeck.

a. Manuellement

C'est le mode par défaut.

L'utilisateur se connecte à l'IHM de Rundeck et demande l’exécution d'un JOB par exemple la copie des données d'une base ou l'installation d'une application sur un environnement donné.

Ce mode - à la demande - est utilisé pour toutes le tâches qui ne sont pas reliés à un automate ou une application exemple : Demande de dump, copie de base de donnée, Installation sur un environnement, exécution d’une commande sur un serveur etc.

b. Cron

Il est possible d'ordonnancer les jobs. Lors de la définition de celui-ci il suffit de spécifier à quel moment le job doit se lancer.

Via le mode classique Rundeck :

Via un Cron :

Cette utilisation est réservée pour les tâches de type batch (nettoyage de file system récurrent, collecte de données, copie d’informations etc.)

c. API

La dernière option est d'utiliser l'API de Rundeck.

Celle-ci permet d'exécuter des jobs, de les lister, de créer des projets, bref de faire presque tout ce que l'on peut faire à travers l'IHM sous réserve d'avoir les droits (j'en parlerais un peu après).

Cela permet notamment d'enchainer un build Jenkins et une livraison via Rundeck, Jenkins faisant un call via l'API de Rundeck.

Cette utilisation est préconisée dans le cas où l’utilisateur est une application (Exemple : Suite à un build jenkins, celui ci appele Rundeck pour déployer sur un environnement. Suite au provisioning d’un environnement, configuration de Rundeck par un script via l’API.)

Plus d'infos sur l'api : https://github.com/rundeck/docs/blob/3.0.x/en/api/rundeck-api.md

4. Notification

Il existe plusieurs solutions de notification au sein de Rundeck.

Par défaut il est possible de :

  • Envoyer un email
  • Utiliser un webhook

En complément il est possible d'utiliser les plugins pour enrichir la solution. Il existe par exemple un plugin permettant d'envoyer des messages dans rocket chat ou slack.

Ces notification permettent aux OPS / DEV d'être alerté en cas de :

  • Échec / Succès d’un job
  • Dysfonctionnement suite au ‘retry’ d'un job
  • Au démarrage d'un job
  • En cas de dépassement du temps moyen d'exécution

Toutes ces notifications permettent par exemple aux développeurs DEV d'être informé d’événements comme lorsqu'un job de copie de données de BDD est terminé et pour les OPS d'être alertés en cas de mauvais fonctionnement d'un processus d'exploitation ou encore lors de l’installation d'un composant en production ou sur un environnement donné.

5. Monitoring

Au sein de l'outil existe une vue qui permet de connaître sur une période donnée l'état des jobs.

Cela indique le nombre de jobs qui ont été exécutés, combien n'ont pas fonctionnés, voir les logs des jobs ce qui est très pratique pour voir l’état des traitements et identifier quel process est en erreur, depuis combien de temps.

Et jenkins dans tout ça ?

On m'a plusieurs fois posé la question : quelle différence avec Jenkins ?

De mon point de vue, Jenkins est spécialisé dans la construction de builds, d'exécuter toutes les étapes du pipeline de build jusqu'à la construction du livrable alors que Rundeck lui est spécialisé dans l'exécution de tâches d'exploitation.

Jenkins à des fonctionnalités que Rundeck n’a pas et vice versa.

Pour illustration voici un schéma tiré de la documentation Rundeck :

https://www.rundeck.com/blog/comparing-rundeck-and-jenkins-works-great-together

Ces deux outils ne sont pas en opposition mais plutôt complémentaires. Ils ont toute leur place au sein d'une usine logicielle.

Et le DEVOPS dans tout cas ?

Grâce à son IHM et sa gestion de rôles, Rundeck est une bonne porte d’entrée pour sensibiliser et faire monter en compétences les développeurs sur les sujets OPS.

On peut alors très bien imaginer, dans une première étape, qu’une fois que les OPS ont défini les tâches d'exploitation dans Rundeck, qu’ils puissent laisser la main aux développeur pour toutes les tâches routinières telles que les copies de données de BDD ou pour les installations d'applications sur les environnements.

Petit à petit et en étant accompagnés, les développeurs pourraient alors être de plus en plus autonomes et à terme même créer leurs propres jobs !

Il est possible de coupler Rundeck avec GIT, ce qui permet de contrôler et valider à tout moment ce qui est poussé par les développeurs et de revenir en arrière en cas de besoin.

Il existe de plus au sein de l’outil une gestion des rôles.

Afin de séparer et restreindre les droits sur les jobs il existe une notion de rôles au sein de Rundeck.

Cette gestion des rôles est très fine à travers des ACL ce qui permet de définir les droits en read / update / create / exécution / delete sur les types suivants :

  • Jobs
  • Ressource
  • Noeud

Pour plus d'informations : https://docs.rundeck.com/docs/administration/security/access-control-policy.html

Ainsi, en automatisant le maximum de tâches d’exploitation et en laissant la main aux développeurs sur leurs exécutions, les OPS peuvent alors se concentrer sur les travaux demandant leur vraie expertise.

En résumé, pour tirer le meilleur parti des automatisations offertes par Rundeck, il est vital d'adopter des pratiques éprouvées comme le Test-Driven Infrastructure (TDI), garantissant ainsi une infrastructure fiable et performante.