Archive dans juin 2021

Personnaliser une VM Ubuntu avec Cloud-Init sur vSphere

Cloud-Init semble être l’outil de personnalisation de prédilection pour les OS majeurs visant à être installer sur différents environnement cloud (AWS, Azure, GCP, vSphere, …), il est très puissant, hétérogène mais au début, il est difficile à appréhender.

Les informations de personnalisation sont stockées dans un fichier appelé user-data. Ce fichier est transmis à Cloud-Init grâce à un mécanisme propre à chaque cloud. Dans notre cas, ce sont les VMtools qui vont transmettre le fichier user-data, une fois reçu, Cloud-Init l’exécutera.

J’ai perdu énormément de temps à trouver les étapes minimales pour personnaliser l’OS Ubuntu avec Cloud-Init dans un environnement vSphere. Je recherchais la possibilité de personnaliser l’OS lors des clones à partir de template OVF.

Ci-dessous la procédure que j’utilise pour un Ubuntu 20.04.2 LTS, fraîchement installé et après son premier reboot. J’ai gardé les valeurs par défaut hormis le clavier Français et l’installation de l’option OpenSSH server.

Il faut dire à Cloud-Init de récupérer le fichier de personnalisation user-data via les paramètres OVF de la VM, il y a une étape à faire coté VM et une coté OS.

 

Coté OS :

  • Supprimer le fichier qui met la Datasource à none au lieu d’OVF
    • sudo rm /etc/cloud/cloud.cfg.d/99-installer.cfg
  • Si vous souhaitez que la configuration réseau se fasse, supprimer le fichier qui la désactive
    • sudo rm /etc/cloud/cloud.cfg.d/subiquity-disable-cloudinit-networking.cfg
  • Si vous êtes en DHCP, la VM récupérera toujours la même IP car elle gardera le même machine ID. Pour éviter cela il faut remettre à zéro l’identité de l’OS :
    • sudo truncate -s 0 /etc/machine-id

 

Coté VM :

  • Récupérer le contenu de votre fichier cloud-init user-data et le codé avec base64 :
    • Base64 user-data.yaml
  • A partir du vSphere client, lorsque la VM est arrêtée, Activer les propriétés de l’OVF :
    • Sélectionnez la VM => Configure => vApp Options => vApp Options are disabled … Edit => Cliquer sur “Enable vApp options”
    • Dans l’onglet OVF details => => sélectionner la case VMware Tools
  • A partir du vSphere client, lorsque la VM est arrêtée, ajouter le champ user-data dans les propriétés de l’OVF :
    • => Sélectionnez la VM => Configure => vApp Options => Properties => ADD mettre le Label et Key id  à la valeur user-data.
  • A partir du vSphere client, lorsque la VM est arrêtée, ajouter la valeur au champ user-data dans les propriétés de l’OVF :
    • Sélectionnez la VM => Configure => vApp Options => Properties => SET VALUE, un popup s’ouvre et mettre la clé base64 du contenu du fichier user-data récupérée un peu plus haut

 

 

Maintenant à partir de cette VM, faite directement un clone pour en faire une autre VM ou pour en faire un template. Si vous souhaitez changer le fichier user-data, au moment du déploiement de la VM, changez uniquement la clé base64 par la nouvelle clé.

 

 

Déployer des VMs dans et via Kubernetes

Les applications sont souvent composées de PODs Kubernetes et de VMs. L’exemple le plus commun que l’on retrouve est, une base de données sous forme de VM et le reste de l’application sous forme de PODs. Par reflexe, à tort ou à raison, ce qui nécessite de la persistance de données est mis sous forme de VMs.

La plateforme vSphere with Tanzu est une plateforme permetant elle aussi d’héberger simultanément et nativement des PODs Kubernetes et des VMs.

Jusqu’à présent les VMs et les PODs étaient déployées via des méthodes différentes et connectés sur des réseaux différents, ce qui pouvait engendrer pour les développeurs un délai de mise à disposition d’environnement de développement et des risques d’échecs de connexion. En effet, les développeurs devaient demander à l’équipe qui gère l’infrastructure, le déploiement d’une VM avec une expression de besoin.

Pour diminuer l’impact temps et les risques d’erreur, les équipes d’infrastructure ont mis en place des outils d’automatisation via un système de ticketing ou via un portail self-service pour donner ainsi une certaine autonomie. Le déploiement est bien simplifié mais ce n’est pas encore suffisant car ça implique au développeur l’apprentissage et l’utilisation d’outils supplémentaires et de récupérer les modalités de connexions à la VM déployée. Le portail self-service n’est pas désuet pour autant, il a beaucoup d’autres valeurs comme la gestion de la gouvernance, j’espère avoir l’occasion d’écrire un article dessus pour les détaillées.

Schéma de principe montrant un développeur qui click sur son portail pour déployer une VM qui sera connectée à un réseau.
Ce même développeur utilise la commande Kubernetes kubectl pour déployer ses PODs. Kubernetes utilise son propre réseau.

Depuis vSphere 7U2a il est désormais possible de provisionner des VMs de la même manière que l’on déploie des PODs, en utilisant la commande kubectl de Kubernetes. Pour être plus précis, depuis le début de vSphere with Tanzu (à l’origine ça s’appelait Projet Pacific) il était possible de déployer des Machines virtuelles à partir de Kubernetes, elles étaient cependant réservées aux usages internes de Kubernetes comme pour la création de Tanzu Kubernetes Cluster.

Désormais le développeur peut lui aussi déployer ses propres machines virtuelles, elles seront en plus connectées au même réseau que les pods. La perte de temps et les risques d’erreur sont ainsi éliminés. J’ai fait le test sur mon environnement de démonstration qui est mutualisé avec mes autres collègues, ça prend moins de 3 minutes pour avoir une base MongoDB fraîchement installée à partir d’un Linux Ubuntu complètement vierge.

Schéma de principe montrant un développeur qui utilise fois la commande Kubernetes kubectl pour déployer ses PODs et ses VMs.
Le tout sera connecté au même réseau Kubernetes

 

Quels sont les périmètres de chaque persona ?

Il y en a deux, le fournisseur de ressources et le consommateur. Le fournisseur est l’administrateur de l’infrastructure qui va présenter les ressources à consommer et si nécessaire les caper. Le consommateur est le développeur qui va utiliser ces ressources via Kubernetes pour développer son application.

La personne de l’infrastructure avec son outil habituel (vSphere client), créé un Namespace de ressources, octroi les droits d’accès au développeur, définir les classes de service (nb de CPU, quantité de RAM) auquel le développeur aura le droit d’utiliser et la librairie d’image de VM qu’il pourra utiliser.

Le développeur se connecte via son compte au Namespace fourni et créé ainsi ses fichiers YAML afin de définir ses besoins en ressources pour sa ou ses machines virtuelles et s’ils le souhaitent, il peut la ou les personnaliser afin d’y installer ses outils et les services dont il a besoin.

En résumé, vSphere with Tanzu laisse le choix au développeur d’avoir ses composant applicatifs développés et hébergés sur des PODs ou sur des VMs et ce en utilisant le même outil, le même réseau et la même plateforme. Il y gagne ainsi en temps de déploiement, de développement et en agilité.

 

Si vous souhaitez soulevez le capot, je vous invite à lire cet article : Etapes pour la création de VM via kubectl

Etapes pour la création de VM via kubectl

Pour créer une machine virtuelle avec vSphere with Tanzu via la commande kubectl, il y a des étapes à respecter pour l’administrateur et pour le développeur, elles sont très simples mais ce ne m’a pas empêché de perdre un peu de temps coté personnalisation sur la partie OS.

je vous recommande cet article pour comprendre l’intéret de déployer des VM via Kubernetes : Déployer des VMs dans et via Kubernetes. Le blog de mon collègue : Introducing Virtual Machine Provisioning, via Kubernetes with VM Service | VMware est aussi très bien détaillé.

Dans la dernière partie cet article, je vais apporter quelques précisions sur la partie Content Library et sur la partie YAML. Mais d’abord, revoyons avant les partie à faire coté administrateur et coté développeur

 

Concernant l’administrateur

La première étape consiste à télécharger les images VMs qui sont différentes de celles utilisées pour TKC (Tanzu Kubernetes Cluster aussi appelé Guest Cluster). Les images sont disponibles dans la marketplace VMware, au moment de la rédaction de cet article, il y en a 2 (Ubuntu et Centos), la version Ubuntu actuelle ne permet pas l’utilisation de volume persistent (PVC) car elle est basée sur une version virtual hardware 10 et il faut au minimum une version 12, ce problème va bientôt être corrigé.

Il faut aller sur la marketplace et faire une recherche avec le mot clé « vm service », ça permet de filtrer (un peu) les images compatibles => VMware Marketplace.

Ensuite cliquer sur l’image souhaitée, se connecter avec son compte MyVMware.

Vous avez deux possibilités, la télécharger puis la charger dans une content library locale

ou récupérer l’url de souscription pour créer une content library qui se synchronisera à celle hébergée par VMware.

Une fois l’image chargée ou le lien renseigné, vous devriez avoir une content library de ce type :

Toujours depuis l’interface vSphere, il faut maintenant créer, un namespace, octroyer les droits aux utilisateurs pour qu’ils puissent s’y connecter, affecter la classe de VM, la content library et la classe de stockage, ce qui devrait donner ceci :

 

L’exemple ci-dessus montre une fois le namespace créé, comment affecter une classe de VM, une content library, autoriser les développeurs qui pourront consommer ce namespace, quelle classe de stockage à utiliser et enfin si besoin caper les ressource, CPU, mémoire et stockage.

C’est tout ce qu’il y a à faire coté administrateur d’infrastructure.

 

Concernant le développeur

Il faut un descriptif YAML pour :

  • La configmap qui contient la personnalisation de la VM
  • La création de la VM
  • Le service réseau si vous souhaitez vous y connecter à partir d’un réseau extérieur (optionnel)
  • Le PVC si vous souhaitez utiliser des volumes persistants (optionnel)

Via la commande Kubernetes, le développeur se connecte avec son compte au Namespace fourni, il pourra ainsi lister les classes de services qu’il peut utiliser ainsi que les images qu’il pourra déployer.

kubectl get virtualmachineclass

NAME                  CPU   MEMORY   AGE
best-effort-2xlarge   8     64Gi     22d
best-effort-4xlarge   16    128Gi    22d
best-effort-8xlarge   32    128Gi    22d
best-effort-large     4     16Gi     22d
best-effort-medium    2     8Gi      31d
best-effort-small     2     4Gi      31d
best-effort-xlarge    4     32Gi     22d
best-effort-xsmall    2     2Gi      22d
..
guaranteed-xsmall     2     2Gi      22d

 

kubectl get virtualmachineimage
NAME                                                         VERSION                           OSTYPE                FORMAT   AGE
centos-stream-8-vmservice-v1alpha1-1619529007339                                               centos8_64Guest       ovf      4h8m
ob-15957779-photon-3-k8s-v1.16.8—vmware.1-tkg.3.60d2ffd    v1.16.8+vmware.1-tkg.3.60d2ffd    vmwarePhoton64Guest   ovf      2d19h
ob-16466772-photon-3-k8s-v1.17.7—vmware.1-tkg.1.154236c    v1.17.7+vmware.1-tkg.1.154236c    vmwarePhoton64Guest   ovf      2d19h
ob-16545581-photon-3-k8s-v1.16.12—vmware.1-tkg.1.da7afe7   v1.16.12+vmware.1-tkg.1.da7afe7   vmwarePhoton64Guest   ovf      2d19h
……
ubuntu-20-1621373774638                                                                        ubuntu64Guest         ovf      4h8m

Il peut ainsi créer ses fichiers descriptifs YAML afin de définir ses besoins en ressources pour sa ou ses machines virtuelles et s’ils le souhaitent, il peut la ou les personnaliser afin d’y installer ses outils.

Le fichier descriptif configmap, comprend la personnalisation de la VM. Les 3 champs importants à renseigner pour la personnalisation sont :

  1. Le hostname qui contient le hostname de l’OS
  2. La public-keys, qui contient la clé publique d’un poste à partir duquel on se connectera à l’os en ssh.
  3. La partie user-data est, si vous le souhaitez, l’endroit où on met le contenu du fichier de configuration Cloud Init, il faudra le chiffrer avec la commande base64.

 

cat loeil-du-se-vm-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
    name: loeil-du-se-vm-configmap # Le nom de la ConfigMap, doit être le même que celui référencé dans le YAML de la VirtualMachine
    namespace: loeil-du-se
data:
  # Champs OVF utilisés lors du déploiment de la VM
  hostname: loeil-du-se
  public-keys: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDC4Cclh3rN/l70lBNlwQyK6ZtugxqT/7HeerHZKPSO0hcl5ZWLvz2+7QG5FqvYbkPP6EomvyDhE2MPnQ0kWaIrumVxYXAbVdpBBKKdTI3xJpewWB2syxgVOXP2ZOrw4cRLFv18rnESGHfsohedyaSB1qvubPWAqBFa+PSS4xh3zKalUknwc7Bs14fci8tEwEg8cpvNsqvrPZliJ6qTYFGqKuG6Ct+y449JNW6k6itTepgSYvUdJfjBTxk5tDzBdWz28km5N7lxgUB0rIWgSDl1XLCBrmm+H6bkHtD59MxAuxwLjih4tS4PzspcVjwWiJhd0HH7u2wbsPLCrrAX7am4EP40zphu9IR+fVxk+2jp7eD2uXPS6p9sDPEWHl6wGclI7pnfuoyvcn+CIwCtMweLuUw5MPj2eIIXcBhqUffeVAXVHrx8+e7+yHvqfyhqm2J9Ay3yt3zvAcXW0VqDxfvnfmv8sc9VNUW+8fUeyoo4b4uZRLLSf2DHM8= root@fbenrejdal-z01 # Clé publique SSH du poste permettant de se connecter sans mot de passe à l’OS de la VM
  user-data: | # optionel, le contenu chiffré en base64 du fichier Cloud Init. La clé peut être sur une seule ligne ou sur plusieurs, elle doit démarrer au niveau du “r” sous user-data
     I2Nsb3VkLWNvbmZpZwojIFdBVENIT1VUIHRoZSBmaXJzdCBsaW5lIG11c3Qgc3RhcnQgd2l0aCAj
     Y2xvdWQtY29uZmlnCmdyb3VwczoKICAtIGRldm9wcwp1c2VyczoKICAtIGRlZmF1bHQgIyBDcmVh
     dGUgdGhlIGRlZmF1bHQgdXNlciBmb3IgdGhlIE9TCiAgLSBuYW1lOiBmYmUKICAgIHNzaC1hdXRo
     b3JpemVkLWtleXM6ICMgdGhlIHB1YmxpYyBrZXkgb2YgbXkgbGFwdG9wLCBpdCBjb3VsZCBhbHNv
     IGJlIGZpbGxlZCBpbiB0aGUgT1ZGIHByb3BlcnR5CiAgICAgIC0gc3NoLXJzYSBBQUFBQjNOemFD
     MXljMkVBQUFBREFRQUJBQUFCZ1FEQzRDY2xoM3JOL2w3MGxCTmx3UXlLNlp0dWd4cVRHLzdIZWVy
     SFpLUFNPMGhjbDVaV0x2ejIrN1FHNUZxdllia1BQNkVvbXZ5RGhFMk1QblEwa1dhSXJ1bVZ4WVhB
     YlZkcEJCS0tkVEkzeEpwZXdXL0Iyc3l4Z1ZPWFAyWk9ydzRjUkxGdjE4cm5FU0dIZisvc29oZWR5
     YVNCMXF2dWJQV0FxQkZhK1BTUzR4aDZELzN6S2FsVWtud2M3QnMxNGZjaTh0RXdFZzhjcHZOc3F2
     clBabGlKNnFUWUZHcUt1RzZDdCt5NDQ5Sk5XNms2aXRUZXBnU1l2VWRKZmpCVHhrNXREekJkV3oy
     OGttNU43bHhnVUIwcklXZ1NEbDFYTENCcm1tK0g2YmtIdEQ1OU14QXV4d0xqaWg0dFM0UHpzcGNW
     dGFydCBtb25nb2QK

 

La base64 est obtenu de la manière suivante :
base64  loeil-du-se-vm-cloud-init.yaml

Son contenu en clair :
cat  loeil-du-se-vm-cloud-init.yaml
#cloud-config
# ATTENTION la première ligne doit commencer par #cloud-config
groups:
  – devops
users:
  – default # Créé l’utisateur par défaut
  – name: fbe
  ssh-authorized-keys: # Clé SSH de mon laptop me permettant de me connecter via SSH sans mot de passe. Clé déjà renseignée dans la configmap
    – ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDC4Cclh3rN/l70lBNlwQyK6ZtugxqTG7HeerHZKPSO0hcl5ZWLvz2+7QG5FqvYbkPP6EomvyDhE2MPnQ0kWaIrumVxYXAbVdpBBKKdTI3xJpewWB2syxgVOXP2ZOrw4cRLFv18rnESGHf+sohedyaSB1qvubPWAqBFa+PSS4xh6D3zKalUknwc7Bs14fci8tEwEg8cpvNsqvrPZliJ6qTYFGqKuG6Ct+y449JNW6k6itTepgSYvUdJfjBTxk5tDzBdWz28km5N7lxgUB0rIWgSDl1XLCBrmm+H6bkHtD59MxAuxwLjih4tS4PzspcVjwWiJhd0HH7u2wbsPLCrrAX7am4EP40zphu9IR+fVxk+2jp7eD2uXPS6p9sDPEWHl6wGclI7pnfuoyvcn+CIwCtMweLuUw5MPj2eIIXcBhqUffeVAXVHrx8+e7+yHvqfyhqm2J9Ay3yt3zvAcXW0VqDxfvnfmv8sc9VNUW+8fUeyoo4b4uZRLLSf2DHM8= root@fbenrejdal-z01
      groups: sudo, devops
    shell: /bin/bash
    passwd: VMware1!
    sudo: [‘ALL=(ALL) NOPASSWD:ALL’] # l’utilisateur fbe n’aura pas à rentrer de mot de passe avec la commande sudo
  ssh_pwauth: true
  chpasswd:
  list: |
    fbe:VMware1! # changer le mot de passe de l’utilisateur fbe
    expire: false  # Pour que le mot de passe n’expire pas
runcmd: # Exemple de runcmd pour installer MongoDB. Cloud Init est aussi capable d’utiliser directement APT pour faire des installations
  – echo “deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse” | tee /etc/apt/sources.list.d/mongodb-org-4.4.list
  – wget -qO – https://www.mongodb.org/static/pgp/server-4.4.asc | apt-key add –
  – echo “deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/4.4 multiverse” | tee /etc/apt/sources.list.d/mongodb-org-4.4.list
  – apt-get update
  – apt-get install -y mongodb-org
  – echo “mongodb-org hold” | dpkg –set-selections
  – echo “mongodb-org-server hold” | dpkg –set-selections
  – echo “mongodb-org-shell hold” | dpkg –set-selections
  – echo “mongodb-org-mongos hold” | dpkg –set-selections
  – echo “mongodb-org-tools hold” | dpkg –set-selections
  – sed -i ‘s/127.0.0.1/0.0.0.0/’ /etc/mongod.conf
  – ufw allow from any to any port 27017 proto tcp
  – sleep 2
  – systemctl start mongod

Très très important, le fichier doit absolument commencer par #cloud-config et pas autre chose. Ça reste un fichier Cloud Init classique.

Si vous ne maitrisez pas trop Cloud Init, j’y ai mis des commentaires pour que ce soit un peu plus lisible.

 

Le fichier descriptif de la VM

cat loeil-du-se-vm-deployment.yaml
apiVersion: vmoperator.vmware.com/v1alpha1
kind: VirtualMachine
metadata:
  name: loeil-du-se-vm
  namespace: loeil-du-se
labels:
  vm: loeil-du-se-vm
spec:
  imageName: ubuntu-20-1621373774638 #L’image doit être présente dans la content library et visible via la commande kubectl get virtualmachineimage
  className: best-effort-xsmall
  powerState: poweredOn
  storageClass: silver-storage-policy
  networkInterfaces:
  – networkType: nsx-t # soit nsx-t ou vsphere-distributed en fonction de votre installation
# networkName:
si -networkType est vsphere-distributed il faut mettre le nom du port group des workload
  vmMetadata:
    configMapName: loeil-du-se-vm-configmap # Nom de la configmap où la personnalisation de la VM est stockée
    transport: OvfEnv
#
#  Au moment de l’écriture de cet article l’image ubuntu (ubuntu-20-1621373774638) ne peut pas utiliser les volumes car elle est basée sur la version 10 du virtual hardware est il faut au minimum qu’elle soit en 12
#  L’image centos image (centos-stream-8-vmservice-v1alpha1-1619529007339) peut utiliser des volumes
#  volumes: # au moment de l’écriture de cet article, le paramètre mount n’est pas utilisable, le volume est vu mais doit être monté à la main ou via Cloud Init
#    – name: loeil-du-se-volume
#      persistentVolumeClaim:
#      claimName: loeil-du-se-pvc
#      readOnly: false

 

Optionnel, le fichier descriptif du service réseau

Dans mon exemple, je créé un service de type LoadBalancer pour me connecter en ssh à partir d’un réseau externe à celui des PODs.

Attention, le kind n’est pas Service comme habituellement mais VirtualMachineService

cat loeil-du-se-vm-service.yaml
apiVersion: vmoperator.vmware.com/v1alpha1
kind: VirtualMachineService
metadata:
  name: loeil-du-se-vm
spec:
  selector:
    vm: loeil-du-se-vm
  type: LoadBalancer
  ports:
    – name: ssh
      port: 22
      protocol: TCP
      targetPort: 22

 

Une fois les fichiers YAML créés, il ne reste plus qu’à les faire prendre en compte par Kubernetes.

Kubectl create -f loeil-du-se-vm-configmap.yaml

Kubectl create -f loeil-du-se-vm-deployment.yaml

Kubectl create -f loeil-du-se-vm-service.yaml

 

Pour verifier la creation de la VM :

Kubectl get vm

 

Pour en savoir un peu plus :

Kubectl describe vm loeil-du-se-vm

 

Ca reste une VM classique, donc elle va bénéficier de HA et vMotion (via DRS ou mode maintenance du host). Par contre, elle est « Developer Managed », c’est-à-dire qu’elle n’est pas administrable via le vCenter, vous ne verrez pas par exemple le contenu de la console.

Une astuce tout de même, vérifiez sur quel ESXi la VM s’exécute, ensuite connectez-vous directement sur l’ESXi via un navigateur et là vous aurez accès à la console.

Pour se connecter en ssh, si vous avez comme moi un accès via un loadbalancer vous pouvez vous y connecter directement, sinon vous devrez passer par un POD de rebond (genre busybox, alpine ou autre) et faire un ssh avec l’adresse IP sur le réseau de POD. Vous pouvez la retrouver ainsi :

kubectl get vm loeil-du-se-vm -o jsonpath='{.status.vmIp}’; echo
10.244.0.130

 

Le ssh doit se faire via le user renseigné dans le Cloud Init, j’avais mis fbe, ca donne ça :

kubectl get svc loeil-du-se-vm
NAME             TYPE           CLUSTER-IP    EXTERNAL-IP    PORT(S)        AGE
loeil-du-se-vm   LoadBalancer   10.96.1.108   172.20.18.71   22:32148/TCP   2d22h

ssh fbe@172.20.18.71

To run a command as administrator (user “root”), use “sudo <command>”.
See “man sudo_root” for details.
fbe@loeil-du-se:~$

 

Si le ssh ne fonctionne pas, c’est que le user n’a pas était pris en compte par Cloud Init, essayez avec root pour obtenir le user par défaut, en générale ubuntu pour Ubuntu et cloud-user pour CentOS :

ssh roo@172.20.18.71
Please login as the user “ubuntu” rather than the user “root”.

 

Si vous avez l’erreur ci-dessous, c’est que le poste à partir duquel vous vous connectez n’a pas la clé ssh publique renseignée ou il y a une erreur dans celle-ci, il faut donc vérifier la clé figurant dans le fichier de configmap :

fbe@172.20.18.71: Permission denied (publickey,password).

Pour debugger Cloud Init, il faut se connecter à l’os de la vm via ssh ou via la console est regardez la log /var/log/cloud-init-output.log

Voilà, n’hésitez pas à me pinger si vous avez besoin d’informations complémentaires.