Введение
Автоматизация шаблонов сохраняет один источник, создавая конфигурацию из входных данных каждого хоста. Вы сформируете файлы из переменных и фактов, преобразуете заголовок встроенным фильтром Jinja, выберете условную ветвь и загрузите проверенную конфигурацию nginx через обработчик. Настоящие запросы HTTP и отклонённый некорректный кандидат покажут разницу между исходным текстом, управляемыми файлами и поведением действующей службы.
Сформируйте управляемое содержимое из переменных хостов и фактов
На этом шаге вы создадите один шаблон страницы и сформируете из одного источника разные настоящие файлы на обоих целевых узлах.
На подготовленных узлах nginx уже установлен как предварительное условие. Ваша работа — формирование и применение конфигурации, а не повторная установка пакета. Шаблоны формируются на управляющем узле с контекстом переменных каждого целевого узла, затем передаются в управляемые места назначения.
Перейдите в проект управляющего узла и создайте каталог templates. mkdir -p создаёт каталог при необходимости без ошибки, если он уже существует:
cd /home/labex/project
mkdir -p templates
Определите типизированные входные данные хостов. Окружение и порт прослушивания различаются, а значения maintenance_mode без кавычек — логические значения YAML:
cat > host_vars/web1.yml <<'YAML'
environment_label: development
service_port: 8081
maintenance_mode: true
YAML
cat > host_vars/web2.yml <<'YAML'
environment_label: production
service_port: 8082
maintenance_mode: false
YAML
Выражения Jinja в двойных фигурных скобках вставляют значения в текст. Суффикс .j2 — полезное соглашение для источников шаблонов, а не требование к имени управляемого файла назначения. Создайте один источник, использующий текущее имя инвентаря, переменную окружения и обнаруженный факт дистрибутива:
cat > templates/page.txt.j2 <<'JINJA'
host={{ inventory_hostname }}
environment={{ environment_label }}
distribution={{ ansible_facts.distribution }}
JINJA
Используйте модуль template для формирования. Copy обычно перенёс бы байты источника без интерпретации этих выражений Jinja. Соберите факты перед использованием значения дистрибутива и явно задайте владельца и права управляемого файла:
cat > templates.yml <<'YAML'
- name: Render host-specific website content
hosts: all
gather_facts: true
become: true
tasks:
- name: Prepare the rendered site directory
ansible.builtin.file:
path: /var/www/template-site
state: directory
owner: root
group: root
mode: "0755"
- name: Render each host's website content
ansible.builtin.template:
src: templates/page.txt.j2
dest: /var/www/template-site/index.html
owner: root
group: root
mode: "0644"
YAML
Сформируйте обе управляемые страницы:
ansible-playbook templates.yml --syntax-check
ansible-playbook templates.yml
Прочитайте фактические целевые файлы:
ansible all -m ansible.builtin.command -a 'cat /var/www/template-site/index.html'
Каждая страница должна обозначать собственный хост, соответствующее окружение development/production и distribution=Ubuntu. На управляющем узле по-прежнему один многократно используемый источник, а два управляемых результата различаются.
Добавьте встроенный фильтр и условную ветвь конфигурации
На этом шаге вы сформируете корректную конфигурацию nginx, меняющую поведение HTTP по входным данным каждого хоста.
Фильтр Jinja преобразует значение через символ вертикальной черты. upper — встроенный фильтр, переводящий текст в верхний регистр; это не пользовательский плагин. Управляющие инструкции Jinja используют разделители из фигурной скобки и знака процента. Ветвь if/else/endif ниже выбирает один заголовок обслуживания по логическому входному значению.
Создайте полный шаблон конфигурации nginx. Порт, заголовок окружения и заголовок обслуживания поступают из данных каждого хоста. Настройки root и index обслуживают уже сформированную страницу:
cat > templates/nginx.conf.j2 <<'JINJA'
events {}
http {
server_tokens off;
server {
listen {{ service_port }};
root /var/www/template-site;
index index.html;
add_header X-Environment "{{ environment_label | upper }}" always;
{% if maintenance_mode %}
add_header X-Maintenance "enabled" always;
{% else %}
add_header X-Maintenance "disabled" always;
{% endif %}
}
}
JINJA
Добавьте задачи конфигурации и службы и именованный обработчик перезагрузки в существующий плейбук. Команда validate задачи template проверяет временный сформированный кандидат (%s) перед заменой управляемой конфигурации. Изменённая корректная конфигурация уведомляет обработчик; обычная задача service сохраняет nginx запущенным.
cat >> templates.yml <<'YAML'
- name: Render and validate the nginx candidate before replacement
ansible.builtin.template:
src: templates/nginx.conf.j2
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: "0644"
validate: /usr/sbin/nginx -t -c %s
notify: Reload nginx
- name: Keep the rendered site service running
ansible.builtin.service:
name: nginx
state: started
handlers:
- name: Reload nginx
ansible.builtin.service:
name: nginx
state: reloaded
YAML
Примените полный плейбук:
ansible-playbook templates.yml
Задача конфигурации должна изменить оба хоста и вызвать обработчик перезагрузки. Изучите web1 на порту 8081 и web2 на порту 8082. curl -i включает заголовки ответа вместе с телом:
ansible web1 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8081/'
ansible web2 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8082/'
Web1 должен возвращать X-Environment: DEVELOPMENT и X-Maintenance: enabled; web2 — X-Environment: PRODUCTION и X-Maintenance: disabled. Оба тела сохраняют правильную страницу своего хоста. Даты заголовков меняются; сравнивайте значения, полученные из данных, и успешный статус.
Измените входные данные и отклоните некорректный кандидат
На этом шаге вы обновите постоянные входные данные без редактирования шаблонов, увидите фактический действующий результат и подтвердите, что проверка защищает рабочую конфигурацию.
Измените сохранённое окружение web1 на staging и его режим обслуживания на false, сохраняя существующий порт:
cat > host_vars/web1.yml <<'YAML'
environment_label: staging
service_port: 8081
maintenance_mode: false
YAML
Примените те же исходные шаблоны:
ansible-playbook templates.yml
Страница и конфигурация web1 должны измениться, уведомив его обработчик перезагрузки; web2 должен остаться без изменений. Снова изучите web1:
ansible web1 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8081/'
Его тело должно показывать environment=staging с X-Environment: STAGING и X-Maintenance: disabled. Шаблоны остались многократно используемыми при изменении входных данных.
Теперь проверьте отклонение кандидата. cp сохраняет корректный источник управляющего узла, чтобы его можно было восстановить. Замените только шаблон nginx заведомо некорректным текстом:
cp templates/nginx.conf.j2 templates/nginx.conf.j2.good
printf 'invalid nginx configuration;\n' > templates/nginx.conf.j2
Следующий запуск плейбука ожидаемо не пройдёт проверку кандидата. Оператор оболочки || выводит сообщение только после ненулевого кода выхода, позволяя продолжить это упражнение с намеренным сбоем:
ansible-playbook templates.yml || printf 'Expected candidate validation rejection\n'
Прочитайте ошибку задачи: nginx должен отклонить временный кандидат. Рабочая управляемая конфигурация должна остаться на месте, поэтому обе действующие службы по-прежнему должны отвечать правильно:
ansible web1 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8081/'
ansible web2 -m ansible.builtin.command -a 'curl --fail --silent --show-error -i http://127.0.0.1:8082/'
mv восстанавливает сохранённый корректный источник под исходным именем. Запустите полный плейбук ещё раз:
mv templates/nginx.conf.j2.good templates/nginx.conf.j2
ansible-playbook templates.yml
Оба хоста должны сообщать changed=0, failed=0 и unreachable=0 без запланированного обработчика перезагрузки. Проверка защитила действующую конфигурацию, не сделав копию некорректного кандидата желаемым итоговым состоянием.
Резюме
Вы сформировали содержимое из переменных хостов и фактов модулем template, использовали встроенный фильтр upper и логическую ветвь Jinja и применили проверенную конфигурацию через обработчик. Изменённые входные данные дали другой действующий результат без переписывания источников. Некорректный кандидат был отклонён до замены, а восстановленные корректные шаблоны достигли нужного состояния без лишних изменений.


