Lorsque l'on évoque le déploiement automatisé, de grands noms viennent immédiatement à l'esprit : Ghost (logiciel propriétaire), CloneZilla ou encore Fog (logiciels libres). Ces logiciels proposent de déployer une image référence sur X postes de travail. La création de cette image référence peut se faire de façon brute, bits à bits ou de manière plus intelligente si le logiciel comprend le système de fichiers utilisé par l'image. Ces images sont ensuite déployées sur tous les postes en unicast ou multicast via un agent en exécution sur les postes à déployer. Cet agent peut être un service réseau du système d'exploitation installé sur le poste ou un service exécuté depuis un périphérique de démarrage (LiveCD, Clé USB, PXE, etc.).
Une autre approche est de créer une image très basique du système à déployer et d'appliquer en post-installation tous les logiciels et paramètres de configuration. L'avantage par rapport à la première approche est le côté moins figé de l'image. Il est possible de mettre à jour de manière modulaire les éléments de configuration en modifiant la configuration du système post-installation. L'inconvénient est que côté administration système, cela fait un service de plus à gérer. C'est à mettre en balance avec la fréquence de modification des images. De tels systèmes de configuration post-installation peuvent soit s'appuyer sur le système de gestion de paquets de la distribution (Kickstart pour rpm et FAI pour dpkg), soit être agnostiques par rapport au système utilisé, moyennant évidemment une couche d'abstraction entre lui et le système d'exploitation (Puppet et Chef).
1. Kadeploy
Kadeploy est utilisé au sein du pôle recherche de la DSI de l'université Paris 13 pour déployer les nœuds du cluster HPC (High Performance Computing) partagés par les différents laboratoires du campus. Il est installé sur un nœud d’administration sous Debian et il est chargé de déployer des images en Debian Wheezy avec la couche logicielle HPC. Le déploiement passe par le réseau hors-bande du cluster soit un réseau Ethernet classique (par opposition aux réseaux à faible latence type Infiniband dédiés au calcul). D'un point de vue scientifique, l’intérêt de Kadeploy est ses excellentes performances sur les déploiements à large échelle [2]. D'un point de vue administration système, c'est sa qualité d'intégration et son fonctionnement très modulaire.
1.1 Architecture
Kadeploy s'appuie sur les briques standards du système. C'est un système assez modulaire et l'installation présentée dans cet article repose sur TakTuk, PXE, DHCP, Puppet, OpenSSH et IPMI. Kadeploy est divisé en deux grosses parties :
- Une partie serveur kadeploy3d qui écoute sur le réseau en attente des demandes de déploiement. Une fois la demande reçue, il lance le déploiement sur la ou les machines concernées ;
- Une partie cliente composée d'un ensemble de commandes préfixées par « ka » : kaenv3, kadeploy3 et karights3 pour les principales utilisées dans cet article.
Le serveur Kadeploy3d écrit ses informations dans une base de données MySQL. Ces informations sont essentiellement relatives aux droits des utilisateurs (qui peut déployer quoi et où) et à l'état des déploiements. Le déploiement d'une machine se décompose en plusieurs étapes (voir figure 1) :
1. La machine à déployer démarre en boot PXE et accroche un serveur PXE ;
2. Le serveur PXE lui fait amorcer un noyau Linux accompagné d'un initrd (petit système de base) minimal. Ce noyau est appelé « noyau de déploiement » dans la terminologie Kadeploy ;
3. Lors de l'initialisation des paramètres réseaux, le noyau de déploiement récupère la configuration via le serveur DHCP (ce n'est pas nécessaire au bon fonctionnement de Kadeploy, c'est un choix arbitraire pour configurer le réseau du noyau de déploiement) ;
4. Le serveur Kadeploy se connecte alors par SSH à la machine pour partitionner le disque et instancier le système de fichiers ;
5. Le serveur Kadeploy envoie l'image sur le système de fichiers fraîchement instancié ;
6. Le serveur Kadeploy redémarre la machine et attend qu'elle ait fini de démarrer pour notifier la réussite du déploiement.
Fig. 1: Étapes de déploiement d'un système sur un nœud via Kadeploy.
1.2 Installation
Dans cet article, Kadeploy est installé sur une Debian Wheezy via les paquets deb fournis sur la forge INRIA [3]. Kadeploy est développé en Ruby, stocke ses informations dans une base MySQL et utilise l'infrastructure de communication TakTuk [4]. Nous reviendrons sur TakTuk un peu plus tard. Installons les dépendances à partir du gestionnaire de paquets :
# apt-get install ruby ruby-mysql taktuk
Ensuite, il faut télécharger les .deb depuis la forge INRIA et les installer :
# dpkg -i kadeploy-client_3.3.0~rc5-1_all.deb kadeploy-common_3.3.0~rc5-1_all.deb kadeploy_3.3.0~rc5-1_all.deb
La dernière étape de l'installation est de mettre en place la partie MySQL. Cette phase est très classique et consiste à installer le serveur et y injecter la base kadeploy en y associant un utilisateur dédié :
# apt-get install mysql-server
Après avoir répondu aux questions de debconf, il faut se connecter à l'instance MySQL fraîchement installée :
# mysql -u root -p
Créer la base de données :
mysql> CREATE DATABASE deploy3;
Ajouter l'utilisateur dédié à cette base de données :
mysql> GRANT select, insert, update, delete, create, drop, alter, create temporary tables, lock tables ON deploy3.* TO 'deploy'@'localhost';
Et on lui fixe un mot de passe :
mysql> SET PASSWORD FOR 'deploy'@'localhost' = PASSWORD('kadeploy');
Enfin, on ajoute les tables nécessaires au fonctionnement de Kadeploy :
mysql> use deploy3;
mysql> source /usr/share/doc/kadeploy/db_creation.sql
La partie physique de l'installation de Kadeploy est terminée, passons aux briques sur lesquels il s'appuie. Cet article est une proposition d'assemblage de briques sur lesquelles appuyer l'installation de Kadeploy. Comme mentionné dans la section précédente, Kadeploy est modulaire et s'appuie sur des protocoles plutôt que des implémentations. L'administrateur a toute latitude pour composer son infrastructure Kadeploy (par exemple si les clients supportent l'iPXE, ils peuvent récupérer environnement de déploiement sur un serveur HTTP plutôt que par TFTP). De mon point de vue d'administrateur système, c'est l'aspect le plus intéressant et ingénieux de Kadeploy. Le code développé au sein du projet s'appuie sur des services standards et les directives de configuration de Kadeploy sont suffisamment génériques pour utiliser les implémentations de services que l'on veut.
2. Installation des briques
Cette étape consiste à démarrer le noyau de déploiement et instancier le système de fichiers sur la machine visée. Les protocoles utilisés sont DHCP et PXE pour le démarrage de la machine. Un noyau Linux minimal de déploiement sera ensuite configuré. Nous allons commencer par quelques rappels sur les protocoles utilisés par Kadeploy.
2.1. DHCP et PXE
DHCP est un protocole de paramétrage automatique de la couche réseau des machines. Lors de l’initialisation de la couche réseau, la machine envoie une requête DHCPDISCOVER en broadcast sur le réseau. Un serveur DHCP répond avec un DHCPOFFER. Le client sélectionne la première offre reçue. Il forme ensuite une requête DHCPREQUEST à destination du serveur retenu et celui-ci répond avec un DHCPACK contenant les paramètres réseau du demandeur (adresse, DNS et passerelle par défaut).
BOOTP est une option du protocole DHCP qui permet de définir un serveur à attaquer en TFTP (ou en HTTP si la carte réseau gère l'iPXE) pour récupérer une image de démarrage. Les interfaces réseau implémentant la norme PXE peuvent réaliser ce type de démarrage.
La configuration minimale pour Kadeploy est un démarrage PXE proposant un noyau de déploiement avec son initrd. À cette configuration, une netinstall standard Debian a été ajoutée. Cet ajout vient du fait que cela facilite la vie pour créer un master de système à déployer sur des machines ne disposant pas de port USB et encore moins de lecteurs CD/DVD. Cette installation est faite à partir des paquets. Dans un premier temps, nous allons installer la partie strictement minimale pour Kadeploy, soit un serveur DHCP, un serveur TFTP (nos cartes ne font pas de l'iPXE) et les bootstrap PXE à démarrer par le réseau :
# apt-get install isc-dhcp-server tftpd-hpa syslinux
2.1.1. DHCP
Le service DHCP sert à gérer les requêtes DHCPDISCOVER contenant l'option de démarrage PXE générées par les interfaces réseau des clients configurés pour démarrer par le réseau. Le serveur DHCP doit disposer d'un fichier d'amorce (ici pxelinux.0) accessible depuis une adresse de serveur TFTP. Tout cela se passe dans le fichier /etc/dhcp/dhcpd.conf :
ignore client-updates;
subnet 192.168.40.0 netmask 255.255.255.0 {
option domain-name "bullx";
option domain-name-servers @IP_SDNS;
option routers @IP_GW;
pool {
range 192.168.40.20 192.168.40.200;
use-host-decl-names on;
filename "pxelinux.0";
next-server @IP_STFTP;
}
}
On définit d'abord le réseau à servir en DHCP (subnet 192.168.40.0 netmask 255.255.255.0). Ensuite, on fixe tous les paramètres de configuration par défaut : le nom de domaine DNS à concaténer par défaut (option domain-name "bullx"), le(s) serveur(s) DNS (option domain-name-servers @IP_SDNS), et la passerelle par défaut (option routers @IP_GW). La section suivante définit un pool. Un pool est une étendue de machine. Ici, on va de la 20 à la 200 (range 192.168.40.20 192.168.40.200). Pour cette étendue, le nom de la déclaration servira de hostname (use-host-decl-names on). On lui demande d'aller chercher le fichier pxelinux.0 (filename "pxelinux.0") situé sur la machine @IP_STFTP (next-server @IP_STFTP).
2.1.2. TFTP
Au boot, la machine cliente cherche dans le répertoire pxelinux.cfg/ si elle trouve un fichier de configuration pour le boot PXE lui correspondant. La correspondance peut se faire par rapport à l'adresse MAC de l'interface réalisant le boot ou sur l'adresse IP de l'hôte. Si elle ne trouve rien, l'interface tente de trouver le fichier default :
PXE entry point found (we hope) at 9AE5:00D6
My IP address seems to be C0A80146 192.168.1.70
FTFTP prefix:
Trying to load: pxelinux.cfg/01-00-14-22-a1-53-85
Trying to load: pxelinux.cfg/C0A80146
Trying to load: pxelinux.cfg/C0A8014
Trying to load: pxelinux.cfg/C0A801
Trying to load: pxelinux.cfg/C0A80
Trying to load: pxelinux.cfg/C0A8
Trying to load: pxelinux.cfg/C0A
Trying to load: pxelinux.cfg/C0
Trying to load: pxelinux.cfg/C
Trying to load: pxelinux.cfg/default
On voit bien que la machine tente d'abord par l'adresse MAC, puis par l'IP (encodée en hexadécimal), puis par les sous réseaux pour enfin se résoudre à utiliser default. Voici le fichier default utilisé dans l'installation de Kadeploy sur le site :
default menu.c32
prompt 0
menu title Debian GNU/Linux installer boot menu
label local
menu label ^Boot local
menu default
kernel chain.c32
append hd0 0
timeout 50
label install
menu label ^Installation Debian Wheezy
kernel debian-wheezy/amd64/vmlinuz
append vga=788 initrd=debian-wheezy/amd64/initrd.gz -- quiet
label deploy
menu label ^Noyau deploiement Kadeploy
kernel kernels/vmlinuz-2.6.32-5-amd64
append vga=788 initrd=kernels/initrd -- quiet
Nous voyons ici trois entrées pour le menu de démarrage. Pour disposer d'un menu en mode texte, il faut placer le fichier menu.c32 à la racine du service TFTP (default menu.c32).
La première section est un démarrage local (label local). Ce boot est l'entrée par défaut (menu default). Ce démarrage utilise le module chain.c32 pour trouver un média sur lequel démarrer. Le média sélectionné ici est le MBR du premier disque dur (hd0 0). Si on avait voulu démarrer sur la première partition du second disque dur on aurait mis hd1 1. Cette entrée sera automatiquement sélectionnée au bout de 5 secondes (timeout 50).
La seconde section démarre l'installeur Debian pour la version Wheezy (label install). Ici on charge un noyau (kernel debian-wheezy/amd64/vmlinuz) et l'initrd qui va avec (ligne suivante, append).
La dernière section démarre un noyau avec un initrd contenant tous les outils pour dépanner une machine plantée. Ici il s'agit du noyau utilisé par Kadeploy. Nous verrons dans la section suivante comment créer (et personnaliser) le noyau Kadeploy.
L'utilisateur deploy doit pouvoir écrire dans le répertoire /var/lib/tftp/pxelinux.cfg/. Kadeploy va utiliser ce répertoire tout au long de l’exécution du processus de déploiement pour piloter par PXE le contexte de boot de la machine (boot local ou environnement de déploiement).
# chown -R deploy /srv/tftp/pxelinux.cfg
2.2. Noyau de déploiement
Ce noyau intervient au moment du déploiement d'une machine. En fait, la machine doit démarrer dessus par PXE. Kadeploy utilise un outil nommé debirf pour générer le noyau et l'initrd à déposer dans le répertoire TFTP. debirf propose un ensemble de scripts facilitant grandement la création d'un environnement de boot minimal . En très gros, il récupère un kernel pour l'image vmlinuz et il fait un debootstrap pour peupler l'initrd (debirf s'occupe de la partie archivage/compression). On obtient deux fichiers en sortie :
- Un noyau ;
- Un initrd.
Il faut installer deux paquets supplémentaires pour créer son noyau de démarrage : debootstrap et debirf (et éventuellement build-essential s'il n'est pas présent) :
# apt-get install debootstrap build-essential debirf
Les prérequis pour un initrd Kadeploy sont :
- Un serveur SSH pour recevoir des commandes depuis le service Kadeploy. Les connexions du service Kadeploy se font traditionnellement via une clé. Il faut donc que cette clé soit installée dans l'initrd ;
- Les drivers minimaux type contrôleurs disques ;
- Les outils d'instanciation de systèmes de fichiers (mkfs, fsck) ;
- Un client DHCP (nous utilisons une imputation par l'adresse MAC, nous savons quelle machine est derrière quelle IP).
Pour créer un tel environnement avec Kadeploy, il faut commencer par copier la partie publique de la clé SSH dans l'environnement de debirf :
# cp /etc/kadeploy3/keys/id_deploy.pub /opt/kadeploy-3.3.0.rc8/addons/deploy_env_generation/debirf/kadeploy-deploy-kernel/kadeploy_specific/ssh/
Ensuite, il faut aller dans le répertoire /opt/kadeploy-3.3.0.rc8/addons/deploy_env_generation/debirf et exécuter cette commande :
# make all
Cette commande va aller lire le fichier debirf.conf pour récupérer la distribution sur laquelle baser l'initrd (DEBIRF_DISTRO), les miroirs (DEBIRF_MIRROR) et la liste des paquets à inclure/exclure dans l'initrd (INCLUDE / EXCLUDE). Ce delta de paquets se fait par rapport au debootstrap.
Cet environnement est tout à fait personnalisable. Par exemple, on peut ajouter le support du firmware pour les cartes réseaux broadcom (firmware-bnx2). Debirf utilise un système de modules pour personnaliser l'initrd. Ils sont localisés dans le répertoire modules de la configuration de votre environnement à créer. Un module existant a0_add_extra_repos active déjà les dépôts « non-free ». On peut créer un autre module firmware-bnx2 pour installer les firmwares broadcom :
#!/bin/bash -e
debirf_exec sh -c "apt-get -y install firmware-bnx2"
On notera que la commande apt-get n'est pas invoquée en direct, mais via la fonction debirf_exec qui réalise un chroot dans l’environnement avant de lancer la commande en paramètre (sinon le paquet sera installé sur le système de base et non dans l'initrd). En sortie, on obtient bien deux fichiers vmlinuz-3.2.0-4-amd64 et debirf-kadeploy-deploy-kernel_wheezy_3.2.0-4-amd64.cgz qui sont respectivement le noyau et l'initrd.
On peut aller observer le contenu pour voir ce qui est réellement installé dans l'initrd associé au noyau de déploiement. On commence par faire une copie de l'initrd original :
# cp /opt/kadeploy-3.3.0.rc8/addons/deploy_env_generation/debirf/kadeploy-deploy-kernel/debirf-kadeploy-deploy-kernel_wheezy_3.2.0-4-amd64.cgz ~/test_initrd
# cd ~/test_initrd
On le décompresse :
# gunzip -dc debirf-kadeploy-deploy-kernel_wheezy_3.2.0-4-amd64.cgz > initrd.cpio
On le désarchive dans le répertoire initrd-rep :
# mkdir initrd-rep
# cd initrd-rep/
# cpio -i < ../initrd.cpio
Puis on décompresse/désarchive la racine de l'initrd :
# gzip -dc rootfs.cgz | cpio -idumv
Pour ajouter des éléments, il vaut mieux passer par la configuration de debirf plutôt que copie et archivage/compression du tout.
2.3. TakTuk
TakTuk est une infrastructure de communication également développée à l'INRIA. Cette infrastructure gère la diffusion de l'information de manière arborescente. Chaque cible de la diffusion peut devenir une source. Si on prend comme exemple la diffusion de la commande hostname sur N machines, cela peut se gérer de trois manières :
- Boucle sur la liste de machines, hostname est exécuté sur la machine 1 puis sur la 2 jusqu'à N ;
- En effectuant un fork du programme lanceur, plusieurs connexions peuvent être gérées en parallèle. Attention aux limites du système en termes de processus par utilisateurs ;
- Diffusion hiérarchique, hostname est exécuté sur la machine 1. La machine 1 se connecte ensuite à la 2 et la 6 pour lancer hostname. La machine 2 se connecte à la 3 et à la 7 et exécute la commande. La machine 6 se connecte à la 8 et à la 9, etc.
Une boite à outils nommée Kanif a été développée au-dessus de TakTuk. Dans cette boîte à outils, on trouve les outils kash (lancement d'une commande sur N nœuds), kaput (copie de fichiers vers N machines) et kaget (récupérer des fichiers de N sources). Ces outils sont des wrappers développés autour de la commande taktuk pour en faciliter l'utilisation. Taktuk est présent dans les dépôts Debian de base :
# apt-get install taktuk
Taktuk est également utilisé par Kadeploy pour propager les commandes sur le cluster.
3. Configuration de kadeploy3d
kadeploy3d est un démon qui accepte les connexions des outils client Kadeploy. L'utilisateur peut interroger kadeploy3d pour lister les images disponibles, en ajouter/supprimer une ou lancer un déploiement.
3.1. Environnement
Pour fonctionner, le serveur kadeploy3d a besoin de se connecter à la base MySQL, de modifier les fichiers du démarrage PXE et définir qui peut interagir avec lui (via les commande ka*). Dans les nouvelles versions de Kadeploy, la communication entre les clients et le serveur se fait en SSL. Un répertoire ssl a été crée pour accueillir les certificats générés avec la commande :
# openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -out ./server.crt -keyout ./server.key
Les permissions doivent être fixées :
# chown -R deploy ../ssl
# chmod 400 server.key
# chmod 444 server.crt
Enfin, il faut créer une clé SSH sans mot de passe pour que le serveur Kadeploy puisse interagir avec les machines dont il gère le déploiement. Cette clé sera utilisée dans le système déployé pour, par exemple, le redémarrer en vue d'un redéploiement. Elle sera également présente dans le noyau de déploiement pour que kadeploy3d puisse effectuer les actions d'installation. Les clés sont stockées dans le répertoire keys.
# ssh-keygen -b 2048 -t rsa -f /etc/kadeploy3/keys/id_deploy
3.2 Configuration du démon
La configuration du service se fait dans le fichier server.conf. Les paquets sont fournis avec des fichiers d'exemples comprenant un certain nombre de paramètres préconfigurés. L'idée est de ne modifier que les attributs propres à notre installation. Commençons par la section base de données (cf. 2.2) :
database:
name: deploy3
kind: mysql
host: localhost
login: deploy
passwd: kadeploy
Ce sont les paramètres classiques : nom de la base de données (name), le type (kind), la machine sur laquelle le serveur de base de données est en exécution (host), le nom d'utilisateur dédié à l'accès à la base (login) et le mot de passe associé (passwd).
La seconde grosse partie à configurer est le PXE. Cette section décrit au serveur Kadeploy l'organisation du service de démarrage PXE (pxe) :
pxe:
dhcp:
method: PXElinux
repository: /srv/tftp
export:
kind: tftp
server: 192.168.40.4
profiles:
directory: pxelinux.cfg
filename: ip_hex
Il faut d'abord indiquer que l'amorçage du noyau de déploiement se fait depuis un service DHCP (dhcp) via PXElinux (method). On indique ensuite l'emplacement des fichiers de démarrage (repository). Comme ici la méthode de récupération de ces fichiers est TFTP (kind) depuis un serveur distant (server), cela revient à indiquer la racine du service TFTP. Enfin, le répertoire contenant les fichiers de configuration des machines démarrant sur PXE (directory) ainsi que la convention de nommage de ces fichiers (filename) sont renseignés. Pour mémoire, le répertoire indiqué dans profiles comme valeur de l'attribut directory doit être accessible en écriture à l'utilisateur deploy.
Attention à la section external / mkfs, il faut ajouter un -q aux variables args. En effet, si la sortie d'une commande est trop verbeuse, cela fait planter l'infrastructure de communication TakTuk (cf 3.3) :
mkfs:
- args: -b 4096 -O sparse_super,filetype,resize_inode,dir_index -q
fstype: ext4
La communication peut se faire de façon chiffrée (via SSL) ou en clair. Pour activer le SSL, il faut ajouter un certificat auto signé :
# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -out ./server.crt -keyout ./server.key
On peut mettre ce que l'on veut dans le CN, car SSL est juste utilisé pour le chiffrement de la communication et non pour l'authentification du serveur Kadeploy auprès du client. Il faut ensuite l'activer côté serveur (security) :
security:
secure_server: true
local_only: true
certificate: /etc/kadeploy3/ssl/server.crt
private_key:
algorithm: RSA
file: /etc/kadeploy3/ssl/server.key
Ici on accepte seulement des requêtes locales à la machine (local_only) en SSL (secure_server). On lui donne l'emplacement du certificat (certificate) et de la clé privée (private_key / file). Il existe également des possibilités d'authentification du client auprès du serveur. On distingue deux niveaux : machine et identité de l'utilisateur. Au niveau machine, on peut autoriser n'importe quel utilisateur d'une machine à lancer des commandes Kadeploy :
authentication:
acl:
whitelist:
- localhost
Dans cet exemple, tout ce qui vient de l'hôte local est autorisé (cohérent avec le local_only à true). On parle d'une authentification au niveau machine. Pour authentifier un utilisateur, Kadeploy propose 3 méthodes : htpasswd, ident et les certificats. Chacune de ces méthodes propose de définir une whitelist de machines autorisées, quel que soit l'utilisateur qui lance la commande Kadeploy. On notera que cette authentification n'est pas du tout destinée à autoriser ou pas un utilisateur à déployer une image, mais plutôt à définir qui est autorisé à interagir avec le serveur Kadeploy. L'autorisation de qui peut déployer quoi et où sera discutée dans la section 4.4.
3.3. Configuration d'un template de machines
Un template de machines est un type de machines à configurer. Sur le site où kadeploy est utilisé, nous avons deux templates : un pour les nœuds de calculs et un autre pour les nœuds GPU. La différence entre les deux étant la présence des librairies CUDA sur le nœud GPU. Cette association entre machines physiques et templates se fait dans le fichier clusters.conf :
---
clusters:
- name: common
conf_file: /etc/kadeploy3/cluster-common.conf
nodes:
- address: 192.168.40.10
name: magi10
- address: 192.168.40.11
name: magi11
Ce fichier définit une suite de clusters. Chaque cluster possède un nom (name), un fichier de configuration (conf_file) et une suite de machines (nodes). Chaque machine est identifiée par une adresse IP (address) et un nom (name). Passons au fichier cluster-common.conf qui définit le template pour tous les nœuds de calcul.
La première chose à faire est de décider du plan de partitionnement des machines. Celui des nœuds de calcul est très simple. Il est composé de quatre partitions primaires : / de 30 Gi, swap de 12 Gi, /tmp de 2 Gi et /scratch de la taille restante. Cela se répercute de la façon suivante dans le fichier de configuration :
partitioning:
partitions:
tmp: 3
deploy: 1
swap: 2
prod: 6
disable_swap: false
block_device: /dev/sda
script: parted-common
La section partitioning contient une liste de partitions. Chaque partition est identifiée par un numéro. Ce numéro correspond au numéro de périphérique attaché à la partition. Par exemple, pour un disque dur sur /dev/sda, le chiffre 1 correspond à /dev/sda1. D'après la configuration, nous avons /dev/sda3 monté sur /tmp, le swap sur /dev/sda2, le / du système déployé sur /dev/sda1 et une partition prod sur /dev/sda6. Or il n'a jamais été question d'une telle partition dans le plan de partitionnement composé de 4 partitions primaires (une partition numérotée 6 est donc un non-sens). Pour comprendre, il faut rappeler que Kadeploy a été développé pour déployer des systèmes expérimentaux sur du vrai matériel à large échelle. La configuration standard est d'avoir sur un nœud un système de production et un système expérimental déployé par l'utilisateur. Lorsqu'un nœud est réservé pour une expérimentation, Kadeploy redémarre le système de production pour lancer le déploiement du système à tester sur la partition deploy et modifie la séquence de démarrage pour amorcer sur ce nouveau système.
La section précédente ne fait que décrire le partitionnement. Pour le réaliser de manière effective, il faut créer (ou adapter) un script de partitionnement (script). Dans notre exemple, il s'agit du fichier parted-common :
#!/bin/bash -e
PARTED_OPTS="--script -a optimal"
UNIT="GB"
function do_parted()
{
/sbin/parted $PARTED_OPTS $KADEPLOY_BLOCK_DEVICE unit $UNIT $@
}
do_parted "mklabel msdos"
do_parted "mkpart primary ext4 0% 30"
do_parted "mkpart primary linux-swap 30 42"
do_parted "mkpart primary ext4 42 44"
do_parted "mkpart primary ext4 44 100%"
do_parted "toggle $KADEPLOY_DEPLOY_PART_NUM boot"
do_parted "align-check optimal 1"
do_parted "align-check optimal 2"
do_parted "align-check optimal 3"
do_parted "align-check optimal 4"
/sbin/partprobe $KADEPLOY_BLOCK_DEVICE
Ce script définit les options à passer à parted (PARTED_OPTS) et l'unité de stockage par défaut (UNIT). Une fonction do_parted est ensuite créée pour invoquer la commande parted avec les bons arguments. Cette fonction est ensuite utilisée pour créer un partitionnement de type msdos (mklabel msdos) et 4 partitions primaires (trois en ext4 et une swap). Chaque ligne est construite sur le même modèle : invocation de la fonction do_parted suivie du type de partition (mkpart primary ext4), du début et de la fin de celle-ci. On a donc une partition démarrant à 0% (c'est-à-dire au début du disque) et s’arrêtant à 30GB (/) suivie d'une partition démarrant à 30GB et s’arrêtant à 42GB soit 12GB pour le swap, puis une allant de 42GB à 44GB soit une taille de 2GB (/tmp) et enfin une partition de la taille restante, c'est-à-dire de 44GB à 100% (/scratch). La partition deploy est ensuite définie comme partition bootable (toggle). Les partions sont ensuite alignées de manière optimale (align-check optimal) et le système est notifié du nouveau partitionnement via la commande partprobe.
La section suivante configure le démarrage de la machine (boot) :
boot:
install_bootloader: install_grub2
kernels:
deploy:
initrd: kernels/initrd
params: console=tty0 console=ttyS0,38400n8 rw
vmlinuz: kernels/vmlinuz-3.2.0-4-amd64
user:
params: console=tty0 console=ttyS0,38400n8
On commence par configurer le gestionnaire de démarrage de la machine à déployer (install_bootloader). Ici il s'agit de GRUB2, le script install_grub2 est récupérable dans l'archive .tar.gz de Kadeploy. La deuxième partie concerne la configuration du noyau de déploiement (deploy) et du système déployé (user). Pour le noyau de déploiement, on définit l'emplacement de l'initrd (initrd) et du noyau (vmlinuz) relativement par rapport à la racine du serveur TFTP (la racine ayant été définie dans le fichier server.conf). Pour les deux noyaux, on donne la liste des paramètres à utiliser pour le démarrage (params).
Une autre information que Kadeploy doit posséder c'est comment procéder à l'allumage, à l'arrêt et au redémarrage des machines. Ces informations sont présentes dans la section remoteops du fichier de configuration :
remoteops:
reboot:
- name: soft
cmd: ssh -A -q -o BatchMode=yes -o StrictHostKeyChecking=no -o PreferredAuthentications=publickey -o ConnectTimeout=2 -o UserKnownHostsFile=/dev/null -i /etc/kadeploy3/keys/id_deploy root@HOSTNAME_SHORT /sbin/reboot
- name: hard
cmd: ipmitool -vI lan -H HOSTNAME_SHORT-ipmi -U root -f /etc/kadeploy3/ipmi/pass chassis power reset
Nous allons juste détailler la partie relative au redémarrage (reboot), les sections allumage (power_on) et arrêt (power_off) sont construites sur le même modèle. Pour le redémarrage, nous avons deux possibilités ayant chacune un nom (name) et une commande associés (cmd). Le nom est une sorte de niveau de la commande à invoquer. On distingue trois niveaux : soft, hard et very hard. Ici, soft correspond à une commande SSH : Kadeploy essaye de redémarrer la machine en envoyant une commande reboot via SSH. La commande hard est une commande IPMI exécutant un arrêt/redémarrage du châssis. IPMI est un contrôleur indépendant sur la machine permettant d'envoyer des ordres à la partie matérielle du serveur (arrêt du châssis, remontées de températures, ouvrir une console virtuelle, etc.). Le niveau very hard n'est pas configuré, mais pourrait interagir avec le PDU.
Enfin, il est également possible de définir des actions pré ou post installation. Sur notre installation, nous avons juste un script qui s’exécute post-installation (postinstall) :
postinstall:
files:
- file: /store/postinst.tgz
format: tgz
script: launch.sh
Plusieurs fichiers peuvent être définis (files). Chaque entrée file correspond à une archive compressée (file) dans un certain format (format). Le script à exécuter à l'intérieur de l'archive doit être précisé (script). Dans notre exemple, le script launch.sh de l'archive compressée postinst.tgz est exécuté. Le fichier launch.sh doit être à la racine de l'archive :
# tar -czf postinst.tgz launch.sh
3.4 Lancement du premier déploiement
La configuration touche à sa fin. Il faut maintenant créer une image à déployer, créer le fichier de configuration de cette image et donner le droit à l'utilisateur de déployer cette image. Commençons par créer une image. Nous partons d'une image Debian Wheezy tout à fait standard.
L'INRIA met à disposition un outil, tgz-g5k [5] qui remonte un système de fichiers en lecture seule afin de créer une archive compressée en excluant certains fichiers. Cet outil est à installer et à exécuter sur le système dont on souhaite faire une image. La documentation d'installation est assez complète. Une fois l'outil installé, la commande est simple :
# tgz-g5k upload@kadeploy:/store/wheezy-hpc.tgz
Cette commande crée une image du système dans l'archive compressée wheezy-hpc.tgz directement sur le serveur kadeploy via SSH en utilisant l'utilisateur upload. Une fois cette image créée, il faut lui ajouter un petit fichier de description (par exemple wheezy-hpc.desc) :
name: wheezy-hpc
version: 1
description: wheezy-hpc
author: nicolas greneche
visibility: shared
image:
file: /store/wheezy-hpc.tgz
kind: tar
compression: gzip
boot:
kernel: /boot/vmlinuz-3.2.0-4-amd64
initrd: /boot/initrd.img-3.2.0-4-amd64
partition_type: 0x83
filesystem: ext4
os: linux
Les paramètres sont assez explicites pour le nom (name), la version (version), la description (description) et l'auteur (author). La visibilité (visibility) propose 3 valeurs :
- privé (private) : seul l'auteur (author) peut l'utiliser ;
- partagée (shared) : tout le monde peut l'utiliser, seulement le nom de l'auteur doit être spécifié dans les commandes ;
- publique (public) : tout le monde peut l'utiliser sans restriction.
La section image renseigne l'emplacement physique de l'archive compressée du système à déployer sur le système de fichiers (file), le type d'archive (kind) et le type de compression (compression). La section boot sert à localiser le noyau (kernel) et l'initrd (initrd) à l’intérieur de l'archive. On termine par le type de partition sur laquelle le système sera déployé (partition_type), le type de système de fichiers à instancier sur cette dernière (filesystem) et la famille de système d'exploitation (os). L'image doit ensuite être enregistrée via la commande kaenv3 :
# kaenv3 -a wheezy-hpc-v4.desc
Et on vérifie :
# kaenv3 -l
Name Version User Description
#### ####### #### ###########
wheezy-hpc 1 root wheezy-hpc
Il ne reste plus qu'à spécifier que notre utilisateur peut demander un déploiement :
# karights3 -a -u root -p /dev/sda1 -m magi[10-50]
Cela veut dire que potentiellement root peut déployer une image sur la partition /dev/sda1 des machines magi10 à magi50.
Lançons maintenant le déploiement :
# kadeploy3 -e wheezy-hpc-v4 -m magi10
Deployment #D-aff57e2e-bdc8-4d5f-a132-47b3f6352d32 started
Grab the tarball file /store/wheezy-hpc.tgz
Launching a deployment on magi10
Performing a Deploy[SetDeploymentEnvUntrusted] step
switch_pxe
reboot
* Performing a soft reboot on magi10
* Performing a hard reboot on magi10
wait_reboot
create_partition_table
format_deploy_part
mount_deploy_part
format_swap_part
End of step Deploy[SetDeploymentEnvUntrusted] after 85s
Performing a Deploy[BroadcastEnvChain] step
send_environment
* Broadcast time: 163s
manage_admin_post_install
manage_user_post_install
check_kernel_files
install_bootloader
sync
End of step Deploy[BroadcastEnvChain] after 213s
Performing a Deploy[BootNewEnvClassical] step
switch_pxe
umount_deploy_part
reboot_from_deploy_env
wait_reboot
End of step Deploy[BootNewEnvClassical] after 82s
End of deployment for magi10 after 380s
End of deployment on cluster common after 380s
Deployment #D-aff57e2e-bdc8-4d5f-a132-47b3f6352d32 done
The deployment is successful on nodes
magi10
Le déploiement s'est déroulé de la manière suivante :
1. Redémarrage soft de la machine à déployer. C'est un échec, car la clé SSH de Kadeploy n'est pas installée sur le système cible. Kadeploy tente donc un redémarrage hard par IPMI ;
2. Une fois la machine redémarrée, les partitions sont créées. Le système de fichiers ext4 et instancié sur /dev/sda1 et le swap sur /dev/sda2 ;
3. L'archive du système à déployer est envoyée sur la machine ;
4. Les tâches post-installation sont réalisées (nous y reviendrons dans la section suivante) ;
5. Installation du système de démarrage ;
6. Démontage de la partition du système et redémarrage ;
7. Vérification de la disponibilité de la machine redéployée ;
À ce stade, nous disposons d'une machine fonctionnelle déployée avec Kadeploy. Ce déploiement est essentiellement binaire, mais propose d'utiliser des scripts de pré/post-installation. Nous allons utiliser cette possibilité pour coupler Kadeploy et Puppet.
4. Post-installation avec Puppet
Dans notre procédure d'installation, l'appel à Puppet se fait juste après le déploiement du système et le redémarrage sur ce dernier. Au moment où Puppet s’exécute, nous avons donc l'archive du système à déployer décompressée dans /mnt/dest. L'objectif est de combiner Puppet et Kadeploy. Cela pose plusieurs problèmes. Nous souhaitons garder les clés SSH d'un hôte à travers ses redéploiements pour éviter de mettre à jour sans cesse le known_hosts ainsi qu'une configuration réseau Ethernet et Infiniband statique.
4.1 Gestion des certificats avec Kadeploy et Puppet
Un problème se pose lorsque Puppet et Kadeploy sont combinés. Pour bien comprendre, lorsque Puppet est lancé pour la première fois sur le client, il génère un certificat qu'il envoie au serveur qui le signe. Cette authentification bilatérale empêche un client non autorisé de récupérer une configuration par la suite. La conséquence niveau déploiement est que la partie cliente doit être « impersonnifiée » (c'est-à-dire que dans l'image de référence il faut retirer le certificat de la machine, la clé privée et le certificat du serveur Puppet). Cette partie est simple, le script tgz-g5k est très bien fait pour ça. Il suffit de noter les fichiers à exclure de l'image dans le fichier dismissed :
# cat /usr/local/share/tgz-g5k/dismissed | grep puppet
etc/puppet/ssl/certificate_requests/*
etc/puppet/ssl/public_keys/*
etc/puppet/ssl/*
etc/puppet/ssl/private_keys/*
etc/puppet/ssl/certs/*
Pour la suppression du certificat de la machine côté serveur Puppet, il faut invoquer la commande suivante :
# puppet cert clean magi11.bullx
Le meilleur moment pour faire cette action est lors du déploiement. Du moment que l'on décide de redéployer un hôte, son certificat sur le serveur doit être effacé et révoqué. Seulement Kadeploy ne propose pas de hook permettant de faire lancer une commande au service kadeploy3d.
Nous avons choisi de gérer ce problème en faisant faire une connexion SSH à l’environnement de déploiement déclenchant une ForcedCommand qui invalide le certificat sur le serveur Puppet. On génère une clé SSH sur le serveur Puppet :
# ssh-keygen -t rsa -b 2048 -f /etc/puppet/keys/puppet
On ajoute cette clé dans le authorized_keys de l'utilisateur puppet :
# cat /etc/puppet/keys/puppet.pub >> /var/lib/puppet/.ssh/authorized_keys
On rédige un script à utiliser en ForcedCommand qui va invalider le certificat du client sur le serveur Puppet/var/lib/puppet/scripts/clean.sh :
#!/bin/bash
case $SSH_ORIGINAL_COMMAND in
"/usr/bin/puppet cert clean "*)
$SSH_ORIGINAL_COMMAND
echo "$SSH_ORIGINAL_COMMAND" > /tmp/test.txt
;;
*)
echo "Permission denied."
exit 1
;;
esac
La variable d’environnement SSH_ORIGINAL_COMMAND contient la commande passée en argument de la connexion SSH. L'idée du script est de n'autoriser que le passage d'une commande /usr/bin/puppet cert clean quelque_chose. On pourrait l’améliorer en faisant une requête DNS de quelque_chose pour voir s'il s'agit bien d'un FQDN.
On doit ensuite forcer cette commande côté serveur SSH :
# tail -n 2 /etc/ssh/sshd_config
Match User puppet
ForceCommand /var/lib/puppet/scripts/clean.sh
Enfin, on ajoute la connexion SSH au script de déploiement (fichier launch.sh de la section 4.3) :
ssh -A -q -o BatchMode=yes -o StrictHostKeyChecking=no -o PreferredAuthentications=publickey -o ConnectTimeout=2 -o UserKnownHostsFile=/dev/null -i /etc/puppet/keys/puppet puppet@puppet "/usr/bin/puppet cert clean $dns_name"
4.2 Puppet en post-installation d'un environnement Kadeploy
Grâce à la section précédente, les certificats sont nettoyés et Puppet va pouvoir s’exécuter sur le système à déployer. Le système à déployer est décompressé dans /mnt/dest. Puppet est installé dans le système à déployer. Nous allons donc exécuter la commande puppet agent en la chrootant dans /mnt/dest.
Premier piège : il faut penser à ajouter le montage de proc et dev dans le chroot avant de lancer la commande puppet agent sinon tous les attributs renseignés par facter ne seront pas disponibles (par exemple les IP attachées aux interfaces). De plus, comme on génère des certificats, il faut que tous les périphériques de génération d'entropie soient accessibles dans le chroot.
Second piège : ajouter un wait après la phase de chroot. Le principe du wait est de dire au père d'attendre la mort du fils pour sortir du wait. Si on ne le fait pas, le déploiement échoue, car le script de post-installation se termine avant que le processus fils exécutant la commande chrootée n'ait terminé. Voici les lignes de commandes à ajouter dans le script launch.sh :
#!/bin/bash
...
mount -t proc none /mnt/dest/proc
mount --rbind /dev /mnt/dest/dev
chroot /mnt/dest /usr/bin/puppet agent --onetime --no-daemonize
wait
mount /mnt/dest/proc
mount /mnt/dest/dev
Conclusion
Kadeploy est un outil qui nécessite quelques bases au niveau administration système pour donner sa pleine mesure. Une fois ces bases maîtrisées, sa conception modulaire permet toutes les fantaisies. Chaque étape du déploiement est configurable. De nombreux hooks sont disponibles pour ajouter une commande ou un script. C'est un outil issu du monde de la recherche ayant parfaitement intégré les besoins de la production. De plus, ce logiciel libre est développé en France. De quoi en être fier, non ?
Références
[1] Grid'5000 : https://www.grid5000.fr/mediawiki/index.php/Grid5000:Home
[2] Performances de Kadeploy sur les déploiements à large échelle : http://hal.inria.fr/docs/00/70/09/62/PDF/kadeploy-scale2012.pdf
[3] Paquets Debian Kadeploy : http://kadeploy3.gforge.inria.fr/
[4] Infrastructure de communication TakTuk : http://taktuk.gforge.inria.fr/
[5] tgz-g5k : https://www.grid5000.fr/mediawiki/index.php/TGZ-G5K