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

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 :

vim:
  pkg.installed

est équivalent à :

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 :

vim is configured:
  file.managed:
    - name: /etc/vim/vimrc
    - source: salt://edit/vimrc
    - require:
      - pkg: vim

La forme du require est donc <statemodule>: <ID>

  • 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,

  • <ID> peut être l’identifiant du state ou la valeur de son paramètre name

Si on a la déclaration :

vim is installed:
  pkg.installed:
     - name: vim

On peut utiliser soit l’identifiant du state :

vim is configured:
  file.managed:
    - name: /etc/vim/vimrc
    - require:
      - pkg: vim is installed

soit la name passé en argument :

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) :

include:
  - other

local:
  pkg.installed:
   - require:
     - sls: other

require_in#

Relation inverse de require

{% 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 :

{% 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

/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 :

/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.

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 : ceralena/salt-state-graph

../../_images/dependencies_graph.png

Autres#

onchanges#

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#

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

/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

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

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 :

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:

# 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 [1] :

    • 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

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 :

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:

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