ИТ Блог. Администрирование серверов на основе Linux (Ubuntu, Debian, CentOS, openSUSE)
Понедельник, 31 марта, 2025
Сегодня у нас 1 праздник:
Международный День Резервного Копирования (World Backup Day). Пользователи сайта социальных новостей reddit предложили сделать дату 31.03 Международным днём резервного копирования, аргументируя это тем, что никогда заранее нельзя узнать, какие сюрпризы преподнесёт 1.04

Ansible. Шаблоны Jinja2

Начало работы с Ansible

В предыдущей статье по принятию решений в Ansible вы узнали, как вносить простые изменения в файлы с помощью блоков blockinfile или встроенных модулей Ansible.

В этой статье вы узнаете, как использовать механизм шаблонов Jinja2 для выполнения более сложных и динамических изменений файлов.

Вы узнаете, как получить доступ к переменным и фактам в шаблонах Jinja2. Кроме того, вы узнаете, как использовать условные операторы и структуры циклов в Jinja2.

 

Доступ к переменным в Jinja2

Ansible будет искать файлы шаблонов jinja2 в каталоге вашего проекта или в каталоге с именем templates в каталоге вашего проекта.

Давайте создадим каталог шаблонов, чтобы все было чище и организовано:

[andreyex@control plays]$ mkdir templates
[andreyex@control plays]$ cd templates/

 

Теперь создайте свой первый шаблон Jinja2 с именем index.j2 :

[andreyex@control templates]$ cat index.j2 
A message from {{ inventory_hostname }}
{{ webserver_message }}

 

Обратите внимание, что имена файлов шаблонов Jinja2 должны заканчиваться расширением .j2.

Inventory_hostname еще один встроенный Ansible ( так называемый специальной или волшебный). Webserver_message является переменной, что вы будете определять в вашем PlayBook.

Теперь вернитесь на шаг назад в каталог вашего проекта и создайте следующий файл check-apache.yml :

[andreyex@control plays]$ cat check-apache.yml 
---
- name: Check if Apache is Working
  hosts: webservers
  vars:
    webserver_message: "I am running to the finish line."
  tasks:
    - name: Start httpd
      service:
        name: httpd
        state: started

    - name: Create index.html using Jinja2
      template:
        src: index.j2
        dest: /var/www/html/index.html

 

Обратите внимание, что пакет httpd уже был установлен в предыдущем руководстве.

В этом руководстве вы сначала убедитесь, что Apache запущен в первой задаче Start httpd. Затем используйте модуль шаблона во второй задаче Create index.html с помощью процесса Jinja2to и перенесите созданный вами файл шаблона index.j2 Jinja2 в место назначения /var/www/html/index.html.

Идите вперед и запустите playbook:

[andreyex@control plays]$ ansible-playbook check-apache.yml 

PLAY [Check if Apache is Working] **********************************************

TASK [Gathering Facts] *********************************************************
ok: [node3]
ok: [node2]

TASK [Start httpd] *************************************************************
ok: [node2]
ok: [node3]

TASK [Create index.html using Jinja2] ******************************************
changed: [node3]
changed: [node2]

PLAY RECAP *********************************************************************
node2                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    
node3                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0

 

Пока все выглядит хорошо; давайте запустим быструю специальную команду Ansible, чтобы проверить содержимое index.html на узлах веб-серверов:

[andreyex@control plays]$ ansible webservers -m command -a "cat /var/www/html/index.html"
node3 | CHANGED | rc=0 >>
A message from node3
I am running to the finish line.
node2 | CHANGED | rc=0 >>
A message from node2
I am running to the finish line.

 

Удивительно! Обратите внимание, как Jinja2 смог получить значения встроенной переменной inventory_hostname и переменной webserver_message в вашем playbook.

Вы также можете использовать команду curl, чтобы узнать , получили ли вы ответ от обоих веб-серверов:

[andreyex@control plays]$ curl node2.andreyexru.local
A message from node2
I am running to the finish line.
[andreyex@control plays]$ curl node3.andreyexru.local
A message from node3
I am running to the finish line.

Доступ к фактам в Jinja2

Вы можете получить доступ к фактам в шаблонах Jinja2 так же, как вы получаете доступ к фактам из своей книги.

Для демонстрации перейдите в каталог шаблонов и создайте файл info.j2 Jinja2 со следующим содержимым:

[andreyex@control templates]$ cat info.j2 
Server Information Summary
--------------------------

hostname={{ ansible_facts['hostname'] }}
fqdn={{ ansible_facts['fqdn'] }}
ipaddr={{ ansible_facts['default_ipv4']['address'] }}
distro={{ ansible_facts['distribution'] }}
distro_version={{ ansible_facts['distribution_version'] }}
nameservers={{ ansible_facts['dns']['nameservers'] }}
totalmem={{ ansible_facts['memtotal_mb'] }}
freemem={{ ansible_facts['memfree_mb'] }}

 

Обратите внимание, что info.j2 имеет доступ к восьми различным фактам. Теперь вернитесь в каталог своего проекта и создайте следующий playbook server-info.yml :

[andreyex@control plays]$ cat server-info.yml 
---
- name: Server Information Summary
  hosts: all
  tasks:
   - name: Create server-info.txt using Jinja2
     template:
       src: info.j2
       dest: /tmp/server-info.txt

 

Обратите внимание, что вы создаете /tmp/server-info.txt на всех хостах на основе файла шаблона info.j2. Идите вперед и запустите playbook:

[andreyex@control plays]$ ansible-playbook server-info.yml 

PLAY [Server Information Summary] *******************************************

TASK [Gathering Facts] **********************************
ok: [node4]
ok: [node1]
ok: [node3]
ok: [node2]

TASK [Create server-info.txt using Jinja2] ********
changed: [node4]
changed: [node1]
changed: [node3]
changed: [node2]

PLAY RECAP *************************
node1                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    
node2                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    
node3                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    
node4                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0

 

Все хорошо! Теперь давайте запустим быструю специальную команду, чтобы проверить содержимое файла /tmp/server-info.txt на одном из узлов:

[andreyex@control plays]$ ansible node1 -m command -a "cat /tmp/server-info.txt"
node1 | CHANGED | rc=0 >>
Server Information Summary
--------------------------

hostname=node1
fqdn=node1.andreyexru.local
ipaddr=10.0.0.5
distro=CentOS
distro_version=8.2
nameservers=['168.63.129.16']
totalmem=1896
freemem=1087

 

Как видите, Jinja2 смог получить доступ и обработать все факты.

 

Условные операторы в Jinja2

Вы можете использовать условный оператор if в Jinja2 для тестирования различных условий и сравнения переменных. Это позволяет определить последовательность выполнения шаблона файла в соответствии с условиями тестирования.

Для демонстрации перейдите в каталог шаблонов и создайте следующий шаблон selinux.j2 :

[andreyex@control templates]$ cat selinux.j2 
{% set selinux_status = ansible_facts['selinux']['status'] %}

{% if selinux_status == "enabled" %}
	"SELINUX IS ENABLED"
{% elif selinux_status == "disabled" %}
	"SELINUX IS DISABLED"
{% else %}
	"SELINUX IS NOT AVAILABLE"
{% endif %}

 

Первый оператор в шаблоне создает новую переменную, для которой selinux_status установлено значение ansible_facts[‘selinux’][‘status’].

Затем вы можете использовать selinux_status в вашем, если условия испытания, чтобы определить, является ли SELinux включен, отключен или не установлен. В каждом из трех разных случаев вы отображаете сообщение, отражающее статус Selinux.

Обратите внимание, как если утверждение в jinja2 мимике Python, заявление if; только не забывайте использовать {% endif %}.

Теперь вернитесь в каталог своего проекта и создайте следующую playbook в selinux-status.yml :

[andreyex@control plays]$ cat selinux-status.yml 
---
- name: Check SELinux Status
  hosts: all
  tasks:
    - name: Display SELinux Status
      debug:
        msg: "{{ ansible_facts['selinux']['status'] }}"

    - name: Create selinux.out using Jinja2
      template:
        src: selinux.j2
        dest: /tmp/selinux.out

 

Идите вперед и запустите playbook:

[andreyex@control plays]$ ansible-playbook selinux-status.yml 

PLAY [Check SELinux Status] ****************************************************

TASK [Gathering Facts] *********************************************************
ok: [node4]
ok: [node2]
ok: [node3]
ok: [node1]

TASK [Display SELinux Status] **************************************************
ok: [node1] => {
    "msg": "enabled"
}
ok: [node2] => {
    "msg": "disabled"
}
ok: [node3] => {
    "msg": "enabled"
}
ok: [node4] => {
    "msg": "Missing selinux Python library"
}

TASK [Create selinux.out using Jinja2] *****************************************
changed: [node4]
changed: [node1]
changed: [node3]
changed: [node2]

PLAY RECAP *********************************************************************
node1                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    
node2                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    
node3                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0    node4                      : ok=3    changed=1    unreachable=0    failed=0    skipped=0

 

Из вывода playbook; вы можете видеть, что SELinux включен как на node1, так и на node3. Мы отключили SELinux на node2 перед запуском playbook, а на node4 не установлен SELinux, потому что Ubuntu использует AppArmor вместо SELinux.

Наконец, вы можете запустить следующую специальную команду для проверки содержимого selinux.out на всех управляемых хостах:

[andreyex@control plays]$ ansible all -m command -a "cat /tmp/selinux.out"
node4 | CHANGED | rc=0 >>

	"SELINUX IS NOT AVAILABLE"
 
node2 | CHANGED | rc=0 >>

	"SELINUX IS DISABLED"
 
node3 | CHANGED | rc=0 >>

	"SELINUX IS ENABLED"
 
node1 | CHANGED | rc=0 >>

	"SELINUX IS ENABLED"

Цикл в Jinja2

Вы можете использовать оператор for в Jinja2 для перебора элементов в списке, диапазоне и т. д. Например, следующий цикл for будет перебирать числа в диапазоне (1,11) и, следовательно, будет отображать числа от 1 до 10:

{% for i in range(1,11) %}
	Number {{ i }}
{% endfor %}

 

Обратите внимание, как цикл for в Jinja2 имитирует синтаксис цикла for Python; снова не забудьте завершить цикл с помощью {% endfor %}.

Теперь давайте создадим полный пример, демонстрирующий возможности циклов for в Jinja2. Перейдите в каталог шаблонов и создайте следующий файл шаблона hosts.j2:

[andreyex@control templates]$ cat hosts.j2 
{% for host in groups['all'] %}
{{ hostvars[host].ansible_facts.default_ipv4.address }}  {{ hostvars[host].ansible_facts.fqdn }}  {{ hostvars[host].ansible_facts.hostname }}
{% endfor %}

 

Обратите внимание, что здесь вы использовали новую встроенную специальную (магическую) переменную hostvars, которая в основном представляет собой словарь, содержащий все хосты в инвентаре и назначенные им переменные.

Вы перебрали все хосты в своем инвентаре, а затем для каждого хоста; вы отобразили значение трех переменных:

  1. {{hostvars [host] .ansible_facts.default_ipv4.address}}
  2. {{hostvars [host] .ansible_facts.fqdn}}
  3. {{hostvars [host] .ansible_facts.hostname}}

 

Также обратите внимание, что вы должны включить эти три переменные в одну строку рядом, чтобы соответствовать формату файла /etc/hosts.

Теперь вернитесь в каталог ваших проектов и создайте следующий playbook local-dns.yml:

[andreyex@control plays]$ cat local-dns.yml 
---
- name: Dynamically Update /etc/hosts File
  hosts: all
  tasks:
    - name: Update /etc/hosts using Jinja2
      template:
        src: hosts.j2
        dest: /etc/hosts

 

Тогда продолжайте и запустите playbook:

[andreyex@control plays]$ ansible-playbook local-dns.yml 

PLAY [Dynamically Update /etc/hosts File] *********************************************

TASK [Gathering Facts] ***************************
ok: [node4]
ok: [node2]
ok: [node1]
ok: [node3]

TASK [Update /etc/hosts using Jinja2] ***********************************************
changed: [node4]
changed: [node3]
changed: [node1]
changed: [node2]

PLAY RECAP **********************
node1                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    
node2                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    
node3                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0    
node4                      : ok=2    changed=1    unreachable=0    failed=0    skipped=0

 

Пока все выглядит хорошо; теперь выполните следующую специальную команду, чтобы убедиться, что файл /etc/hosts правильно обновлен на узле node1:

[andreyex@control plays]$ ansible node1 -m command -a "cat /etc/hosts"
node1 | CHANGED | rc=0 >>
10.0.0.5  node1.andreyexru.local  node1
10.0.0.6  node2.andreyexru.local  node2
10.0.0.7  node3.andreyexru.local  node3
10.0.0.8  node4.andreyexru.local  node4

 

Отлично! Выглядит правильно отформатированным, как вы и ожидали.

Надеюсь, теперь вы осознали всю мощь шаблонов Jinja2 в Ansible. Следите за обновлениями, и вы увидите следующее руководство, в котором вы научитесь защищать конфиденциальную информацию и файлы с помощью Ansible Vault.

Exit mobile version