Déployer des machines virtuelles avec Ansible (VMware)

Posted by

Déployer des VMs via Ansible ?

Depuis que j’automatise tout ce qui peut l’être sur mes machines virtuelles avec l’aide d’Ansible, il y avait une corde qui manquait encore à mon arc.

Jusqu’à présent, j’utilisais Ansible simultanément en tant qu’outil de déploiement de logiciel et aussi comme logiciel de « Configuration Management ».

Je m’explique :

  • A partir d’un template de machine virtuelle, je suis capable de déployer une configuration par défaut en fonction du rôle de la machine (au sens large du terme, que ce soit des fichiers ou de l’installation de packages).
  • Si jamais une modification de la configuration est faite, je suis capable de « pousser » cette modification sur toutes les machines d’un seul coup.
  • Et enfin, si jamais des modifications ont été réalisées (par exemple en cas d’erreur) sur une partie de mes machines, je suis capable de « corriger » tous les serveurs « non-compliant » SANS TOUCHER à ceux qui le sont (car mes playbooks sont idempotent, comme mon module pour centreon).

Pour autant, j’étais frustré de ne pas avoir encore trouvé le moyen d’aller encore un peu plus loin : provisionner les machines directement depuis Ansible.

Pourquoi ça coince

2 obstacles majeurs m’empêchaient d’aller au bout de la démarche.

Gather facts

D’abord parce que par défaut, si vous donnez une liste de serveurs à Ansible dans votre inventaire avec pour but de les déployer, Ansible va tenter de les se connecter à ces machines… qui n’existent pas encore !

ansible-playbook -l 192.168.100.103 zwindler_deploy.yml

PLAY [xxxxxxaaaaaaaaaaa] ********************************************************************************************************************************************************************

TASK [Gathering Facts] **********************************************************************************************************************************************************************
fatal: [192.168.100.103]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 192.168.100.103 port 22: No route to host\r\n", "unreachable": true}

PLAY RECAP **********************************************************************************************************************************************************************************
192.168.100.103 : ok=0 changed=0 unreachable=1 failed=0

Dans ce cas précis, il faut donc dire à Ansible de ne pas essayer de s’y connecter dans un premier temps. J’ai mis un moment à trouver. Dans certains cas (comme pour alimenter un serveur centreon avec CLAPI), j’ai utilisé la fonction delegate_to.

Mais le mieux dans le cas présent consiste à utiliser les options suivantes au début de votre playbook :

gather_facts: false
connection: local

Grâce à ces deux options, vous pourrez indiquer à Ansible de ne pas faire l’opération gather_facts, étape préliminaire à l’exécution de tout playbook, et de ne même pas essayer de se connecter sur les futurs serveurs mais de tout exécuter localement.

Problem solved.

vsphere_guest versus vmware_guest

Mon deuxième problème était qu’il existe 2 groupes de modules pour VMware dans les modules Ansible, et que l’un des deux est abandonné ! Et bien sûr comme c’est le plus ancien, c’est celui qui remonte le plus dans les exemples/articles sur Internet ;-).

Dans un premier temps, j’avais fais des tests avec vsphere_guest. Bien que « fonctionnel », ce module intégré à la suite Ansible en version 1.6 nécessite en dépendance python et surtout pysphere. Or, pysphere n’est plus maintenu depuis 2013 !

Dans l’absolu, on peut très bien utiliser vsphere_guest pour provisionner des machines virtuelles. La dernière version normalement pour vSphere 5.0 fonctionne parfaitement sur vSphere 6.X. Un très bon exemple de ce que vous pouvez faire avec ce module est expliqué dans un article de EverythingShouldBeVirtual.

Pour autant, vous ne pourrez pas :

  • Réaliser de modifications sur la machine virtuelle si vous la copiez depuis un template
  • Réaliser des customisations côté OS

Personnellement, je travaille massivement avec des templates, qui sont ensuite agrémentés par des customisations via des playbooks Ansible. Mais j’ai besoin de customiser ces templates en fonction du contexte et je rechigne à faire la modification en 2 étapes. De plus, une fois déployée, j’ai quand même besoin de passer manuellement sur chacune des machines pour leur donner une adresse IP et un hostname unique par exemple.

Heureusement, VMware ne s’est pas contenté du VIPerl et de PowerCLI ! Depuis quelques années, il existe un module Python, maintenu par VMware, et qui est régulièrement étendu avec les nouvelles fonctionnalités de l’API : pyvmomi.

A partir de cet API python, un module Ansible plus récent a donc été créé, et vous allez le voir, les possibilités sont bien plus étendues.

Et la lumière fut

La liste des modules Ansible qui utilisent le module pyvmomi est assez impressionnant. Il s’agit de tous les modules qui commencent par vmware_ (vous pouvez trouver la liste sur la documentation officielle d’Ansible, dans la section VMware).

Deux d’entre eux ont particulièrement retenus mon attention, même s’il y en a plein d’autres intéressants :

  • vmware_guest qui permet de gérer les machines virtuelle
  • vmware_vm_shell qui permet d’exécuter des commandes directement sur l’OS invité de la VM (si elle dispose des VMware Tools)

Prérequis

Pour exécuter ces modules, vous avez besoin de 2 choses :

  • pyvmomi et ces dépendances (python 2.6+ et sphinx)
pip install pyvmomi sphinx
  • Ansible 2.2 minimum (mais plutôt 2.3 voire 2.4 !)

Sur le papier, le module « fonctionne » avec Ansible 2.2, l’actuelle version stable que vous pourrez trouvez sur tous les dépôts. Sauf que la plupart des fonction intéressantes n’apparaissent qu’avec la version 2.3 d’Ansible, pour l’instant uniquement disponible si vous compilez les sources !

La version disponible en ce moment est la 2.3.1. Il n’est donc plus nécessaire de compiler les sources, SAUF si vous voulez utiliser les nouvelles fonctionnalités du modules introduites dans la 2.4. Je laisse la procédure de compilation en fin d’article.

Et maintenant le playbook !

Voici un exemple de playbook qui :

  • Prompte pour un mot de passe administrateur pour se connecter au vCenter
  • Déploie une machine virtuelle depuis template

Ce déploiement dépend d’un certain nombre de variables qui peuvent être renseignées dans le fichier hosts tels que :

  • la taille du disque virtuel
  • le nombre de vCPU
  • la quantité de vRAM
  • l’ESXi ou le datastore destination
  • l’adresse IP de la VM
  • Une fois la VM créée, l’adresse IP et le hostname est configurée dans l’OS invité
cat zwindler_vmware_add_normal.yml
---
- hosts: all
gather_facts: false
connection: local

vars_prompt:
- name: "vsphere_password"
prompt: "vSphere Password"
- name: "notes"
prompt: "VM notes"
private: no
default: "Deployed with ansible"

tasks:
# get date
- set_fact: creationdate="{{lookup('pipe','date "+%Y/%m/%d %H:%M"')}}"

# Create a VM from a template
- name: create the VM
vmware_guest:
hostname: '{{ vsphere_host }}'
username: '{{ vsphere_user }}'
password: '{{ vsphere_password }}'
validate_certs: no
esxi_hostname: esxi_server
datacenter: 'ZWINDLER'
folder: A_DEPLOYER
name: '{{ inventory_hostname }}'
state: poweredon
guest_id: rhel7_64Guest
annotation: "{{ notes }} - {{ creationdate }}"
disk:
- size_gb: 150
type: thin
datastore: '{{ vsphere_datastore }}'
networks:
- name: server_network
ip: '{{ custom_ip }}'
netmask: 255.255.252.0
gateway: 192.168.100.1
dns_servers:
- 192.168.100.10
- 192.168.101.10
hardware:
memory_mb: 4096
num_cpus: 2
customization:
dns_servers:
- 192.168.100.10
- 192.168.101.10
domain : zwindler.fr
hostname: '{{ inventory_hostname }}'
template: tmpl-rhel-7-3-app
wait_for_ip_address: yes

- name: add to ansible hosts file
lineinfile:
dest: /etc/ansible/hosts
insertafter: '^\[{{ ansible_host_group }}\]'
line: '{{ inventory_hostname }}'

A partir là, vous pouvez tout faire :)

Juste une petite limitation que je n’ai malheureusement pas encore pu contourner : à la suite de la création de la VM, vous ne pourrez pas « enchainer » sur un autre playbook (par exemple pour vous connecter sur la machine pour ajouter un service). Vous devrez passez par un second playbook.

La raison principale à ceci est qu’on a spécifié « connection local » et « gather facts » à faux pour que ça ne plante pas au début. Du coup le playbook ne s’exécuterait pas sur la bonne machine.

EDIT : la solution à ce problème est disponible sur l’article suivant !

Liens utiles

Le module pause peut être utile dans certain cas, même si je n’en suis pas fan dans un environnement type « production ».

Egalement, je vais bientôt faire un article sur le module dédié a Proxmox dans les semaines qui viennent !!

Nouvelles fonctionnalités

En attendant donc que la 2.3 2.4 soit distribuée partout, voici l’erreur que vous risquez d’avoir si vous utilisez les fonctions linked_clone ou snapshot_src :

ansible-playbook zwindler_vmware_add_micro.yml
vSphere Password:
VM notes [Deployed with ansible]:

PLAY [ansible_node] **********************************************************

TASK [create the VM] ***********************************************************
fatal: [ansible_node]: FAILED! => {"changed": false, "failed": true, "msg": "unsupported parameter for module: linked_clone"}

PLAY RECAP *********************************************************************
ansible_node : ok=0 changed=0 unreachable=0 failed=1

Voici la marche suivre pour compiler et installer la version 2.4 d’Ansible via les sources :

git clone https://github.com/ansible/ansible

cd ansible
make
make install

ansible --version
ansible 2.4.0

Vous aimez ce blog ? Partagez-le avec vos amis !   Twitter Facebook Linkedin email

Vous pouvez également soutenir le blog financièrement :
Tipeee

35 comments

    1. Je m’en doutais… ça c’est parce que j’ai mis trop de temps à sortir l’article ;-)

      Je pense quand même le laisser (peut être en annexe) pour ceux qui voudraient compiler la 2.4 puisqu’elle donne des fonctionnalités supplémentaires sur le module en question.

      Merci pour le retour en tout cas :)

  1. en utilisant le module « meta: refresh_inventory » vous pouvez recharger dynamiquement l’inventory et enchainer dans le meme playbook

  2. Salut,
    Site très sympa tombé dessus par hasard en faisant des recherches.
    Puis je suis tombé sur cet article qui m’intéresse énormément.
    j’aurais aimé savoir comment tu indiquais dans ton fichier hosts tes vm avec RAM … car aucune infos sur la doc Ansible. Merci à toi pour l’info

  3. Si je comprend bien la question, la modification de la RAM peut se faire en utilisant les paramètres dans hardware.
    hardware:
    memory_mb: 4096

    Dans mon exemple je n’ai pas variabilisé la mémoire mais on pourrait très bien imaginer d’en mettre une par exemple :
    memory_mb: ‘{{ guest_ram }}’

    A partir de là, il suffirait de déclarer la variable dans un fichier du dossier group_vars et de mettre l’hôte dans le groupe correspondant, ou bien carrément de le mettre dans le fichier hosts :

    [hotes_a_deployer]
    mon_serveur guest_ram=4096

  4. C’est rigolo, j’ai fais exactement la même chose que toi avec des instances AWS :-)
    Et je butte sur le chainage des playbooks/roles:
    – Playbook pour provisionner l’instance
    – Role pour déployer docker&compose
    – Role pour le conteneur n°1
    – Role pour le conteneur n°2
    – …
    Je cherche du coté des nouveaux modules de la 2.4:
    import_playbook / import_tasks / include_tasks / import_role

    1. Tu vas être content d’apprendre que j’ai la solution alors ;) c’est juste que j’ai pas encore pu écrire dessus => c’est dans la pile des articles en retard :-p
      T’inquiète je te dirai ce que j’ai trouvé ;)

  5. si ca peut aider, pour le chainnage j’ai fais un truc comme ca:
    ce qu’il faut retenir:
    initialisation de la VL seffectue via un role en localhost
    Ajout a chaud dans un inventaire en memoire de l’host a provisionner pour enchainer sur d’autre role plus classique
    par contre les roles doivent commencer par : setup: pour recuperer les fact (si besoin)

    – hosts: newhost
    gather_facts: no
    remote_user: root
    vars_files:
    – « ../../vars/global.yml »
    vars:
    var1: xxx
    var2: xxx
    ….

    pre_tasks:
    – name: « add hostname in inventory »
    local_action: add_host name= »newhost » ansible_ssh_host= »{{ AF_ipv4_admin | ipaddr(‘address’) }} »
    tags: always

    roles:
    – { role: « ../roles/init_vm », delegate_to: localhost }
    – { role: « ../roles/update_rpm », tags: ‘rpm’ }
    – { role: « ../roles/etx… », tags: ‘register’ }

  6. C’est un superbe article ! merci beaucoup !
    Par contre quand je relance un playbook de génération de VM, au lieu de reprendre la VM existante et de la vérifier une nouvelle VM est créée, tu as déjà eu ce problème ?

    1. En effet, le playbook ne semble pas être idempotent ! Côté VMware j’ai une erreur « Création d’une machine virtuelle dans l’inventaire : le nom ‘ma_VM’ existe déjà » et côté Ansible j’ai un « failed »: true, « msg »: « The name ‘********’ already exists. »

      A mon avis c’est une erreur dans le design du module, il faudrait ouvrir un ticket sur Github pour leur demander si c’est normal (je verrai si j’ai le temps de le faire, j’ai gardé les traces).

  7. On est d’accord pour la partie « pas idempotent », par contre comme je le disais je n’ai pas le même problème que toi. Moi il ne me créé pas une nouvelle VM, il me claque une erreur :
    « failed »: true, « msg »: « The name ‘hostname_de_mon_guest’ already exists. »

    Est ce que c’est parce que je suis en 6.0 et toi en 5.5 côté vSphere ?

    Dans les deux cas, ce n’est pas idempotent : il faudrait qu’il vérifie que la machine existe, et si oui, qu’il vérifie la configuration, et si rien n’est changé, mettre un « OK »

  8. Soit c’est ça, soit c’est parce que je suis en ansible 2.3 et toi en ansible 2.4.

    Au passage j’ai essayé la 2.4 : impossible de le faire fonctionner avec du VCenter 5.5.

    1. C’est quoi le message d’erreur quand tu utilises Ansible 2.4 sur vSphere 5.5 ? Je pense que ça vaut le coup d’ouvrir un autre ticket.

  9. En l’occurrence, pas de message d’erreur, tout se passe bien, c’est juste qu’au lieu de reprendre la VM existante il en créé une seconde, ou une troisième, ou une quatrième… Pour lui pas de soucis.

    Je vais ajouter une info à mon propre ticket j’ai l’impression que ça pourrait être lié à la façon dont vmware_guest cherche la VM (mon client gère ses VMs dans des vApps, elles-même dans des vApp) mais rien ne dit que ce soit ça.

  10. Pardon, j’ai mal lu ton dernier post.

    Je n’ai pas tenté plus que ça à vrai dire, j’avais des problèmes de TLS, une histoire de python et par la suite je n’ai pas réussi à corriger un problème avec pyVMOMI

    Sur le coup ça m’avait paru un problème avec ansible mais pour l’avoir rapidement réessayé à l’instant c’est mon installation qui est un peu raide.

  11. Salut.
    Merci pour l’article. Le problème avec le module VMWARE_GUEST c’est qu’il impose de disposer d’un Vcenter, donc d’une licence pour les hôtes ESXi et la VM Vcenter avec sa licence également …
    Effectivement les possibilités sont bien supérieures et les libs basées sur MOMI sont complète (pyvmomi, rbvmomi …).
    A plus

    1. Tout a fait. C’est pareil pour le scripting avec PowerCli ou autre. VMware bride énormément les clients qui utilisent l’ESXi gratuit. On a attendu un moment pour avoir le client web sur ESXi seul, aussi

  12. Salut,
    Je commence à découvrir Ansible et je suis tombé sur ce site. Super sympa d’avoir partagé tes playbooks : ca a l’air efficace.

    Je butte sur un truc et j’aurais voulu avoir ton avis (visiblement c’est purement lié a vCenter/ESX et non pas à Ansible).

    Lors de l’appel de ton playbook, j’ai une erreur :

    fatal: [srv-test.coom.lan]: FAILED! => {« changed »: false, « msg »: « Failed to create a virtual machine : La personnalisation du système d’exploitation client ‘debian9_64Guest’ n’est pas prise en charge dans cette configuration. Les clients Microsoft Vista (TM) et Linux avec Logical Volume Manager (LVM) sont pris en charge uniquement sur la dernière version de l’hôte ESX et la dernière version de VMware Tools. Consulter la documentation du vCenter pour connaître les configurations prises en charge. »}

    Je n’ai rien trouvé sur Internet. Je me demandais si tu avais des pistes ? Pour info :
    – vCenter Windows 6.5
    – ESX 6.5
    – Templates Debian 9.

    D’avance merci !

  13. A priori si on fait confiance au message d’erreur, soit tu n’as pas installé dans ton template les VMware Tools, soit le type Debian9_64Guest n’est pas dans la liste des OS qui bénéficient de la customisation des VMs mais ça serait quand même étonnant car c’est une distrib récente.
    La doc suivante indique aussi qu’il faut avoir Perl d’installé :

    La doc suivante liste le matériel et les fonctionnalités supportées pour Debian 9 64 bits mais je n’ai pas trouvé l’information sur les « guest customisations » :

  14. Salut zwindler,

    Merci pour ta prompte réponse, ainsi que pour ton site Web. J’y ai trouvé des articles vraiment sympa, et la citation concernant les reboots sur les Windows me laisse à penser qu’on a bossé (ou qu’on bosse) dans la même ESN .. ;)

    J’ai trouvé la réponse par moi même en creusant un peu. Voir ici :

    https://partnerweb.vmware.com/programs/guestOS/guest-os-customization-matrix.pdf

    Il semblerait que Debian ne fasse pas partie des distrib qu’il est possible de personnaliser via VMWare .. J’ai testé avec de l’Ubuntu Server (plutôt proche de Debian du coup) et ca fonctionne à merveille.

    Quand j’aurais le temps je testerais en lab de « tricher » en déclarant une machine Debian comme etant de l’Ubuntu, voir si j’arrive à dupper mon vCenter.

    Encore merci à toi !

    1. Merci pour le retour. Et pas bête l’idée de changer le « Guest OS Type » de VMware pour lui faire croire que c’est Ubuntu. A tester !

  15. Je confirme que cela fonctionne, je le fais pour les VM debian, je les declare comme étant des Ubuntu server et la customisation se passe sans soucis.

    1. Super merci pour le retour c’est sympa :-).
      Je vais mettre à jour l’article pour donner l’astuce !

    1. Pour l’ESXi, si le but est de se passer du vcenter, ce n’est pas possible, VMware oblige à un avoir un pour utiliser l’API de cette manière.
      Pour l’IP du vcenter, il suffit de mettre une IP plutôt qu’un nom.

  16. Salut, est-il possible et si oui comment ? de créer avec Ansible uniquement une image VM personnalisé avec certains outils installés dessus pour VirtualBox ?

Leave a Reply

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

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.