.. -*- coding: utf-8 -*- :orphan: ======================== Gestion de dépendances ======================== Exprimer les dépendances entre les states ========================================= Introduction ------------ :Défi: définir **la causalité** des éléments du système et **les dépendances** entre les différentes briques du système de configuration - C'est le point difficile pour décrire l'état d'un système ou d'une infrastructure .. 6 états possibles ----------------- 1) ERROR 2) OK - pas de changements 3) OK - avec des changements 4) test=True - error 5) test=True - pas de changements 6) test=True - avec des changements Requisites ---------- * Salt propose des outils (appelés *requisites*) pour définir ces dépendances et contrôler l'ordre d'exécution des *states* * Les 3 principaux *requisites* qui permettent de déclarer des dépendances fonctionnelles entre des *states* sont : * ``require`` et ``require_in`` : déclaration de dépendance entre 2 *states* * ``watch`` et ``watch_in`` : pour s'assurer qu'un *state* est réévalué lors de la modification de l'état d'un autre *state* * ``prereq`` et ``prereq_in`` : pour déclarer un état nécessaire d'un autre *state* pour appliquer un *state* **Attention :** la définition des *states* est **déclarative** ; cependant, l'ordre dans lequel les *states* apparaissent dans les fichiers est respecté. Autres requisites ----------------- * On trouve en plus des *requisites* plus organisationnelles : * ``include`` : pour pouvoir faire référence à des *states* déclarés dans un autre fichier * ``use`` et ``use_in`` : permet de dupliquer les paramètres d'un autre *state* * ``onchanges`` et ``onchanges_in`` : pour appliquer un état lorsque l'état dont il dépend a changé et a fonctionné. * ``onfail`` et ``onfail_in`` : pour appliquer un état lorsqu'un autre état est en échec (par exemple, point de montage des backups) * ``order`` : déclarer un numéro d'ordre (à éviter) * sur le *state* ``cmd`` : ``unless`` et ``onlyif`` Identifiants ------------ - Les identifiants des *states* doivent être uniques *pour un système ciblé* - La valeur de l'argument obligatoire ``name`` de toutes les fonctions des ``state modules`` est par défaut l'identifiant du ``state`` (le ``state-id``) - Il est recommandé de ne pas utiliser le ``state-id`` comme ``name`` par défaut, cela peut entraîner des conflits de nom (dans les ``include``, ``require``, etc.) Ceci : .. code-block:: yaml vim: pkg.installed est équivalent à : .. code-block:: yaml vim is installed: pkg.installed: - name: vim Cette seconde forme est recommandée. Require ======= ``require`` ----------- On peut exprimer une dépendance entre deux *states* à l'aide du paramètre ``require``, par exemple : .. code-block:: yaml vim is configured: file.managed: - name: /etc/vim/vimrc - source: salt://edit/vimrc - require: - pkg: vim La forme du ``require`` est donc ``: `` - il ne peut donc y avoir qu'une seule fonction de *state* dans un ``state module`` qui implémente la gestion de la dépendance, - ```` peut être l'identifiant du *state* ou la valeur de son paramètre ``name`` Si on a la déclaration : .. code-block:: yaml vim is installed: pkg.installed: - name: vim On peut utiliser soit l'identifiant du *state* : .. code-block:: yaml vim is configured: file.managed: - name: /etc/vim/vimrc - require: - pkg: vim is installed soit la ``name`` passé en argument : .. code-block:: yaml vim is configured: file.managed: - name: /etc/vim/vimrc - require: - pkg: vim Il est possible de faire un ``require`` sur un ficher ``.sls`` au complet (tous les états déclarés dans un fichier ``sls``) : .. code-block:: yaml include: - other local: pkg.installed: - require: - sls: other ``require_in`` -------------- Relation inverse de `require` .. code-block:: jinja {% if not grains['custom_machine'] %} vim is installed: pkg.installed: - name: vim - require_in: vim is configured {% endif %} vim is configured: file.managed: - name: /etc/vim/vimrc - source: salt://edit/vimrc Permet de rajouter des dépendances quand l'accès aux fichiers dépendants n'est pas possible ou pour des dépendances sur des ``include`` par exemple. À utiliser pour des dépendances optionnelles (blocs ``if`` et ``for`` notamment) afin d'améliorer la lisibilité du code. L'exemple précédant écrit avec un ``require`` serait : .. code-block:: jinja {% if not grains['custom_machine'] %} vim is installed: pkg.installed: - name: vim {% endif %} vim is configured: file.managed: - name: /etc/vim/vimrc - source: salt://edit/vimrc {% if not grains['custom_machine'] %} - require: - pkg: vim {% endif %} Ce qui est moins lisible. Watch ===== ``watch`` --------- Permet de définir un comportement supplémentaire d'un *state* en fonction des changements qui surviennent sur un autre *state* Ce "comportement supplémentaire" dépend du ``state module`` sur lequel ce paramètre ``watch`` est utilisé : - pour que *watch* fonctionne, il faut que la fonction ``mod_watch`` soit définie dans le module sur lequel on met un ``watch`` - sinon, *watch* se comporte comme *require* - les ``state modules`` qui définissent la fonction ``mod_watch`` sont : - ``cmd`` - ``service`` - ``supervisord`` - ``mount`` - ``etcd`` - ``dockerio`` et ``dockerng`` - ``tomcat`` ``watch`` - ``service`` ----------------------- Pour ``states.service``: Principe : quand un fichier de configuration d'un service est modifié, et on souhaite le recharger. Salt essaye alors les fonctions suivantes : * ``service.reload`` * ``service.force_reload`` * ``service.full_restart`` * ``service.restart`` * ``service.start`` .. code-block:: yaml /etc/stuff/config: - file.managed: - source: salt://stuff/configfile stuffdaemon: - service.running: - watch: - file: /etc/stuff/config ``watch`` - ``cmd`` ------------------- Principe : quand l'application d'un state ``cmd.run`` renvoit ``True`` avec la liste des choses qui ont été modifiées, l'utilisation de ``watch`` permet de réagir à cette modification de l'état du système : - pour utiliser un *watch* dans un *state* ``cmd``, il faut utiliser ``cmd.wait`` plutôt que ``cmd.run`` : .. code-block:: yaml /usr/local/bin/postinstall.sh: cmd.wait: - watch: - pkg: mycustompkg file.managed: - source: salt://utils/scripts/postinstall.sh mycustompkg: pkg.installed: - require: - file: /usr/local/bin/postinstall.sh ``watch_in`` ------------ * Relation inverse de ``watch`` * Comme dans le cas des ``require_in``, le ``watch_in`` est à utiliser pour les dépendances optionnelles Prereq ====== ``prereq`` ---------- Définit le *state* actuel comme pré-requis pour effectuer des modifications dans un autre *state*. Utilise ``test=True`` dans le *state* auquel on fait référence pour évaluer si des changements vont être appliqués par l'état testé Intérêt : - évite d'effectuer des opérations de préparation inutiles automatiquement - permet de scripter des opérations plus complexes avec des ``sls``. ``prereq`` - exemple -------------------- Arrêter un service pendant une mise à jour d'un site uniquement si cette mise à jour fait quelque chose. .. code-block:: yaml apache_stopped: service.dead: - name: apache2 - prereq: - file: data_files data_files: file.recurse: - name: /var/www/site/data - source: salt://var/www/sitedata/ apache_running: service.running: - name: apache2 - watch: - file: data_files ``prereq_in`` ------------- * Utilisé par des blocs optionnels (cf. ``require_in`` et ``watch_in``) Visualisation ------------- Un projet tiers permet de visualiser en GraphViz les dépendances : https://github.com/ceralena/salt-state-graph .. image:: media/dependencies_graph.png :align: center :width: 10cm .. image from http://i.imgur.com/wETR0WG.png (README of the project) .. TODO : make our own image Autres ====== ``onchanges`` ------------- .. code-block:: yaml devpi service: file.managed: - name: /lib/systemd/system/devpi.service - source: salt://devpi/conf/devpi.service cmd.run: - name: systemctl daemon-reload - onchanges: - file: /lib/systemd/system/devpi.service ``onfail`` ---------- .. code-block:: yaml mount remote data: mount.mounted: - name: /mnt/data - device: storage.example.org:/data - fstype: nfs mount backup data: mount.mounted: - name: /mnt/data - device: backup.storage.example.org:/data - fstype: nfs - onfail: - mount: mount remote data ``use`` ------- Utiliser les mêmes paramètres qu'une autre déclaration .. code-block:: yaml /etc/foo.conf: file.managed: - source: salt://foo.conf - template: jinja - user: apache - group: apache /etc/bar.conf: file.managed: - source: salt://bar.conf - use: - file: /etc/foo.conf **Attention**: - ``use`` n'hérite pas des ``require``, ``watch`` et ``prereq`` - il n'est pas possible de "chaîner" les ``use`` ``unless`` - ``cmd.run`` ------------------------ * Exécuter un état sauf si une commande est vraie * *bonne pratique* : lancer les tests avant d'installer un logiciel ou un service .. code-block:: yaml mirror site: cmd.run: name: wget --mirror [snip] unless: cat /var/run/mirror-in-progress ``onlyif`` - ``cmd.run`` ------------------------ * Définition inverse de ``unless`` * Exécuter un état seulement si une commande définie est vraie .. code-block:: yaml mirror site: cmd.run: name: wget --mirror [snip] onlyif: wget http://example.org Dépendances entre machines ========================== ``orchestrate`` --------------- Exemple, dans ``/srv/salt/orchestration/webapp.sls`` : .. code-block:: yaml install_postgres: # en premier les machines db* salt.state: - tgt: 'db*' - sls: - postgres.server load_database_dump: salt.function: - tgt: 'db*' - name: cmd.run - arg: - pg_restore dump install_webserver: salt.state: - tgt: 'web*' - highstate: True puis on lance depuis la console: .. code-block:: console # salt-run state.orchestrate orchestration.webapp Le *runner* ``orchestrate`` permet d'orchestrer des états et des exécutions de fonctions entre plusieurs *minions* * utilise les fonctions définies dans le *state module* ``salt`` [#]_ : * ``salt.function`` : exécuter une fonction d'un module d'exécution * ``salt.state``: exécuter un état salt (sls), dont *highstate* * ``salt.runner`` : exécuter un ``runner`` coté master (pas de *tgt*) * ``salt.wait_for_event``: observer le bus d'événement et attendre un événement * ``salt.wheel`` : exécuter une fonction ``wheel`` coté master (module de configuration du master) * les minions sont ciblés sont spécifiés par l'argument ``tgt`` .. [#] attention, le module Python qui l'implémente est ``salt.states.saltmod``, donc typiquement le fichier python ``/usr/lib/python2.7/dist-packages/salt/states/saltmod.py`` ``overstate`` (deprecié) ------------------------ * ``overstate.sls`` permet de définir des dépendances entre sls et de les appliquer à différents *minions* * il s'agit du passage de la notion de dépendance présente dans les sls au niveau des machines * au lieu de travailler au sein d'un seul *minion*, on gère des dépendances entre *minions*, par exemple : .. code-block:: console salt-run state.over [...] salt-run state.over env='dev' alternative_overstate.sls [...] * le fichier par défaut pour definir des *overstates* est recherché à la racine et s'appelle ``overstate.sls`` * on peut spécifier d'autres fichiers *overstate* si on le souhaite et les appeler explicitement depuis le *master* Exemple: .. code-block:: yaml mysql: #en premier les machines db* match: 'db*' sls: - mysql.server - drbd webservers: #puis les serveurs web* match: 'web*' require: - mysql all: #si tout marche, le reste match: '*' require: - mysql - webservers