Ansible template: проверяем конфигурацию до замены рабочего файла

Jinja-шаблон отрендерился без ошибки, задача стала changed, а после restart приложение не поднялось из-за некорректной конфигурации. Рендеринг подтвердил только возможность подстановки переменных. Чтобы не устанавливать заведомо неверный файл, модулю template можно дать команду проверки временного кандидата.

Параметр validate запускает проверку до замены dest. Если команда возвращает ошибку, задача не должна устанавливать кандидат в рабочий путь. Это полезная граница: действующий файл сохраняется, пока новый не удовлетворяет выбранному контракту.

Проверим маленькую конфигурацию приложения

Пример рассчитан на Linux-хост с /usr/bin/python3. Наш учебный формат JSON содержит единственный параметр port: целое число от 1024 до 65535. Реальный сервис может иметь другой контракт, но проверка должна точно соответствовать его требованиям.

Сохраните validate_config.py рядом с playbook. Валидатор проверяет не только JSON-синтаксис, но и допустимое значение, возвращая ошибку при нарушении.

import json
import sys
from pathlib import Path

try:
    config = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
    if not isinstance(config, dict) or set(config) != {"port"}:
        raise ValueError("Expected exactly one key: port")
    port = config["port"]
    if type(port) is not int or not 1024 <= port <= 65535:
        raise ValueError("port must be an integer from 1024 to 65535")
except (OSError, ValueError, IndexError) as error:
    print(str(error), file=sys.stderr)
    sys.exit(1)

Файл app.json.j2 состоит из одной строки. To_json сохраняет корректное представление значения, а валидатор отдельно проверяет его смысл и тип.

{"port": {{ app_port | to_json }}}

В demo.yml сначала устанавливается валидатор, затем создаётся кандидат конфигурации. Все файлы находятся в отдельном учебном каталоге.

- name: Validate before installing configuration
  hosts: localhost
  connection: local
  gather_facts: false
  vars:
    app_port: 8080
  tasks:
    - name: Prepare demo directory
      ansible.builtin.file:
        path: /tmp/latorn-template-demo
        state: directory
        mode: '0700'
    - name: Install validator
      ansible.builtin.copy:
        src: validate_config.py
        dest: /tmp/latorn-template-demo/validate_config.py
        mode: '0600'
    - name: Install a valid configuration
      ansible.builtin.template:
        src: app.json.j2
        dest: /tmp/latorn-template-demo/app.json
        mode: '0600'
        validate: /usr/bin/python3 /tmp/latorn-template-demo/validate_config.py %s

Выполните ansible-playbook -i localhost, demo.yml. Ожидается app.json с числовым 8080. Затем повторите с -e '{"app_port": 70000}'. Валидатор должен отклонить кандидат, задача — завершиться ошибкой, а установленный файл — сохранить 8080. Отдельно проверьте -e '{"app_port": true}': bool в Python является подтипом int, поэтому type(port) is int выбран намеренно.

В validate передают путь кандидата

Маркер %s обязателен: template подставляет временный путь. Команда выполняется на целевом хосте, поэтому там должны существовать Python, валидатор и нужные вспомогательные файлы. Проверка старого dest вместо %s дала бы ложный успех: старый файл корректен, новый ещё никто не читал.

Validate не запускается через обычный shell, поэтому pipe и перенаправления не работают как в терминале. Для сложной проверки используйте отдельный скрипт с понятными аргументами. У настоящих сервисов часто уже есть штатные валидаторы, например sshd -t -f или visudo -cf; предпочтительнее проверять именно формат, который сервис будет читать.

Проверка файла ещё не проверяет работу сервиса

Наш валидатор подтверждает контракт JSON и диапазон port. Он не проверяет, свободен ли порт, есть ли доступ к базе и способен ли процесс стартовать. Эти проверки нужны после установки и запуска отдельно. Для конфигураций с include убедитесь, что проверяется весь набор, а временное расположение не ломает относительные ссылки.

После подключения restart handler уведомляется только при успешном изменении template. Неверный кандидат должен остановиться раньше этой точки. Проверяйте отрицательный сценарий сохранением контрольной суммы рабочего dest до и после попытки, а затем исправляйте значение и подтверждайте успешное применение. Так validate становится проверяемым предохранителем перед заменой файла, а не ещё одной командой после поломки сервиса.

Ещё по теме

Продолжить изучение

Хотите собрать настройку серверов в повторяемый процесс? Inventory, роли, шаблоны и обновление сервисов входят в программу «Ansible: автоматизация инфраструктуры».

Продолжить на практике

  • Ansible: автоматизация инфраструктуры ↗

    Настраивайте серверы воспроизводимо: inventory, playbooks, роли, шаблоны и секреты. Соберите автоматизацию для нескольких окружений, проверьте повторный запуск и обновляйте сервисы управляемо.