https://github.com/lorin/ansible-quickref
production # inventory file for production servers stage # inventory file for stage environment group_vars/ all # here we assign variables to particular groups boston # "" host_vars/ hostname1 # if systems need specific variables, put them here xyz.boston.example.com # "" site.yml # master playbook webservers.yml # playbook for webserver tier dbservers.yml # playbook for dbserver tier roles/ common/ # this hierarchy represents a "role" defaults/ main.yml # variables par defaut du module, supplantées par tout le reste tasks/ # main.yml # <-- tasks file can include smaller files if warranted handlers/ # main.yml # <-- handlers file templates/ # <-- files for use with the template resource ntp.conf.j2 # <------- templates end in .j2 files/ # bar.txt # <-- files for use with the copy resource foo.sh # <-- script files for use with the script resource vars/ # main.yml # <-- variables associated with this role -> valeurs spécifiques, dont on a besoin d'être sur, suplpanté seulement par '-e' meta/ # main.yml # <-- role dependencies webtier/ # same kind of structure as "common" was above, done for the webtier role monitoring/ # "" fooapp/ # ""
Groupé par localisation (enfants) et par rôle (parents).
# file: production [atlanta-webservers] www-atl-1.example.com www-atl-2.example.com [boston-webservers] www-bos-1.example.com www-bos-2.example.com [atlanta-dbservers] db-atl-1.example.com db-atl-2.example.com [boston-dbservers] db-bos-1.example.com # webservers in all geos [webservers:children] atlanta-webservers boston-webservers # dbservers in all geos [dbservers:children] atlanta-dbservers boston-dbservers # everything in the atlanta geo [atlanta:children] atlanta-webservers atlanta-dbservers # everything in the boston geo [boston:children] boston-webservers boston-dbservers
# file: group_vars/atlanta ntp: ntp-atlanta.example.com backup: backup-atlanta.example.com # file: group_vars/webservers apacheMaxRequestsPerChild: 3000 apacheMaxClients: 900 # file: group_vars/all ntp: ntp-boston.example.com backup: backup-boston.example.com # file: host_vars/db-bos-1.example.com foo_agent_port: 86 bar_agent_port: 99
# file: site.yml - include: webservers.yml - include: dbservers.yml
# file: webservers.yml - hosts: webservers roles: - common - webtier
# file: roles/common/tasks/main.yml - name: be sure ntp is installed yum: pkg=ntp state=installed tags: ntp - name: be sure ntp is configured template: src=ntp.conf.j2 dest=/etc/ntp.conf notify: - restart ntpd tags: ntp - name: be sure ntpd is running and enabled service: name=ntpd state=running enabled=yes tags: ntp
# file: roles/common/handlers/main.yml - name: restart ntpd service: name=ntpd state=restarted
ansible-playbook -i production site.yml ansible-playbook -i production site.yml --tags ntp ansible-playbook -i production webservers.yml ansible-playbook -i production webservers.yml --limit boston ansible-playbook -i production webservers.yml --limit boston[0-10] ansible-playbook -i production webservers.yml --limit boston[10-20] ansible boston -i production -m ping ansible boston -i production -m command -a '/sbin/reboot'
Confirm what task names would be run if I ran this command and said “just ntp tasks”
ansible-playbook -i production webservers.yml --tags ntp --list-tasks
Confirm what hostnames might be communicated with if I said “limit to boston”
ansible-playbook -i production webservers.yml --limit boston --list-hosts
Si Red Hat Enterprise Linux (TM), CentOS, Fedora, Debian, or Ubuntu : prendre les derniers packages
on peut bootstraper un noeud pour installer python 2.X en mode “raw” : ansible myhost –sudo -m raw -a “yum install -y python2 python-simplejson”
apt-get install ansible ansible-doc sudo sed --in-place "s/^remote_port/#remote_port/g" /etc/ansible/ansible.cfg iceweasel /usr/share/doc/ansible-doc/html/index.html
Par défaut, Ansible utilise l'authentification SSH par clef.
Par défaut, le serveur central se connecte sur les noeuds et lance des commandes/scripts. Mais cela peut être inversé (voir ansible-pull).
BASEDIR="${HOME}/ansible" INVENTORY=${BASEDIR}/localhost LIMIT="localhost" PARALLELISM=5
if [ ! -d ${BASEDIR} ]; then mkdir ${BASEDIR} fi
echo "localhost ansible_connection=local" > ${INVENTORY}
ansible --inventory=${INVENTORY} --limit=${LIMIT} --forks=${PARALLELISM} --module-name=shell --args="ls" all
Le nom de fichier correspond a l'environnement. On peut utiliser des sections [<nom_du_groupe>] pour creer des groupes d'hotes (conseille : par emplacement geographique).
RQ : on peut faire apparaitre un meme hote dans plusieurs groupes, mais attention a la determination des variables et leurs précédences !
RQ : mettre au carre ~/.ssh/config au prealable, et copier la clef publique SSH sur les serveurs !
Host plop.liberasys.com Hostname plop.liberasys.com Port 2222 User root
mail.example.com [webservers] foo.example.com bar.example.com [dbservers] one.example.com two.example.com three.example.com
BASEDIR="${HOME}/ansible" INVENTORY=${BASEDIR}/test LIMIT="" PARALLELISM=5
Pinger les machines :
ansible --inventory=${INVENTORY} --limit=${LIMIT} --forks=${PARALLELISM} --module-name=ping all
Voir si l'option UseDns est bien positionnee :
ansible --inventory=${INVENTORY} --limit=${LIMIT} --forks=${PARALLELISM} --module-name=command --args="bash -c 'cat /etc/ssh/sshd_config | grep -i dns'" all
Si vous utilisez sudo, configurez le pour ne pas demander de mot de passe, et mettre en plus l'argument “–sudo” dans la ligne de commande ansible. Si besoin de spécifier un autre utilisateur, utiliser “–sudo-user=”.
Si vous ne voulez plus être embêté avec les vérifications de clefs connues des servuer SSH, éditer /etc/ansible/ansible.cfg et modifier : “host_key_checking = False”. Il y a aussi une variable d'environnement prévue : “export ANSIBLE_HOST_KEY_CHECKING=False”
Si vous voulez optimiser le temps de connexion SSH aux serveurs (à faire sur chaque serveur) : sudo echo “UseDNS no” » /etc/ssh/sshd_config && service ssh stop && sleep 3 && service ssh start
Si vous voulez tracer les changements de configuration, utilisez un système de gestion de version sur le répertoire de configuration ansible !
But : définir les hosts, des groupes de machines.
mail.example.com [webservers] foo.example.com bar.example.com [dbservers] one.example.com two.example.com three.example.com [targets] localhost ansible_connection=local other1.example.com ansible_connection=ssh ansible_ssh_user=mpdehaan other2.example.com ansible_connection=ssh ansible_ssh_user=mdehaan Groupe de Groupe : [southeast:children] atlanta raleigh Avec des patterns : [webservers] www[01:50].example.com Avec des variables de groupes : [southeast:vars] some_server=foo.southeast.example.com halon_system_timeout=30 self_destruct_countdown=60 escape_pods=2 Avec des variables par host : [atlanta] host1 http_port=80 maxRequestsPerChild=808 host2 http_port=303 maxRequestsPerChild=909
On peut les définir par hote, par groupe ([<nom_du_groupe>:vars]) ou par groupe de groupes (mais attention, cette derniere methode ne fonctionne pas avec ansible, seulement avec ansible-playbook).
Exemple :
[atlanta] host1 http_port=80 maxRequestsPerChild=808
Certaines variables configurent le comportement d'interraction avec les hotes (on pourrait se passer de la configuration ssh_config) :
ansible_ssh_host ansible_ssh_port ansible_ssh_user ansible_connection ansible_ssh_private_key_file ansible_shell_type ansible_python_interpreter ansible\_\*\_interpreter
Mais ! La bonne pratique veut que l'on sépare les variables “métier” du fichier d'inventaire. Hiérarchie conseillée :
${BASEDIR}/group_vars/groupe_1 ${BASEDIR}/group_vars/groupe_2 ${BASEDIR}/host_vars/host_a
RQ : ce sont alors des fichiers YAML
Example :
ntp_server: acme.example.org database_server: storage.example.org
RQ : on peut faire de l'inventaire automatique / dynamique, avec cobbler ou AWS EC2 par exemple, en passant par un script d'inventaire :
file:///usr/share/doc/ansible-doc/html/intro_dynamic_inventory.html
RQ : si l'option –inventory est utilisée avec un répertoire, toutes les sources présentes sont prises en compte (même les scripts .py).
BSD Jails Digital Ocean Google Compute Engine Linode OpenShift OpenStack Nova Red Hat's SpaceWalk Vagrant (not to be confused with the provisioner in vagrant, which is preferred) Zabbix
one.example.com
one.example.com:two.example.com 192.168.1.50 - hosts : 192.168.1.* *.example.com *.com - nom d'un groupe : - opérations booléennes avec les groupes : groupe1:groupe2 : OR (union) groupe1:!groupe2 : AND NOT (différence) groupa1:&groupe2 : ET (intersection) on peut combiner : webservers:dbservers:&staging:!phoenix (précédence de gauche à droite) - mix : one*.com:dbservers ~(web|db).*\.example\.com - limit flag : ansible-playbook site.yml --limit datacenter2
Utilisé pour les petites tâches qui n'ont pas besoin d'être réutiliser.
Ex :
rebooter les machines : ansible atlanta -a “/sbin/reboot” -f 10
copier un fichier : ansible webservers -m file -a “dest=/srv/foo/b.txt mode=600 owner=mdehaan group=mdehaan”
Polling :
lancer en arrière plan :
ansible all -B 3600 -a "/usr/bin/long_running_operation --do-stuff" (arrière plan) ansible all -m async_status -a "jid=123456789" (status manuel) ansible all -B 1800 -P 60 -a "/usr/bin/long_running_operation --do-stuff" (polling)
Ordre de prise en compte des paramètres :
Chaine de caractères de tag de config par ansible : ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}
Erreur si variable non renseignée : #error_on_undefined_vars=False ⇒ attention, par défaut à False (erreur silencieuse)
Logging : log_path = /var/log/ansible.log =⇒ par defaut, pas de logging !
Timeout SSH : par défaut à 10, peut être diminué sauf systèmes fortement chargés
Pipelining : a voir pour améliorer les perfs, par défaut à false, mais requiert des modification sur la configuration de sudo si il est utilisé (require tty)
[defaults] log_path = ~/ansible/log/ansible.log timeout = 1 [ssh_connection] pipelining = True [accelerate] accelerate_port = 5099 accelerate_timeout = 30 accelerate_connect_timeout = 1.0 accelerate_multi_key = yes
Via powershell en natif. Requiert pywinrm sur la machine de contrôle :
pip install http://github.com/diyan/pywinrm/archive/master.zip#egg=pywinrm
Gestion de paquets via chocolatey : https://chocolatey.org
Il y a de smodules spécifiques windows (ex : ACLs).
Il faut toujours une machine de contrôle linux (et cygwin n'est pas supporté).
Langage de configuration, déploiement et orchestration.
Playbooks : on utilise des 'Modules' dans des 'Tasks' assemblées dans des 'Plays' contenus dans des 'Playbooks'.
Syntaxe = YAML
RQ : Ansible utilise des caractères spéciaux en interne. Si ces caractères sont utilisés pour la syntaxe YAML, il faut les escaper dans le playbook : [] {} : > | /. En fait Ansible a sa surcouche à YAML.
Exemple :
vars:
http_port: 80 max_clients: 200 remote_user: root tasks: - name: ensure apache is at the latest version yum: pkg=httpd state=latest - name: write the apache config file template: src=/srv/httpd.j2 dest=/etc/httpd.conf notify: - restart apache - name: ensure apache is running service: name=httpd state=started handlers: - name: restart apache service: name=httpd state=restarted
Système évènementiel : notify/handler, example :
ATTENTION BUG https://github.com/ansible/ansible/issues/11025
template: src=template.j2 dest=/etc/foo.conf
notify: - restart memcached - restart apache
handlers: - name: restart memcached service: name=memcached state=restarted - name: restart apache service: name=apache state=restarted
Pour exécuter (flusher) les handlers immédiatement :
tasks: - shell: some tasks go here - meta: flush_handlers - shell: some other tasks
Lancement d'un playbook :
ansible-playbook playbook.yml -f 10
RQ : on peut inverser le sens de connection d'ansible, et lancer ansible-pull dans une crontab et l'asservir à un outil de gestion de version
Lister les hotes cibles :
ansible-playbook playbook.yml --list-hosts
vérifier la syntaxe :
ansible-playbook --syntax-check playbook.yml
Abstractions réutilisables, combinant des fichiers d'inclusion.
Le but d'un 'play' dans un 'playbook' est de faire correspondre un groupe de machines à plusieurs 'roles'.
Fichier de 'tasks' à inclure :
# possibly saved as tasks/foo.yml
command: /bin/foo
command: /bin/bar
Inclusion d'un fichier de 'tasks' :
tasks:
On peut donner des paramètres à des inclusions :
tasks: - include: wordpress.yml user=timmy - include: wordpress.yml user=alice - include: wordpress.yml user=bob tasks: - { include: wordpress.yml, user: timmy, ssh_keys: [ 'keys/one.txt', 'keys/two.txt' ] }
Autre syntaxe pour le passage de variables :
tasks: - include: wordpress.yml vars: remote_user: timmy some_list_variable: - alpha - beta - gamma
On peut aussi le faire avec les handlers :
# this might be in a file like handlers/handlers.yml - name: restart apache service: name=apache state=restarted handlers: - include: handlers/handlers.yml
L'inclusion peut être utilisée avec les playbooks :
hosts: all
remote_user: root tasks: - name: say hi tags: foo shell: echo "hi..." - include: load_balancers.yml - include: webservers.yml - include: dbservers.yml
MAIS :
Les roles sont juste une couche d'automatisation pour les directives d'inclusion.
Roles : outils d'organisation, définition de composants réutilisables et utilisant une structure de fichiers déterminée
Ex de structure :
site.yml webservers.yml fooservers.yml roles/ common/ files/ templates/ tasks/ handlers/ vars/ meta/ webservers/ files/ templates/ tasks/ handlers/ vars/ meta/
Utilisation des 'roles' dans un playbook :
roles:
Pour chaque 'role' x :
RQ : on peut configurer le chemin des 'roles' ('roles_path').
On peut donner des variables aux roles :
roles:
On peut conditionner les roles :
roles:
On peut assigner des 'tags' aux roles :
roles:
RQ : les 'roles' sont executes avant les 'tasks' du playbook dans un 'play'. Mais on peut ordonner les choses :
pre_tasks: - shell: echo 'hello'
roles: - { role: some_role }
tasks: - shell: echo 'still busy'
post_tasks: - shell: echo 'goodbye'
RQ : dans ce cas, les 'tags', si utilisés, doivent être présents également dans pre_tasks and post_tasks
Il est possible de définir un jeu de variables par défaut dans : defaults/main.yml
Les dépendances entre 'roles' gèrent automatiquement les séquences. Les dépendances sont rédigées dans le fichier meta/main.yml du répertoire du role. Dans ce fichier, ce sont les roles de dependance amont (antérieure, aka pré-conditions) :
dependencies: - { role: common, some_parameter: 3 } - { role: apache, port: 80 } - { role: postgres, dbname: blarg, other_parameter: 12 } - { role: '/path/to/common/roles/foo', x: 1 }
Les roles de dependance amont ne sont executés qu'une seule fois. On peut changer ce comportement au besoin (par exemple si on se sert d'une variable en condition), avec la directive 'allow_duplicates: yes' en début de meta/main.yml
RQ : on peut inclure des modules développés dans un 'role' afin de le distribuer.
RQ : avec cette méthode on peut patcher des modules existant
roles/ my_custom_modules/ library/ module1 module2
Repository de roles : https://galaxy.ansible.com/
nommage : lettres (minuscules), chiffres et '_' c'est tout !
Variables dans les inventaires : non recommandé
Variables en ligne dans un playbook :
vars:
http_port: 80
Variables dans les roles ou les fichiers inclus.
Substitution des variables : nom_de_variable
Jinja2 permet des choses comme les boucles ou les donctions dans les templstes. Mais il n'est pas souhaitable de les utiliser dans les playbooks.
Jinja2 permet de filtrer, reformater et tansformer les expressions (voir : http://jinja.pocoo.org/docs/dev/templates/#builtin-filters).
Exemple pour le débugging :
{{ some_variable | to_nice_json }} {{ some_variable | to_nice_yaml }}
Pour les filtres, Jinja2 utilise le pipe : <nom_de_variable>|<nom_filtre>(“<variable_du_filtre>”)
Exemple :
{{ listx|join(', ') }}
Voir : http://jinja.pocoo.org/docs/dev/templates/#builtin-filters
Affectation par défaut :
{{ some_variable | default(5) }}
Avec Ansible :
var|match : renvoie vrai si la variable correspond à une valeur travail sur les listes : {{ list1 | unique }} {{ list1 | union(list2) }} {{ list1 | intersect(list2) }} {{ list1 | difference(list2) }} {{ list1 | symmetric_difference(list2) }} {{ list | join(" ") }} random : {{ 59 |random}} * * * * root /script/from/cron {{ ['a','b','c']|random }} => 'c' {{ 100 |random(step=10) }} => 70 {{ 100 |random(1, 10) }} => 31 {{ 100 |random(start=1, step=10) }} => 51 basename : {{ path | basename }} dirname : {{ path | dirname }} expansion chemin contenant ~ : {{ path | expanduser }} base64 : {{ encoded | b64decode }} {{ decoded | b64encode }} md5sum : {{ filename | md5 }} casting : some_string_value | bool regexp match : when: url | match("http://example.com/users/.*/resources/.*") regexp search : when: url | search("/users/.*/resources/.*") regexp replace : {{ 'foobar' | regex_replace('^f.*o(.*)$', '\\1') }} casting : <nom_variable>|<type>, ex : ansible_lsb.major_release|int
Les filtres sont améliorés sans cesse avec les versions et on peut en implémenter.
Attention aux caractères spéciaux de YAML : il faut escaper : [] {} : > | /
Relevé d'informations de l'hote distant = “locally supplied user values”.
On peut les afficher avec : ansible hostname -m setup
On les utilise comme des variables : ansible_hostname
Souvent utilisé dans les pré-conditions ('when')
On peut les utiliser pour créer des groupes dynamiques d'hotes ('group_by').
Si on a pas besoin des facts, on peut les désactiver (ce la accélère l'exécution) :
gather_facts: no
On peut aussi créer des 'facts' dynamiquement, en utilisant le répertoire local des hotes : /etc/ansible/facts.d/preferences.fact
On peut aussi créer des modules spécifiques de 'facts'.
Voir doc pour l'application.
{{ ansible_eth0["ipv4"]["address"] }} {{ ansible_eth0.ipv4.address }}
On peut ajouter un 'fact' dans un 'task' : set_fact
Example setting host facts using key=value pairs
- set_fact: one_fact="something" other_fact="{{ local_var * 2 }}"
Example setting host facts using complex arguments
- set_fact: one_fact: something other_fact: "{{ local_var * 2 }}"
As of 1.8, Ansible will convert boolean strings ('true', 'false', 'yes', 'no') to proper boolean values when using the key=value syntax, however it is still recommended that booleans be set using the complex argument style:
one_fact: true
other_fact: false
La sortie d'un module peut produire une variable utilisable.
Utiliser “-v” lors du lancement d'un playbook pour voir les valeurs utilisables. Exemple :
tasks:
- shell: /usr/bin/foo register: foo_result ignore_errors: True - shell: /usr/bin/bar when: foo_result.rc == 5
On peut accéder aux variables des autres hosts : hostvars
{{ hostvars['test.example.com']['ansible_distribution'] }}
Pour lister les groupes qui incluent un hote : group_names
{% if 'webserver' in group_names %} # some part of a configuration file that only applies to webservers {% endif %}
Pour lister les hotes et les groupes de l'inventaire : groups
{% for host in groups['app_servers'] %} # something that applies to all app servers. {% endfor %}
{% for host in groups['app_servers'] %} {{ hostvars[host]['ansible_eth0']['ipv4']['address'] }} {% endfor %}
Pour récupérer le FQDN tel que définit dans l'inventaire : inventory_hostname
Pour le hostnmae de l'inventaire : inventory_hostname_short
Pour avoir la liste des hosts concernés par le 'play' courrant : play_hosts
Pour le répertoire et le fichier d'inventaire : inventory_dir / inventory_file
Fichier de variables :
remote_user: root
vars: favcolor: blue vars_files: - /vars/external_vars.yml tasks: - name: this is just a placeholder command: /bin/echo foo
Le fichier de variables est un simple dictionnaire YAML :
# in the above example, this would be vars/external_vars.yml somevar: somevalue password: magic
On peut avoir des fichiers de variables par host, par groupe (voir patterns).
Passer des variables via la ligne de commande :
ansible-playbook release.yml --extra-vars "version=1.23.45 other_variable=foo" ansible-playbook release.yml --extra-vars '{"pacman":"mrs","ghosts":["inky","pinky","clyde","sue"]}' ansible-playbook release.yml --extra-vars "@some_file.json"
On peut donner des conditions aux inclusions de fichiers de variables :
remote_user: root
vars_files: - "vars/common.yml" - [ "vars/{{ ansible_os_family }}.yml", "vars/os_defaults.yml" ]
tasks:
service: name=apache state=running
Mais il faut facter ou ohai installés avant !
On gère ça normalement avec le 'group_by'.
On peut charger des variables dynamiquement dans une task: include_vars:
# Conditionally decide to load in variables when x is 0, otherwise do not. - include_vars: contingency_plan.yml when: x == 0 # Load a variable file based on the OS type, or a default if not found. - include_vars: "{{ item }}" with_first_found: - "{{ ansible_distribution }}.yml" - "{{ ansible_os_family }}.yml" - "default.yml"
ATTENTION : with_first_found cherche dans le répertoire files !!!
Précédence des variables :
Bien utiliser les fichier de variables :
# file: /etc/ansible/group_vars/all # this is the site wide default ntp_server: default-time.example.com
# file: /etc/ansible/group_vars/boston ntp_server: boston-time.example.com
# file: /etc/ansible/host_vars/xyz.boston.example.com ntp_server: override.example.com
Les groupes enfant supplantent les groupes parents.
Les hosts supplantent toujours leur groupes.
Supplanter les variables d'un role : appel avec parametres :
roles: - { name: apache, http_port: 8080 }
Utilisé comme clause dans une 'task'
Utilise les expressions jinja2
tasks: - name: "shutdown Debian flavored systems" command: /sbin/shutdown -t now when: ansible_os_family == "Debian" tasks: - command: /bin/false register: result ignore_errors: True - command: /bin/something when: result|failed - command: /bin/something_else when: result|success - command: /bin/still/something_else when: result|skipped tasks: - shell: echo "This certainly isn't epic!" when: not epic tasks: - command: echo {{ item }} with_items: [ 0, 2, 4, 6, 8, 10 ] when: item > 5
Inclusion conditionnée (roles et taks) :
when: “'reticulating splines' in output”
- hosts: webservers roles: - { role: debian_stock_config, when: ansible_os_family == 'Debian' }
remote_user: root
vars_files: - "vars/common.yml" - [ "vars/{{ ansible_os_family }}.yml", "vars/os_defaults.yml" ] tasks: - name: make sure apache is running service: name={{ apache }} state=running
# for vars/CentOS.yml apache: httpd somethingelse: 42
Templates conditionnnés :
template: src=item dest=/etc/myapp/foo.conf
with_first_found: - files: - {{ ansible_distribution }}.conf - default.conf paths: - search_location_one/somedir/ - /opt/other_location/somedir/
Exemple contruit avec le multiligne sur register :
hosts: all
tasks: - name: retrieve the list of home directories command: ls /home register: home_dirs - name: add home dirs to the backup spooler file: path=/mnt/bkspool/{{ item }} src=/home/{{ item }} state=link with_items: home_dirs.stdout_lines # same as with_items: home_dirs.stdout.split()
Simple : with_items - name: add several users user: name={{ item }} state=present groups=wheel with_items: - testuser1 - testuser2
Items type liste :
user: name=item.name state=present groups=item.groups
with_items: - { name: 'testuser1', groups: 'wheel' } - { name: 'testuser2', groups: 'root' }
mysql_user: name=item_0 priv=item_1.*:ALL append_privs=yes password=foo
with_nested: - users - [ 'clientdb', 'employeedb', 'providerdb' ]
users: alice: name: Alice Appleworth telephone: 123-456-7890 bob: name: Bob Bananarama telephone: 987-654-3210 tasks: - name: Print phone records debug: msg="User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})" with_dict: users
Tous les fichiers d'un seul répertoire, non récursif, correspondant à un motif.
tasks:
# first ensure our target directory exists - file: dest=/etc/fooapp state=directory # copy each file over that matches the given pattern - copy: src={{ item }} dest=/etc/fooapp/ owner=root mode=600 with_fileglob: - /playbooks/files/fooapp/*
RQ : si chemin relatif pour with_fileglob, la base est : roles/<rolename>/files
alpha: [ 'a', 'b', 'c', 'd' ] numbers: [ 1, 2, 3, 4 ] tasks: - debug: msg="{{ item.0 }} and {{ item.1 }}" with_together: - alpha - numbers
users: - name: alice authorized: - /tmp/alice/onekey.pub - /tmp/alice/twokey.pub - name: bob authorized: - /tmp/bob/id_rsa.pub - user: name={{ item.name }} state=present generate_ssh_key=yes with_items: users - authorized_key: "user={{ item.0.name }} key='{{ lookup('file', item.1) }}'" with_subelements: - users - authorized
# create a series of directories with even numbers for some reason - file: dest=/var/stuff/{{ item }} state=directory with_sequence: start=4 end=16 stride=2
On peut formater en plus, faire de l'hexa, …
with_random_choice:
register: result
until: result.stdout.find("all systems go") != -1 retries: 5 delay: 10
template: src=item dest=/etc/file.cfg mode=0444 owner=root group=root
with_first_found: - files: - "{{inventory_hostname}}/etc/file.cfg" paths: - ../../../templates.overwrites - ../../../templates - files: - etc/file.cfg paths: - templates
shell: /usr/bin/frobnicate item
with_lines: /usr/bin/frobnications_per_host --param {{ inventory_hostname }}
MAIS on est sensé plutot utiliser les inventaires dynamiques ou register :
shell: /usr/bin/something
register: command_result - name: Do something with each result shell: /usr/bin/something_else --param {{ item }} with_items: command_result.stdout_lines
debug: msg=“at array position item.0 there is a value item.1”
with_indexed_items: some_list
# file: roles/foo/vars/main.yml packages_base: - [ 'foo-package', 'bar-package' ] packages_apps: - [ ['one-package', 'two-package' ]] - [ ['red-package'], ['blue-package']] - name: flattened loop demo yum: name={{ item }} state=installed with_flattened: - packages_base - packages_apps
with_items:
register: echo
C'est très détaillé :
{ "changed": true, "msg": "All items completed", "results": [ { "changed": true, "cmd": "echo \"one\" ", "delta": "0:00:00.003110", "end": "2013-12-19 12:00:05.187153", "invocation": { "module_args": "echo \"one\"", "module_name": "shell" }, "item": "one", "rc": 0, "start": "2013-12-19 12:00:05.184043", "stderr": "", "stdout": "one" }, { "changed": true, "cmd": "echo \"two\" ", "delta": "0:00:00.002920", "end": "2013-12-19 12:00:05.245502", "invocation": { "module_args": "echo \"two\"", "module_name": "shell" }, "item": "two", "rc": 0, "start": "2013-12-19 12:00:05.242582", "stderr": "", "stdout": "two" } ] }
Inspection :
fail:
msg: "The command ({{ item.cmd }}) did not have a 0 return code" when: item.rc != 0 with_items: echo.results
Combien de hosts doivent être gérés en parallèle au maximum : serial
Cela permet de forcer l'exécution d'un 'play' sur n hosts avant d epasser aux prochaines 'plays'
Permet d'arrêter une execution en cas de problèmes répétés :
max_fail_percentage: 30
serial: 10
Continue tant qu'il n'y a pas plus de 3 échecs sur 10 machines.
Particulièrement utile pour la gestion des cibles de load-balancer, la gestion des fenêtres de hors-service (outage) : delegate_to et local_action :
serial: 5
tasks: - name: take out of load balancer pool command: /usr/bin/take_out_of_pool {{ inventory_hostname }} delegate_to: 127.0.0.1 - name: take out of load balancer pool v2 : shorthand local_action: command /usr/bin/take_out_of_pool {{ inventory_hostname }} - name: actual steps would go here yum: name=acme-web-stack state=latest - name: add back to load balancer pool command: /usr/bin/add_back_to_pool {{ inventory_hostname }} delegate_to: 127.0.0.1
Autre exemple : rsync vers une cible :
tasks: - name: recursively copy files from management server to target local_action: command rsync -a /path/to/files {{ inventory_hostname }}:/path/to/target/
run_once: true
delegate_to: web01.example.org
Ou sur un host particulier :
when: inventory_hostname == webservers[0]
Lancement local :
remote_user: root
tasks: - apt: name=cobbler state=installed environment: http_proxy: http://proxy.example.com:8080
remote_user: root
# here we make a variable named "proxy_env" that is a dictionary vars: proxy_env: http_proxy: http://proxy.example.com:8080 tasks: - apt: name=cobbler state=installed environment: proxy_env
# file: group_vars/boston ntp_server: ntp.bos.example.com backup: bak.bos.example.com proxy_env: http_proxy: http://proxy.bos.example.com:8080 https_proxy: http://proxy.bos.example.com:8080
command: /bin/false
ignore_errors: yes
command: /usr/bin/example-command -x -y -z
register: command_result failed_when: "'FAILED' in command_result.stderr"
tasks: - shell: /usr/bin/billybass --mode="take me to the river" register: bass_result changed_when: "bass_result.rc != 2" # this will never report 'changed' status - shell: wall 'beep' changed_when: False
Sert à utiliser des informations externes (filesystem, centres de stockages externes, services, templates).
Sous forme de plugins.
Fait sur la machine locale.
vars:
contents: "{{ lookup('file', '/etc/foo.txt') }}" tasks: - debug: msg="the value of foo.txt is {{ contents }}"
tasks:
# create a mysql user with a random password: - mysql_user: name={{ client }} password="{{ lookup('password', 'credentials/' + client + '/' + tier + '/' + role + '/mysqlpassword length=15') }}" priv={{ client }}_{{ tier }}_{{ role }}.*:ALL # create a mysql user with a random password using only ascii letters: - mysql_user: name={{ client }} password="{{ lookup('password', '/tmp/passwordfile chars=ascii_letters') }}" priv={{ client }}_{{ tier }}_{{ role }}.*:ALL # create a mysql user with a random password using only digits: - mysql_user: name={{ client }} password="{{ lookup('password', '/tmp/passwordfile chars=digits') }}" priv={{ client }}_{{ tier }}_{{ role }}.*:ALL # create a mysql user with a random password using many different char sets: - mysql_user: name={{ client }} password="{{ lookup('password', '/tmp/passwordfile chars=ascii_letters,digits,hexdigits,punctuation') }}" priv={{ client }}_{{ tier }}_{{ role }}.*:ALL
RQ : le module Vault permet d'enregistrer les mots de passes en chiffré
tasks:
with_lines:
vars: motd_value: "{{ lookup('file', '/etc/motd') }}" tasks: - debug: msg="motd value is {{ motd_value }}"
remote_user: root
vars: from: "camelot" vars_prompt: name: "what is your name?" quest: "what is your quest?" favcolor: "what is your favorite color?"
vars_prompt: - name: "release_version" prompt: "Product release version" default: "1.0"
vars_prompt: - name: "some_password" prompt: "Enter password" private: yes
vars_prompt: - name: "my_password2" prompt: "Enter password2" private: yes encrypt: "md5_crypt" confirm: yes salt_size: 7
Permet de lancer une partie seulement d'un playbook.
Tags est supporté dans les plays, les tasks, les roles et les includes : tags:
tasks: - yum: name={{ item }} state=installed with_items: - httpd - memcached tags: - packages - template: src=templates/src.j2 dest=/etc/foo.conf tags: - configuration roles: - { role: webserver, port: 5000, tags: [ 'web', 'foo' ] }
(l'include ajoute le tag sur toutes les tashs inclues)
ansible-playbook example.yml --tags "configuration,packages" ansible-playbook example.yml --skip-tags "notification"
ansible-vault create foo.yml => demande un mot de passe ansible-vault edit foo.yml ansible-vault rekey foo.yml bar.yml baz.yml ansible-vault encrypt foo.yml bar.yml baz.yml ansible-vault decrypt foo.yml bar.yml baz.yml ansible-playbook site.yml --ask-vault-pass => lance un playbook qui appelle des fichiers chiffrés (avec le même mot de passe) ansible-playbook site.yml --vault-password-file ~/.vault_pass.txt ansible-playbook site.yml --vault-password-file ~/.vault_pass.py
En ligne de commande : -m
ansible webservers -m service -a "name=httpd state=started" ansible webservers -m ping ansible webservers -m command -a "/sbin/reboot -t now"
En playbook :
action: command /sbin/reboot -t now
command: /sbin/reboot -t now
Les modules renvoient du JSON.
Ils sont idempotent : ils vont éviter de faire des changements si ce n'est pas nécessaire, on peut les lancer plusieurs fois sans problème.
Documentation en ligne de commande :
ansible-doc <nom_du_module>
Modules par catégorie :
file:///usr/share/doc/ansible-doc/html/modules_by_category.html
Tous les modules :
file:///usr/share/doc/ansible-doc/html/list_of_all_modules.html
that:
# Example playbook using fail and when together - fail: msg="The system may not be provisioned according to the CMDB status." when: cmdb_status != "to-be-staged"
# Example that prints the loopback address and gateway for each host - debug: msg="System {{ inventory_hostname }} has uuid {{ ansible_product_uuid }}" - debug: msg="System {{ inventory_hostname }} has gateway {{ ansible_default_ipv4.gateway }}" when: ansible_default_ipv4.gateway is defined - shell: /usr/bin/uptime register: result - debug: var=result - name: Display all variables/facts known for a host debug: var=hostvars[inventory_hostname]
Utilise un module qui démarre un démon ZaroMQ temporaire.
# Pause for 5 minutes to build app cache. - pause: minutes=5 # Pause until you can verify updates to an application were successful. - pause: # A helpful reminder of what to look out for post-update. - pause: prompt="Make sure org.foo.FooOverload exception is not present"
# wait 300 seconds for port 8000 to become open on the host, don't start checking for 10 seconds - wait_for: port=8000 delay=10 # wait until the file /tmp/foo is present before continuing - wait_for: path=/tmp/foo # wait until the string "completed" is in the file /tmp/foo before continuing - wait_for: path=/tmp/foo search_regex=completed # wait until the lock file is removed - wait_for: path=/var/lock/file.lock state=absent # wait until the process is finished and pid was destroyed - wait_for: path=/proc/3466/status state=absent # Wait 300 seconds for port 22 to become open and contain "OpenSSH", don't start checking for 10 seconds - local_action: wait_for port=22 host="{{ inventory_hostname }}" search_regex=OpenSSH delay=10
Ajouter le flag '-check' permet de voir si Ansible a beaucoup de changements à faire (mais ne change rien).
Dans le playbook, ajouter always_run
:
roles: - webserver tasks: - script: verify.sh always_run: True
tasks: - wait_for: host={{ inventory_hostname }} port=22 delegate_to: localhost - action: uri url=http://www.example.com return_content=yes register: webpage - fail: msg='service is not happy' when: "'AWESOME' not in webpage.content" - shell: /usr/bin/some-command --parameter value register: cmd_result - assert: that: - "'not ready' not in cmd_result.stderr" - "'gizmo enabled' in cmd_result.stdout" - stat: path=/path/to/something register: p - assert: that: - p.stat.exists and p.stat.isdir
serial: 5
pre_tasks: - name: take out of load balancer pool command: /usr/bin/take_out_of_pool {{ inventory_hostname }} delegate_to: 127.0.0.1 roles: - common - webserver - apply_testing_checks post_tasks: - name: add back to load balancer pool command: /usr/bin/add_back_to_pool {{ inventory_hostname }} delegate_to: 127.0.0.1
Le fichier commence par “—”
Tous les membres d'une liste commencent par “-” :
# A list of tasty fruits - Apple - Orange - Strawberry - Mango
Tous les membres d'un dictionnaire (table de hachage) sont représentés par clef: valeur :
# An employee record name: Example Developer job: Developer skill: Elite
On peut aussi représenter les doctionnaires sous une forme abrégée :
# An employee record {name: Example Developer, job: Developer, skill: Elite}
Les booléens sont représentés de manière multiple :
create_key: yes needs_agent: no knows_oop: True likes_emacs: TRUE uses_cvs: false
Lors des rolling releases, utiliser le paramètre “serial” : permet de conditionner au nombre d'hosts
en état avant de passer au 'play' suivant
Toujours mentionner l'état souhaité pour l'application d'un module : state=present
Grouper l'inventaire par roles.
Pour les différentes version d'OS, utiliser le group-by qui crée des groupes dynamiques :
# talk to all hosts just so we can learn about them - hosts: all tasks: - group_by: key={{ ansible_distribution }} # now just on the CentOS hosts... - hosts: CentOS gather_facts: False tasks: - # tasks that only happen on CentOS go here
Les modules spécifiques à un playbook doivent être mis dans ./library/
Utiliser les lignes vides pour donner de l'air aux documents.
Utiliser # pour commenter
Toujours nommer les 'tasks'
KISS
Utiliser un gestionnaire de versions sur l'arborescence Ansible.
Exemple concret : file:/usr/share/doc/ansible-doc/html/guide_rolling_upgrade.html Exemples : https://github.com/ansible/ansible-examples Jinja2 : http://jinja.pocoo.org/docs/dev/templates/ debops : http://debops.org/ playbook super complet : https://gist.github.com/webstandardcss/9d1a293914d972399712 ===== Utilisation ===== ==== lancement ==== ansible-playbook ./install.yml –inventory-file=./test ==== lancement limité à un groupe ou à un host ==== ansible-playbook ./install.yml –inventory-file=./prod –limit=nommachine ==== limite en plus sur un tag ==== ansible-playbook install.yml -t staging -i prod -l ruche.alocean.com ==== facts ==== ansible -i prod -m setup ruche.alocean.com