Lorsqu'il s'agit d'initialiser une machine virtuelle dans une infrastructure VMWare vSphere, les systèmes Linux sont le parent pauvre. En effet, VMWare a bien intégré SysPrep de Microsoft. Pour Linux, il y a VMWare Guest Tools qui permet un peu d'initialisation. C'est limité au nom d'hôte et aux interfaces réseaux essentiellement. Notamment, VMWare Guest Tools ne permet pas d'injecter une clef publique SSH.
cloud-init
cloud-init est un projet de configuration de système. C'est un agent déployé dans une image de machine virtuelle. Au démarrage du système, cloud-init cherche une configuration et l'applique. Cela inclue la définition du nom d'hôte, la configuration réseau, la création d'utilisateurs, l'injection de clef publique SSH, l'installation de paquets, etc.
En général, cloud-init laisse la main à un outils comme Ansible pour le reste du provisionnement. cloud-init est adapté pour l'amorçage d'une machine à partir d'une image : nom d'hôte, configuration réseau, injection de clef publique SSH.
Le projet cloud-init est un des rares projets Canonical ayant dépassé le microcosme Ubuntu. Il faut dire que le créateur initial est Yahoo. Et que l'idée est en fait d'Amazon pour l'initialisation des machines virtuelles EC2 via un serveur de métadonnées. Ensuite, OpenStack a implémenté cette architecture en s'appuyant sur cloud-init comme agent. À partir de là, le projet cloud-init était bien installé.
Servir les méta-données
Un des enjeux de cloud-init est de récupérer ces fameuses métadonnées : le nom
d'hôte, la configuration réseau et les clefs publiques SSH. Chaque technologie
IaaS a sa propre manière de faire. Chez EC2 et OpenStack, le fameux serveur
magique http://169.254.169.254/
retourne les méta-données ad-hoc de la machine
qui lui demande. Mais le format est différent. cloud-init a un composant appelé
DataSource
responsable de récupérer ces méta-données et des les normaliser.
Plusieurs implémentations sont disponibles. On a donc une source EC2
et une
source OpenStack
.
Pas de ça pour VMWare vSphere. Il faut concevoir soi-même le moyen de passer ces métadonnées à la machine, et les bonnes ! Jusqu'à cloud-init 21.2, il faut forger une image disque ISO avec deux fichiers dedans et nommer le disque CIDATA pour Cloud-Init DATA. Pas franchement le plus pratique.
Depuis cloud-init 21.3, une DataSource
VMWare dédiée est arrivée. L'hébergeur
injecte les méta-données dans les extraOptions
de la machine virtuelle,
encodées en base64. La source VMWare
de cloud-init récupère les méta-données
via le protocole RPC de VMWare Guest Tools. On peut facilement jouer avec ces
extraOptions
avec l'outil vmware-rpctool
:
$ vmware-rpctool 'info-get guestinfo.vmtools.description'
open-vm-tools 11.2.5 build 17337674
$ vmware-rpctool 'info-get guestinfo.ip'
100.64.24.32
$
Mais voilà, cette version 21.3 est sortie le 10 août 2021. Autant dire qu'on ne va pas la voir de si tôt dans les chaumières. Alors que faire ?
Relier VMWare et cloud-init
Une autre source va nous dépanner : NoCloud. Cette source est assez agnostique
comme son nom l'indique. Elle permet de récupérer les données depuis un dossier
ou une URL HTTP. NoCloud cherche notamment les méta-données dans le dossier
/var/lib/cloud/seed/cloud-net/
. En clair, le simple script bash suivant va
interroge l'API RPC de VMWare Guest Tools, décode les données et les stockes au
bon endroit pour la source NoCloud de cloud-init :
#!/bin/bash -eux
mkdir -p /var/lib/cloud/seed/nocloud-net/
vmware-rpctool "info-get guestinfo.metadata" | base64 --decode > /var/lib/cloud/seed/nocloud-net/meta-data
vmware-rpctool "info-get guestinfo.userdata" | base64 --decode > /var/lib/cloud/seed/nocloud-net/user-data
Simple, non ? Les clefs guestinfo.metadata
et guestinfo.userdata
sont celles
utilisées par la source VMWare de cloud-init 21.3.
Reste à exécuter ce script au bon moment. systemd vient à notre aide, en
ajoutant une ligne ExecStartPre
au service cloud-init-local.service
. Pour
cela, ajouter un fichier seed-nocloud-from-guest-infos.conf
dans le dossier
/etc/systemd/system/cloud-init-local.service.d/
avec les deux lignes suivantes
:
[Service]
# Script récupérant les méta-données avec vmware-rpctool
ExecStartPre=/usr/local/sbin/seed-nocloud-from-guest-info
Pensez à recharger systemd avec systemctl daemon-reload
pour prendre en compte
ce fichier. Vérifier que c'est bien le cas avec la commande systemctl cat
:
[root@host ~]# systemctl daemon-reload
[root@host ~]# systemctl cat cloud-init-local
# /usr/lib/systemd/system/cloud-init-local.service
[Unit]
Description=Initial cloud-init job (pre-networking)
...
[Service]
Type=oneshot
ExecStartPre=/bin/mkdir -p /run/cloud-init
ExecStartPre=/sbin/restorecon /run/cloud-init
ExecStartPre=/usr/bin/touch /run/cloud-init/enabled
ExecStart=/usr/bin/cloud-init init --local
...
# /etc/systemd/system/cloud-init-local.service.d/vmware-seeder.conf
[Service]
ExecStartPre=/usr/local/sbin/seed-nocloud-from-guest-info
[root@host ~]#
Avant de redémarrer, s'assurer que la source NoCloud est bien active ! Pour
cela, déposer un fichier /etc/cloud/cloud.cfg.d/nocloud.cfg
avec le contenu:
datasource_list: [NoCloud, None]
Générer les métadonnées
Les méta-données sont au format YAML, encodé en base64.
$ cat metadata.yml
instance-id: monhote
$ cat userdata.yml
#cloud-config
hostname: monhote
ssh_authorized_keys:
- "ssh-ed25519 ..."
runcmd:
# Relancer dhclient pour prendre en compte le nom d'hote.
- dhclient -r
- dhclient -H monhote
final_message: "Configuré par VMWare vSphere"
Encode ça en base64 et gardez ça sous le coude. Naviguer dans VMWare vSphere.
Une fois éteinte, éditer la configuration de la machine virtuelle, dans l'onglet
Options VM. Dans la section Avancé, cliquer sur le lien Modifier la
configuration. Dans la nouvelle boite de dialogue, cliquer sur le bouton
Ajouter des paramètres de configuration.. Renseigner guestinfo.metadata
avec
le contenu de metadata.yml encodé en base64, et de la meme manière le contenu de
userdata.yml encodé en base64 dans un paramètre guestinfo.userdata
.
Pour info, la source VMWare de cloud-init 21.3 demande également de spécifier
l'encodage base64
dans les paramètres guestinfo.metadata.encoding
et
guestinfo.userdata.encoding
. Le script bash n'en tiens pas compte. Voir la
documentation de la source VMWare de
cloud-init
pour plus de détails.
Le grand test
Après avoir injecté les métadonnées dans les extraOptions
de la machine, il
nous faut maintenant redémarrer. On vérifie ensuite les traces:
[root@host ~]# journalctl -u cloud-init-local
-- Logs begin at jeu. 2021-09-16 09:44:09 CEST, end at ven. 2021-09-17 16:42:12 CEST. --
sept. 16 09:44:14 host systemd[1]: Starting Initial cloud-init job (pre-networking)...
sept. 16 09:44:15 host seed-nocloud-from-guest-info[785]: + mkdir -p /var/lib/cloud/seed/nocloud-net/
sept. 16 09:44:15 host seed-nocloud-from-guest-info[785]: + base64 --decode
sept. 16 09:44:15 host seed-nocloud-from-guest-info[785]: + vmware-rpctool 'info-get guestinfo.metadata'
sept. 16 09:44:15 host seed-nocloud-from-guest-info[785]: + base64 --decode
sept. 16 09:44:15 host seed-nocloud-from-guest-info[785]: + vmware-rpctool 'info-get guestinfo.userdata'
sept. 16 09:44:17 host cloud-init[795]: Cloud-init v. 19.4 running 'init-local' at Thu, 16 Sep 2021 07:44:16 +0000. Up 7.99 seconds.
[root@host ~]# cat /var/log/cloud-init.log
2021-09-16 07:44:17,001 - util.py[DEBUG]: Cloud-init v. 19.4 running 'init-local' at Thu, 16 Sep 2021 07:44:16 +0000. Up 7.99 seconds.
2021-09-16 07:44:17,001 - main.py[DEBUG]: No kernel command line url found.
...
2021-09-16 07:44:17,042 - __init__.py[DEBUG]: Looking for data source in: ['NoCloud', 'None'], via packages ['', u'cloudinit.sources'] that matches dependencies ['FILESYSTEM']
2021-09-16 07:44:17,058 - __init__.py[DEBUG]: Searching for local data source in: [u'DataSourceNoCloud']
...
2021-09-16 07:44:17,129 - util.py[DEBUG]: Reading from /var/lib/cloud/seed/nocloud-net/user-data (quiet=False)
2021-09-16 07:44:17,129 - util.py[DEBUG]: Read 1034 bytes from /var/lib/cloud/seed/nocloud-net/user-data
2021-09-16 07:44:17,129 - util.py[DEBUG]: Reading from /var/lib/cloud/seed/nocloud-net/meta-data (quiet=False)
2021-09-16 07:44:17,130 - util.py[DEBUG]: Read 877 bytes from /var/lib/cloud/seed/nocloud-net/meta-data
2021-09-16 07:44:17,130 - DataSourceNoCloud.py[DEBUG]: Using seeded data from /var/lib/cloud/seed/nocloud-net
...
2021-09-16 07:44:17,362 - stages.py[INFO]: Loaded datasource DataSourceNoCloud - DataSourceNoCloud [seed=/var/lib/cloud/seed/nocloud-net][dsmode=net]
...
[root@host ~]#
Et voilà, le script seed-nocloud-from-guest-info
a bien été exécuté. Les
fichiers sont bien pris en compte par cloud-init
. Et d'ailleurs, la résolution
DNS de la machine est correct ainsi que l'accès SSH.
Un playbook tout en un
J'ai mis en œuvre cette solution dans Cornac, l'implémentation libre de AWS RDS développée par Dalibo. Un playbook Ansible pour CentOS 7 configure tout ça de bout en bout : cloud-init.yml. Et vous ? Comment initialisez-vous les machines virtuelles Linux dans votre infrastructure VMWare ?