Les bases d'Ansible, sans lesquelles vos playbooks sont des tas de pâtes, partie 3

Dans cette partie, nous arrêtons de parler du simple et de l'agréable et commençons à parler du difficile. Variables dans Ansible: portée, priorité, interpolation récursive. Pour ceux qui lisent jusqu'au bout, un petit plus: une table de priorité simplifiée à vivre. Parties précédentes: 1 , 2 .



Habituellement, une histoire sur les variables dans Ansible commence par quelque chose de très simple, qui donne au lecteur l'illusion que les variables dans Ansible sont comme dans n'importe quel autre langage de programmation. Mutable ou non mutable, local et global. Ce n'est pas vrai.



Ce n'est pas vrai.



Ansible a un modèle de variable unique (modèle de mémoire?), Qui doit être appris à partir de zéro. Et nous commencerons à le considérer à partir de l'endroit où les valeurs sont utilisées (généralement les variables Ansible sont considérées à partir de là où elles apparaissent). Pourquoi? Parce qu'en disant dans cette direction, nous avons un graphe orienté, ce qui est beaucoup plus facile à mettre dans nos têtes.



Remarque - J'ai dit «valeurs» car les «variables» ne sont que des noms de valeurs. Les variables ont leur propre monde intérieur profond, et à ce sujet dans la deuxième partie.



Tout au long de cette histoire, j'utiliserai les termes «apparaît» et / «est utilisé». Apparaît - c'est là que la valeur a été définie. Et «frappe» - c'est le lieu où la valeur commence à influencer le travail d'Ansible, plus précisément ses effets secondaires - sur le processus réel d'exécution des modules dans le système cible. Pendant que les variables sont déplacées d'un endroit à l'autre dans la section vars, les valeurs ne «vont» nulle part et n'affectent en aucune façon le monde environnant.



C'est la première pensée importante dont vous devez vous souvenir dans votre tête: tant que la valeur n'a pas été utilisée par quelque chose qui affecte le monde qui l'entoure, elle peut contenir des erreurs de syntaxe et des références à des variables inexistantes, etc., et cela ne déroutera personne. Pourquoi - lisez la suite.



Alors, où sont les valeurs utilisées?



  1. . - copy: src=foo.conf dest=/etc/foo.conf. foo.conf /etc/foo.conf — . . , , , . , jinja, / , (, , , , ).
  2. ( action plugin) 'template' lookup plugin' template. ( lookup plugin , , ).
  3. play. play ( play) play. , jinja2 gather_facts, play. — hosts, remote_user .
  4. , . , ansible_host, ansbile_user, ansible_extra_ssh_args, ansible_transport .. .


, :



- name: Do not do this
  file: path=hello.txt state=touch
  loop: '{{ groups.all }}'
  vars:
     ansible_host: '{{ hostvars[item].ansible_host }}'


. "" ( , ).



?



  1. ansible_host ( ) {{ hostvars[item].ansible_host }}. . yaml, .
  2. loop. {{ groups.all }}. — jinja. , . loop , , item " ".
  3. hello.txt touch. jinja, .
  4. file ansible_host ssh — . , ansible_host Jinja, . , (.. loop). , jinja item, (.. item - ). , . ? . , .


— . , , - . set_fact, ( ), .



. , ( best practice) IP- :



allow_access: '{{ ansible_default_ipv4.address }}'


, setup ( gathering_facts), allow_access Jinja , — , .



Jinja



— Jinja ( ). , , (), ( ). : - . . - , . , . , ( , ..). , Jinja , .



:



- debug:
     msg: '{{ message }}'
  vars:
    foo: 'foo'
    foobar: '{{ foo + "bar" }}'
    message: 'This is {{ foobar }}' 


, 'msg' 'debug' ( , action plugin, ), {{ message }} . ({{ }}) message. This is {{foobar }}. This is {{ foo + "bar" }}. This is foobar. , .. . msg debug.



, , , .



WTF, " ".



- hosts: localhost
  tasks:
    - debug: msg={{foo}}
       vars:
          foo: ''{{ foo + 1 }}'
  vars:
     foo: 1


play, "" foo . play, . debug? msg . , {{ foo + 1 }}, {{ foo + 1 }} + 1 .. , .



:



- hosts: localhost
  tasks:
    - set_fact:
         foo: '{{ foo + 1 }}'
     - debug: msg={{foo}}
  vars:
     foo: 1


""? ? set_fact - foo. foo, . , , foo ( 1) , foo . , .



, , . .



.



- hosts: localhost
  vars:
    foo: '{{ bar * 2 }}'
  tasks:
    - debug: var=foo
      loop: [1,2,3]
      vars:
          bar: '{{ item + 10 }}'


vars play ( ). WTF , .



? 1, 2, 3. :

foo '{{ bar * 2 }}' bar '{{ item + 10 }}'. , vars ( play, ), ( ) , . . , "", - .



debug. foo {{ bar *2 }}, {{ (item + 10) * 2 }}. item ( loop') 22, 24, 26.



— , , , , .. scope/precedence ( ), , .



:



- hosts: localhost
  vars:
     foo: '{{ bar }}'
  tasks:
    - debug: var=foo
      vars:
         bar: 'one value'
     - debug: var=foo
       vars:
           bar: 'another value'


. , , bar . Mystery solved.



Jinja



, (, jinja) . jinja. {{ }}, {% if True %} "" {%endif %}. . yaml .



- foo_module:
      username: <
           {% for user in myusers %}
                   {% if user.lower() in good and user.upper() in other %}
                          {{ user }}
                    {% endif %}
           {% endfor %}


'content' file. :



- name: Configure sfcapd systemd service
  become: true
  copy:
    content: |
      [Unit]
      Description=sflow capture service
      [Service]
      Type=simple
      ExecStart=/usr/bin/sfcapd sfcapd -w -T{{ sflow_extensions }} -p {{ sflow_port }} -B {{ sflow_buffer_size }} -l {{ sflow_data_path }} -b {{ sflow_host }}
      Restart=always
      [Install]
      WantedBy=multi-user.target

    dest: /etc/systemd/system/sfcapd.service
  notify:
    - restart sfcapd


? 100500 . , . template, copy content. , .



. .



- (, prometheus Go), . yaml- .



Ansible : safe unsafe. safe- — , . unsafe .



:



- debug: 
    msg: !unsafe'{{ inventory_hostname }}'


" ", {{ inventory_hostname }}.

, .



— , ? , , : . , , ( ).





, , -, PHP .



.



---
- hosts: localhost
  gather_facts: false
  tasks:
    - name: Case1
      debug: var=item
      loop: '{{ [1,2,3,4] }}'
    - name: Case2
      debug: var=item
      loop: '{{ foo + bar }}'
    - name: Case3
      debug: var=item
      loop: ' {{ [9,10] }}'
  vars:
    foo: '[5,6'
    bar: '7,8]'


Case1 Jinja . , jinja2 , . , yaml'.



Case2 ( — , ) — , , - , . PHP.



Case3 — , .



Case2 Case3 — , . , , . ( ) json. , ( json').



Ansible:



    - name: Case6, space at the end
      debug: var=item
      loop: '{{ [15, 16] }} '
    - name: Case7, space at the start
      debug: var=item
      loop: ' {{ [15, 16] }}'


Case6 , loop ( ), Case7 , . ? . .



… . , , WTF' .



: , . 5 , , . ", ". , - — .



, : yaml, .



Scope ""



. -"", scope . :



  1. , inventory group_vars.
  2. play, play. ( , "play" — , ).
  3. task, .
  4. , . ( — ).


include_role , include_role ( ), — include_role. , include .



Scope — scope scope:



---
- hosts: localhost
  gather_facts: false
  vars:
    foo: 2
  tasks:
    - name: Case1
      debug: var=foo
      vars:
        foo: 1
    - name: Case2
      debug: var=foo


play, foo=1, scope , foo scope play, foo 2. ( precedence ). "" "", ( ). "" — include_vars, set_fact, register ..



Variable precedence



. include_role -"". , - scope, , . variable precedence ( — , ).



: , . , , http_port: 8088, , http_port: 80 . -e .. , , group_vars/.



, , foo



inventory.yaml   # [all:vars] foo=inventory
group_vars/all.yaml  # foo: g_v_a
playbook.yaml


playbook.yaml



- debug: var=foo


?



… , variable precedence , , group_vars .



, , : host_group_vars .



host_group_vars



( ) host_group_vars. , group vars host vars. , . group_vars/all.yaml , . playbook, playbook.



, , group_vars . , variable precedence (playbook), group_vars .



. group_vars , . , . , , , .. .



group_vars/other, group_vars/all, host_vars .. , .



, , — .



. precedence WTF, : ( ), hostvars .





, , WTF'.



  • role/defaults — . , . pre/post tasks.
  • group_vars/all — all
  • group_vars/other_groups
  • host_vars
  • gather_facts: true (host_facts)
  • play
  • block
  • task
  • set_fact/register. set_fact , (.. play).
  • -e .


, ( ). "". .



: group_vars/all group_vars/other_group.yaml.



,



, . include' (, , ), , import include', add_host, .. set_fact . , . jinja, . group_host_vars () play. , , .



Keep it clean, keep it simple.




All Articles