Avr 13 2021

Rendez Ansible 6X Plus Rapide avec ces 3 Astuces

Publié par sous Ansible

Ansible est généralement lent car il se connecte à l’hôte distant pour chaque tâche qu’il exécute. Faisons quelques tests sur un petit rôle qui récupère la dernière version de kubectl (client Kubernetes) depuis une URL, et installe le binaire.
Voyons 3 façons simples d’accélérer Ansible et d’obtenir des meilleurs temps d’exécution.

- name: Récupération de la dernière version
   uri:
     url: "{{kubectl_url}}/stable.txt"
     return_content: yes
     status_code: 200
   register: kubectl_latest_version
 

 - name: Téléchargement du binaire kubectl
   get_url:
     url: "{{kubectl_url}}/v{{ kubectl_version |
       default(kubectl_latest_version.content | 
       regex_replace('^v', '')) }}/bin/{{kubectl_os}}/{{kubectl_arch}}/kubectl"
     dest: "/usr/local/bin"
     mode: '755'
     owner: "{{ kubectl_owner }}"
     group: "{{ kubectl_group }}"


Temps d’exécution avec les paramètres par défaut: 32,2s

Accélérer Ansible


Cache des Facts

Récupérer les facts est la première tâche qu’Ansible exécute lorsqu’il se connecte à un hôte et le moins que l’on puisse dire est que c’est lent. La performance est aussi un problème lorsqu’on écrit un playbook que l’on teste maintes fois.

Heureusement, il est possible de faire des ajustements pour économiser du temps en ajoutant ces quelques lignes dans ansible.cfg:

# implémentation du cache et paramétrage des facts 
# pour une performance améliorée

gathering = smart
gather_subset = !hardware,!facter,!ohai

fact_caching_connection = /tmp/ansible_fact_cache
fact_caching = jsonfile

# expire let cache après 2 heures
fact_caching_timeout = 7200

Selon le fichier exemple ansible.cfg disponible en ligne, smart gathering « récupère par défaut, mais seulement si ce n’est pas déjà fait ».

Les facts hardware sont les plus longs à collecter, mais vous pourriez en avoir besoin surtout si vos rôles reposent sur les interfaces réseau. Vous pourriez recevoir une erreur « ansible_eth0 is undefined » par exemple.
Facter et ohai sont liés aux clients Puppet et Chef.

Et le plus efficace est le cache bien sûr qui stocke les informations dans un fichier JSON. Mais ca pourrait aussi l’être en mémoire, ou même dans une base partagée Redis.

On peut aussi désactiver les facts dans un playbook si on n’en a pas besoin. Dans ce playbook. C’est un gain potentiellement significatif mais qui ne peut pas être utilisé souvent, la plupart des playbooks ont besoin des facts.

- name: mon_playbook
    hosts: *
    gather_facts: no
    tasks:


Temps d’exécution: 19,2s


Pipelining

Activer le pipelining réduit le nombre d’opérations SSH requises pour exécuter un module sur le serveur distant. Ceci améliore la performance mais il faut d’abord désactiver ‘requiretty’ dans /etc/sudoers sur tous les hôtes gérés. Raison pour laquelle le pipelining est désactivé par défaut. Ajoutez dans ansible.cfg:

[ssh_connection]
pipelining = True

Si requiretty est paramétré, sudo ne fonctionnera que si l’utilisateur est loggué sur un tty réel.  Dans ce cas, sudo ne se lancera que dans une session de login et non dans un script cron ou cgi-bin. En revanche, ce paramètre est désactivé par défaut d’après la page de man, sur Debian et Ubuntu en tout cas.
On peut donc envisager d’utiliser le pipelining dans ce cas. Vérifiez la page de man de votre distribution Linux.

Temps d’exécution: 11,6s

Delegate_to localhost

La plupart des améliorations dépendent de la façon dont vous écrivez vos tâches Ansible. Dans ce rôle, vous pourriez vous connecter à l’URL depuis n’importe quel hôte – localhost? – et économiser une connexion SSH.
C’est le but de delegate_to qui est souvent pointé sur localhost. La première tâche devient:

- name: get kubectl last version
   delegate_to: localhost
   become: false
   uri:
     url: "{{kubectl_url}}/stable.txt"
     return_content: yes
     status_code: 200
   register: kubectl_latest_version


C’est une optimisation non négligeable que vous pouvez utiliser dès que la tâche peut s’exécuter n’importe où.

Il est préférable d’ajouter become: false ou vous pourriez avoir ce message d’erreur si Ansible essaye de faire un sudo root sur votre machine locale:

fatal: [backup]: FAILED! => {"changed": false, "module_stderr": "sudo: a password is required\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}


Temps d’exécution: 4,7s

Le temps d’exécution est le temps moyen de 10 exécutions à la suite, et les tests ont été réalisés sur un lien VPN loin d’être optimum.

Bien sûr les résultats ne sont pas linéaires, tous les playbooks ne s’exécuteront pas 6 fois plus rapidement mais cela donne une idée. Le cache permet d’économiser quelques secondes au lancement du playbook alors que la delegation au localhost est seulement applicable à quelques cas. Mais n’empêche.
Il existe d’autres améliorations possibles pour accélérer Ansible comme les tâches async pour lancer une tâche et passer immédiatement à la suite. Mais votre meilleur atout est d’exécuter Ansible sur une machine aussi proche que possible des hôtes ciblées.

 

Aucune Réponse

Avr 01 2021

Inventaire Ansible Dynamique des Serveurs IBM Cloud

Publié par sous Ansible,Python

IBM Cloud repose sur l’API Softlayer – aussi appelée IBM Classic Infrastructure – pour automatiser les actions. L’API est disponible en différents langages comme Python, Go, Java ou PHP.


Je vais générer un inventaire dynamique des serveurs récupérés sur mon compte IBM Cloud directement utilisable par Ansible. Je vais aussi générer des groupes correspondants aux datacenters. Vous aimeriez aussi regrouper les serveurs dans toutes sortes de catégories comme Databases, Mysql, Linux, Web, etc… Pas d’inquiétude, on peut aussi faire ca de façon très simple.
On peut y parvenir en ajoutant des tags aux hosts dans IBM Cloud portal et un host peut avoir plusieurs tags évidemment et appartenir aux groupes Ansible correspondants à ces tags/étiquettes.

Une fois que ces groupes ont été déclarés dans le fichier inventory, vous pouvez lancer les playbooks Ansible comme l’installation des paquets Apache sur les serveurs appartenant au groupe « Web » par exemple.


Vous devez d’abord ajouter votre API key dans un fichier shell à exécuter avant de lancer le script. Ca pourrait faire partie du script, mais cella permet à chaque utilisateur d’utiliser sa propre clé.

export SL_USERNAME=nom_utilisateur
export SL_API_KEY=a1b2c3[...]3456


Lancez . ./IBM_SL_env.sh tout d’abord pour ajouter le username et l’API key dans des variables d’environnement qui seront lues par le script Python ci-dessous.

#!/usr/bin/env python3  
import os
import SoftLayer
 
HOST_VARS_DIR  = "host_vars"
INVENTORY_FILE = "inventory"
 
class Inventory:
   def __init__(self):
      # Variables
      self.categories = {}
      self.datacenter = {}
      self.servers = {}
 
      # Create Softlayer connection
      self.client = SoftLayer.create_client_from_env()
 
      # Init Methods
      self.get_server_list()
      self.write_inventory()
 

   def short_host_name(self, host):
      return host['fullyQualifiedDomainName'][:host['fullyQualifiedDomainName'].find('.mydomain.')]
 

   def add_host_to_cat(self, host, cat):
      if cat == "ibm-kubernetes-service": cat = "kube"
      if cat not in self.categories:
         self.categories[cat] = [host]
      else:
         self.categories[cat].append(host)
 

   def add_server_to_list(self, host):
      try:
         host["primaryBackendIpAddress"]
      except KeyError:
         pass
      else:
         host["ShortHostname"] = self.short_host_name(host)
             
         # Build server Categories list
         if host["tagReferences"] != []:
            for tagRef in host["tagReferences"]:
               self.add_host_to_cat(host["ShortHostname"], tagRef["tag"]["name"].strip())
             
         # Build datacenter lists
         if host["datacenter"]["name"] not in self.datacenter:
            self.datacenter[host["datacenter"]["name"]] = [host["ShortHostname"]]
         else:
            self.datacenter[host["datacenter"]["name"]].append(host["ShortHostname"])
             
         # Build server attribute list
         serverAttributes = {}
         serverAttributes['IP'] = host["primaryBackendIpAddress"]
         self.servers[host["ShortHostname"]] = serverAttributes
     

   def get_server_list(self):
      object_mask = "mask[id,fullyQualifiedDomainName," \
                    "primaryBackendIpAddress," \
                    "tagReferences[tag[name]]," \
                    "datacenter]"
 
      # Get virtual server list
      mgr = SoftLayer.VSManager(self.client)
      for vs in mgr.list_instances(mask=object_mask):
         self.add_server_to_list(vs)
 
      # Get bare metal servers
      hsl = SoftLayer.HardwareManager(self.client)
      for hs in hsl.list_hardware(mask=object_mask):
         self.add_server_to_list(hs)
 

   def write_inventory(self):
      # host_vars structure
      if not os.path.exists(HOST_VARS_DIR):
         os.makedirs(HOST_VARS_DIR)
 
      inventoryFile = open(INVENTORY_FILE,"w")
 
      for cat in self.categories.keys():
         if cat != "kube":
            inventoryFile.write("[" + cat + "]\n")
            for host in self.categories[cat]:
               # write host vars
               inventoryFile.write(host + "\n")
               file = open(HOST_VARS_DIR+"/"+host,"w")
               file.write("ansible_host: " + self.servers[host]['IP'] + "\n")
               file.close()        
            inventoryFile.write("\n")
      for dc in self.datacenter.keys():
         inventoryFile.write("[" + dc + "]\n")
         for host in self.datacenter[dc]:
            if not host.startswith("kube"):
               inventoryFile.write(host + "\n")
         inventoryFile.write("\n")
      inventoryFile.close()

if __name__ == "__main__":
   exit(Inventory())


Quelques notes:

Les nodes Kubernetes managés sont visibles dans l’infrastructure classique d’IBM comme n’importe quelle VM (VSI dans la terminologie IBM) mais vous ne pouvez pas vous y connecter. Ils sont donc ajoutés à la catégorie « Kube » avant d’être ignorés.


Un fichier distinct est créé dans hosts_var pour chaque serveur contenant son address IP. Vous pourriez y ajouter d’autres variables comme les paramètres iSCSI, etc…


Le script récupère les machines virtuelles ainsi que les machines physique bare metal. Vous pouvez commenter l’une de ces sections si vous n’en avez pas besoin, épargnant un appel à IBM.

 

Aucune Réponse

Mar 26 2021

Diagrammes de Gantt Horaires avec les Sparklines

Publié par sous Divers

Je voulais montrer les temps d’exécution des tâches d’un plan de reprise d’activité sur un diagramme de Gantt pour un plus bel effet qu’une liste ennuyeuse de temps de de démarrage et de fin, mais je n’ai trouvé aucun outil gérant les heures sur une journée/24 heures.

Les softs Gantt proposent trop de fonctionnalités dont je n’ai pas besoin pour un simple rapport. J’ai donc opté pour la fonction Sparkline sur Google sheet: c’est gratuit, et ca reste assez facile même s’il faut travailler un peu pour le faire fonctionner avec des heures. C’est aussi disponible sur Excel évidemment.

Qu’est-ce que Sparkline? En résumé, Sparkline est un graphique dans une cellule Excel (ou équivalent) destiné à donner une tendance. Les graphiques en barres sont parfaits pour les diagrammes de Gantt.


Il n’y a pas de min, et max fonctionne uniquement pour des nombres sur les graphiques en barres, c’est pour cela que j’ai créé 2 colonnes Start delay et Duration en minutes. Des formules basiques sur les les heures de début et de fin du projet et des tâches permettent de les remplir automatiquement.

=HEURE(D5-$D$3)*60+MINUTE(D5-$D$3); Start delay
=HEURE(E5-D5)*60+MINUTE(E5-D5); Duration


Les cellules Sparkline sont construites à partir de ces 2 valeurs; La première barre est blanche pour positionne la barre de couleur au bon endroit. La valeur Max est tout simplement la durée du projet.

=SPARKLINE(F5:G5,{"charttype","bar"\"color1","white"\"color2","#199698"\"max",$G$3})
 

Aucune Réponse

Mar 16 2021

Faites de Beaux Diagrammes avec le Diagram as Code

Publié par sous Python

Le Diagram as Code est un sujet bien présent en ce moment qui apporte de nombreux avantages. Parmi lesquels:
– Garder une trace des changements, qui et quoi
– Stocker sur un dépôt de code comme simple texte
– Pas besoin de réaligner les flèches ou bouger les icônes quand on ajoute des éléments
– Plus rapide une fois prise en main
– C’est du code, et vous savez combien les devs aiment faire des diagrammes manuels
– le rendu des diagrammes, tout est aligné proprement


Une bibliothèque de diagrammes open source est disponible sur diagrams.mingrammer.com en Python et Go. Je ne décrirai pas les étapes d’installation, c’est très bien expliqué dans leur documentation. Ils fournissent aussi de très bons diagrammes d’exemples.
Je vais tenter de convertir un vieux diagramme fait sur Microsoft Visio il y a longtemps en diagram as code. Cette image vient d’un article de réplication Mysql.

Visio Diagram


Exemple de Diagram as Code


Voici le code que j’ai écrit pour générer l’image:

from diagrams import Cluster, Diagram, Edge
from diagrams.azure.compute import *
from diagrams.azure.database import *
 
with Diagram("Replication", show="False", direction='TB'):
 
  slaves = []
  clients= []
 
  master = SQLServers("Master")
  with Cluster("slaves"):
    for s in range(3):
      slave = SQLDatabases("slave"+str(s+1))
      slaves.append(slave)
 
  for c in range(3):
    client = VM("client"+str(c+1))
    clients.append(client)
    clients[c] >> Edge(color="darkgreen",label="Reads") >> slaves[c]
    
  clients >> Edge(color="red",label="Writes") >> master
  master >> Edge(color="blue",label="Replication") >> slaves[1]

Moins de 20 lignes de code, c’est tout. Les icônes sont celles du répertoire Azure mais j’aurais pu en prendre d’autres. La seconde image est à peu près la même mais j’ai supprimé la direction qui devient gauche vers droite. Les labels sont mieux gérés dans cette direction comme vous le voyez, même si ce n’est toujours pas parfait avec des flèches multiples.
J’ai aussi enlevé le label « Read » au milieu pour un meilleur alignement en liant des objets simple plutôt que des tableaux. J’ai remplacé

clients[c] >> Edge(color="darkgreen",label="Reads") >> slaves[c]

par (en dehors de la boucle for):

for i in [0,2]: clients[i] >> Edge(color="darkgreen",label="Reads") >> slaves[i]


Difficultés rencontrées

Il peut s’avérer un peu compliqué d’obtenir exactement ce que vous voulez surtout avec des niveaux multiples connectés les uns aux autres. Vous pouvez experimenter et changer les comportements en jouant avec les groupements par Cluster. Ca peut être utile quand vous voulez que les niveaux 2 et 4 soient regroupés par exemple mais ca peut aussi devenir brouillon comme sur ce cluster Mysql:

Mysql Replication Diagram


En un mot, cette implémentation de Diagram as Code est puissant et efficace même si l’on perd du contrôle sur le placement des éléments. Un petit inconvénient pour une très bonne librairie. Une fois familiarisé avec toutes ces petites astuces, vous pouvez créer de magnifiques diagrammes. Voici un exemple d’architecture basique Kubernetes:

Kubernetes Diagram
 

Aucune Réponse

Mar 10 2021

Pourquoi Ansible Met à Jour les Paquets en Hold et Comment y Remédier

Publié par sous Ansible

J’écrivais un nouveau rôle Ansible pour mettre à jour toutes mes VMs. J’utilise toujours une vieillle version de Rancher qui ne fonctionne qu’avec la version 18.06 du paquet docker-ce.

Une première tâche met le paquet en hold c’est-à-dire en interdit la mise à jour avec le module Ansible Dpkg, comme recommandé sur de nombreux sitesweb.
Une deuxième étape exécute une full upgrade de mon système avec le module Ansible apt.

- name: keep docker from being updated on Rancher nodes
  dpkg_selections:
    name: docker-ce
    selection: hold

- name: apt full-upgrade
  apt:
    upgrade: full


J’ai ensuite lancé mon playbook plein de confiance et, constaté que docker-ce avait été mis à jour! Bizarrement, les distributions Ubuntu semblent impactées, alors que ca fonctionne bien sur la famille Debian.


La page du module Ansible apt mentionne « Option full, lance une full-upgrade aptitude ».
Vérifions le paquet en hold après la 1re étape:

$ dpkg -l | grep docker
hi  docker-ce 18.06.3~ce~3-0~ubuntu. amd64. Docker: the open-source application container engine


La même chose avec aptitude:

$ aptitude search ~i | grep docker
i  docker-ce - Docker: the open-source application container engine

h pour hold est MANQUANT!


apt-get et aptitude semblent reposer sur des fonctions hold différentes, d’où « dpkg –selections » qui ne permet pas de s’assurer qu’aptitude (la commande qui se charge des upgrades) ne touchera pas aux paquets « holdés ».


Et maintenant?
Nous avons de la chance, le module Ansible apt propose un moyen de forcer la mise à jour avec apt-get au lieu d’aptitude

- name: apt full-upgrade
  apt:
    upgrade: full
    force_apt_get: yes


Problème résolu !

 

Aucune Réponse

Suivant »