Skip to content

Playbook

Playbook (плейбук) в Ansible - это файл YAML, который содержит описание задач и действий, которые Ansible должен выполнить на целевых узлах. Плейбук представляет собой основной инструмент для создания автоматизированных задач конфигурации и управления системами.

Playbook состоит из plays

Play

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

базовая структура плея внутри плейбука
---
- name: Название плея
  hosts: целевые_хосты
  become: yes  # (необязательно) Установите 'yes', если вам нужно выполнять задачи с повышенными привилегиями
  vars:
    имя_переменной: значение  # (необязательно) Переменные на уровне плея
  pre_tasks: # (необязательно) tasks которые нужно выполнить в первую очередь, до roles и tasks, могут обращаться к handlers
  tasks: # (необязательно) можно все делать с помощью roles
    - name: Название задачи
      module_name:
        параметр_модуля: значение
      become: yes  # (необязательно) Привилегии на уровне задачи
  roles: # (необязательно) можно все делать с помощью tasks
    - role-name1
    - role-name2
  post_tasks: # необязательный параметр, перечисление tasks, которые необходимо выполнить после tasks и roles
  handlers: # (необязательно) обработчики событий, запускаются, если какая-то task или role обратилась к handler, могут быть сгруппированы через listen
  tags: # (необязательно) позволяет группировать roles, tasks и вызывать их запуск отдельно от остальных сущностей
Минимальный набор play
# Минимальный набор указаний для выполнения play: play-name, hosts, task(role)-name

- name: Test play # Имя плея
  hosts: all # Целевые хосты
  tasks: # Определение блока тасок
    - name: Debug massage # Имя таски
      debug: # Вызов модуля debug
        msg: "Hello from ansible" # Вывод сообщения в консоль

Зарезервированные параметры которые можно применять в play

Зарезервированные параметры
- any_errors_fatal: true # Когда этот параметр установлен в true, это означает, что любая ошибка (независимо от её типа)
# в процессе выполнения задачи сделает выполнение плейбука неуспешным и вызовет прерывание выполнения плейбука.
- any_errors_fatal: false # По умолчанию - false, что означает, что Ansible будет продолжать
# выполнение плейбука, даже если в процессе выполнения задач возникают ошибки.

- become: yes  # Выполнение задачи с привилегиями суперпользователя
- become_user: root  # Выполнение задачи от имени пользователя root

- check_mode: yes # это опция в Ansible, которая позволяет вам выполнять задачи в режиме проверки без фактического изменения состояния целевой системы. 
# В этом режиме Ansible выполняет все шаги задачи, но не вносит фактических изменений. 
- diff: yes # используется для включения вывода разницы (diff) при выполнении задачи, которая изменяет файлы или конфигурацию. 

- meta:
    force_handlers: true # Этот параметр может использоваться для принудительного выполнения обработчиков (handlers), даже если задачи, вызывающие их, завершились с ошибкой.
- gather_facts # булевая переменная для контроля запуска сбора фактов с хостов
- gather_facts: yes # (по умолчанию): факты будут собраны для всех хостов, указанных в инвентаре, перед выполнением задач в плейбуке. Факты будут доступны в переменной ansible_facts для каждого хоста.
- gather_facts: no # Ansible не будет собирать факты перед выполнением задач. Это может быть полезно, если вам не нужна информация о хостах, и вы хотите ускорить выполнение плейбука.

- ignore_errors: yes # позволяет игнорировать ошибки при выполнении tasks и продолжить выполнение play
- ignore_unreachable: yes # позволяет игнорировать ошибки недоступности хоста и продолжать выполнение play
# Параметр ignore_unreachable полезен, когда вы хотите выполнить плейбук на множестве хостов, но не обязательно требуете, чтобы все они были доступны. 
- max_fail_percentage: 15 # определяет максимальный процент задач, которые могут завершиться с ошибкой, прежде чем выполнение плейбука будет прервано. 
# max_fail_percentage позволяет более гибко управлять тем, какие ошибки следует считать критичными для выполнения плейбука, и какие можно игнорировать.

- order # позволяет указать порядок сортировки хостов из inventory
- run_once # запустить данный play только на первом хосте из inventory в рамках одного batch
- serial # позволяет определить количество хостов, на которых запускается данный play в рамках одного batch

Tasks

Задачи (Tasks): Задачи представляют собой конкретные действия, которые необходимо выполнить на целевых узлах. Каждая задача имеет имя, модуль Ansible (например, apt, yum, file, service, и т. д.), и параметры для выполнения задачи.

пример task
tasks: # Объявление списка tasks
  - name: Get Docker version # Произвольное имя для task
    ansible.builtin.command: docker --version # Что и как необходимо сделать
    register: is_installed # Запись результата в переменную is_installed
    notify: # Директива notify позволяет обратиться к handlers, чтобы он был исполнен в конце выполнения всех tasks
      - Restart Nginx # Вызов handler Restart Nginx
  - name: Get RPM # Произвольное имя для второй task
    ansible.builtin.get_url: # Объявление использования module get_url, ниже указание его параметров
      url: “https://url_с_нужным_пакетом”
    when: # Условия при которых task будет выполняться
      - is_installed is failed

Tasks можно принудительно воспроизвести на указанном хосте: - Для того, чтобы определить на каком хосте необходимо выполнить task - нужно использовать delegate_to. - Если действие необходимо делать на localhost, можно использовать инструкцию local_action

Tasks можно и нужно разделять на pre_tasks, tasks и post_tasks: - Разделение группируется по вашей собственной внутренней логике - Если task внутри этих групп вызвала handler, то он выполнится после того, как закончит исполнение последней task из текущей группы - Внутри набора tasks их можно также группировать при помощи group

Ansible не рекомендует ставить на выполнение такси и роли в одном play. Т.е. если в play выполняет роли, то пусть только их и выполняет, дополнительные таски можно разместить в разделах pre_tasks и post_tasks

Blocks of tasks

В Ansible блоки (blocks) используются для группировки нескольких задач в единый логический блок. Это позволяет применять к ним общие действия, такие как обработка исключений или применение общих условий. Блоки могут быть полезны для повышения читаемости и управляемости кода, особенно при работе с большим количеством задач.

Пример использования блоков
- name: Пример использования блоков
  hosts: localhost
  tasks:
    - name: Блок задач
      block:
        - name: Задача 1
          debug:
            msg: "Выполняется задача 1"
        - name: Задача 2
          debug:
            msg: "Выполняется задача 2"
      # Обработка ошибок, если какая-то задача из блока завершилась неудачно
      rescue:
        - debug:
            msg: "Произошла ошибка при выполнении блока задач"
      # Заключительные действия, выполняемые после выполнения блока задач (независимо от успеха или неудачи)
      always:
        - debug:
            msg: "Выполнение блока задач завершено"

# В этом примере:
# Блок block содержит две задачи, которые будут выполнены вместе как единое действие.
# Если хотя бы одна из задач завершится неудачно, будет выполнен блок rescue, который может содержать дополнительные задачи по обработке ошибок.
# Блок always содержит задачи, которые будут выполнены независимо от результата выполнения задач в блоке block.

Как запустить Playbook?

запуск ansible-playbook
# Первый запуск стоит осуществлять или на тестовом окружении или с флагом --check:
ansible-playbook -i inventory/<inv_file>.yml <playbook_name>.yml --check

# Если были найдены ошибки:
ansible-playbook -i inventory/<inv_file>.yml <playbook_name>.yml --start-at-task <task_name>

# Для запуска исполнения в полуинтерактивном виде:
ansible-playbook -i inventory/<inv_file>.yml <playbook_name>.yml --step

# Полноценный запуск playbook в целевом виде должен выглядеть:
ansible-playbook -i inventory/<inv_file>.yml <playbook_name>.yml

# Запуск плейбука с использованием другого файла инвентаря
ansible-playbook -i my_inventory.ini my_playbook.yml

# Запуск плейбука с передачей дополнительных переменных
ansible-playbook -e "my_var=value" my_playbook.yml

# Запуск плейбука только на определенных хостах
ansible-playbook -l my_host my_playbook.yml

# Запуск плейбука только с задачами, помеченными метками
ansible-playbook -t my_tag my_playbook.yml

# Запуск плейбука в режиме проверки (dry-run)
ansible-playbook --check my_playbook.yml
Как тестировать Playbook?
# В первую очередь, нужно использовать debugger
# Необходимо использовать возможности check
# Организовать запуск на тестовом окружении
# Нужно не забывать про идемпотентность и использовать флаг --diff:
ansible-playbook -i inventory/<inv_file>.yml <playbook_name>.yml --diff
# Тестировать playbook против разного окружения на control node
# Тестировать playbook против разного окружения на managed node
# Активно использовать флаг -vvv

Vars

Переменные в Ansible используются для хранения и передачи данных, которые могут быть использованы в задачах, шаблонах (templates), ролях и других частях автоматизации. Переменные могут быть определены на разных уровнях, и у них есть приоритеты, определяющие, какие значения будут использоваться при выполнении задач.

Основные уровни, на которых могут быть определены переменные:

  1. Глобальные переменные (Global Variables): Глобальные переменные определяются в файле /etc/ansible/ansible.cfg (или в другом конфигурационном файле Ansible). Они применяются ко всем задачам и плеям (plays) на всех хостах и имеют наивысший приоритет.

  2. Переменные инвентаря (Inventory Variables): Вы можете определять переменные для конкретных хостов или групп хостов в вашем файле инвентаря. Эти переменные могут быть определены как в формате INI, так и в формате YAML.

  3. Переменные плейбука (Playbook Variables): Вы можете определять переменные внутри плейбука с помощью ключа vars. Эти переменные будут применяться только к задачам внутри данного плейбука.

  4. Групповые переменные (Group Variables): Вы можете определить групповые переменные в каталоге group_vars. Эти переменные применяются ко всем хостам в соответствующей группе.

  5. Переменные роли (Role Variables): Роли в Ansible могут иметь свои собственные переменные, определенные в каталоге defaults или vars внутри роли. Эти переменные могут быть использованы только внутри соответствующей роли.

Когда переменные определены на нескольких уровнях, Ansible применяет их в соответствии с приоритетами, и переменные с более высоким приоритетом переопределяют значения переменных с более низким приоритетом. Например, переменные плейбука переопределяют групповые переменные, а глобальные переменные имеют приоритет над всеми остальными.

Уровни приоритезации (от меньшего к большему):

  • Значения из командной строки (-u username);
  • Значения по умолчанию из roles;
  • Значения из файла inventory;
  • Значения из файлов group_vars/all;
  • Значения из файлов group_vars/{groupname};
  • Переменные из play;
  • Значения переменных role из vars;
  • Экстра-аргументы из командной строки (-e “user=myuser”).

Handlers

Handlers - это специальные задачи, которые выполняются только в случае изменения состояния системы после выполнения задач. Handlers часто используются для перезапуска служб или выполнения других действий в ответ на изменения конфигурации.

По умолчанию, если задача, вызывающая обработчик, завершилась с ошибкой, обработчик не будет выполнен. Однако, установив параметр force_handlers: true, вы можете заставить Ansible выполнить обработчики, даже если предшествующие задачи завершились с ошибкой.

Handlers используются для проведения одного действия в рамках одного play, например, для рестарта сервиса, после обновления конфигурации.

  • Указывается в директиве Play.
  • На handler могут ссылаться task, role, pre_task, post_task.
  • Вне зависимости от того, сколько раз handler был вызван - он исполнится один раз.
  • Если handler вызван в role - он исполнится после всех roles.
  • Если handler вызван в tasks любого вида - он исполнится в конце tasks, в рамках которого был вызван.
  • Handler, который определён в рамках одного playbook может быть вызван в любом play
Пример синтаксиса Handlers
handlers: # Объявление списка handlers
  - name: restart-prometheus # Произвольное имя для handler
    ansible.builtin.service: # Вызов module, обрабатывающего операции с сервисами
      name: prometheus # Имя сервиса
      state: restarted # Ожидаемый результат работы модуля
    listen: “restart monitoring” # Группировка handlers для возможности вызова группы
  - name: restart-alertmanager
    ansible.builtin.service:
      name: alertmanager
      state: restarted
    listen: “restart monitoring”

Inventory

Inventory (инвентарь) в Ansible - это файл или набор файлов, которые содержат информацию о целевых хостах и их атрибутах, таких как IP-адреса, имена хостов, группы хостов и переменные. Инвентарь используется для определения того, на какие хосты и группы хостов Ansible должен направлять свои задачи и действия.

Инвентарь может быть представлен в различных форматах, таких как INI, YAML или JSON, и может содержать следующую информацию:

  • Имена и IP-адреса хостов: Инвентарь должен указывать, на какие хосты следует направлять задачи. Обычно это включает в себя имена хостов или их IP-адреса.
  • Группы хостов: Хосты могут быть объединены в группы. Группы могут иметь описательные имена, и вы можете выполнять задачи на всей группе хостов. Группы также могут вложиться в другие группы для создания иерархии.
  • Переменные хостов и групп: Вы можете назначать переменные хостам и группам в инвентаре. Эти переменные используются для настройки задач и плейбуков. Например, вы можете определить переменные, такие как ansible_user (пользователь, используемый для подключения) или ansible_ssh_pass (пароль SSH) для хостов.

INI inventary

В этом примере есть две группы хостов (web_servers и db_servers) и каждая группа содержит переменные хостов (ansible_host и ansible_user). Группы также имеют свои собственные переменные (web_var и db_var).

Пример инвентаря в формате INI
[web_servers]
web1 ansible_host=192.168.1.10 ansible_user=webuser

[db_servers]
db1 ansible_host=192.168.1.11 ansible_user=dbuser

[web_servers:vars]
web_var=web_value

[db_servers:vars]
db_var=db_value

YAML inventary

Это пример структуры файла инвентаря в формате YAML. В этом файле определены группы хостов (group1, group2) и их характеристики, такие как IP-адреса, пользователи для подключения и другие параметры. Каждая группа хостов может также иметь свои собственные групповые переменные (vars).

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

пример инвентаря в формате YAML
all:
  children:  # Секция "children" содержит определения групп хостов
    group1:  # Имя группы
      hosts:  # Секция "hosts" содержит список хостов в группе
        host1:
          ansible_host: 192.168.1.10  # IP-адрес или DNS-имя хоста
          ansible_user: user1  # Имя пользователя для подключения
          ansible_port: 22  # (необязательно) Порт SSH (по умолчанию 22)
          ansible_ssh_private_key_file: /путь/к/ключу/id_rsa  # (необязательно) Путь к ключу SSH
          ansible_ssh_pass: пароль  # (необязательно) Пароль SSH (не рекомендуется)
          переменная1: значение1  # (необязательно) Дополнительные переменные хоста
        host2:
          ansible_host: 192.168.1.11
          ansible_user: user2
        # Дополнительные хосты в группе group1
      vars:  # (необязательно) Секция "vars" содержит переменные, применяемые к группе
        групповая_переменная1: значение1
        групповая_переменная2: значение2
    group2:
      hosts:
        host3:
          ansible_host: 192.168.1.12
          ansible_user: user3
        # Дополнительные хосты в группе group2
      vars:
        групповая_переменная3: значение3
  vars:  # (необязательно) Глобальные переменные для всех хостов
    глобальная_переменная1: значение1
    глобальная_переменная2: значение2

Inventary path

Ansible автоматически ищет инвентарь в стандартных местоположениях, но вы также можете явно указать путь к инвентарю, используя опцию -i или --inventory-file при выполнении команд Ansible.

Пример использования инвентаря в команде Ansible
ansible -i inventory.ini web_servers -m ping
# В этой команде Ansible использует инвентарь из файла inventory.ini и выполняет модуль ping на группе хостов web_servers.

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

Пример использования инвентаря в команде Ansible
ansible-playbook -i inventory.yml -l web_servers my_playbook.yml
# В этой команде -i используется для указания пути к инвентарю YAML (inventory.yml),
# а -l используется для указания целевой группы хостов (web_servers).

Templates (jinja2)

Templates (шаблоны) - это способ создания и управления файлами конфигурации, которые могут быть динамически сгенерированы в зависимости от переменных и данных из вашей инфраструктуры. Это особенно полезно при настройке приложений или служб, где конфигурационные файлы должны быть настроены на основе конкретных условий на целевых хостах.

Вот как работают шаблоны в Ansible:

  • Создание шаблона: Вы создаете файл-шаблон, который содержит текстовое описание конфигурационного файла, в котором вместо конкретных значений используются переменные и выражения Jinja2. Jinja2 - это язык шаблонов, который позволяет вставлять переменные и выполнение логики в текстовых файлах.
  • Передача переменных: Вы передаете переменные в шаблон из плейбука Ansible. Эти переменные могут содержать информацию о конфигурации, которую вы хотите применить к хостам.
  • Рендеринг шаблона: Ansible занимается рендерингом (заменой) переменных в шаблоне с использованием данных, переданных из плейбука. Результатом является конфигурационный файл, который будет использоваться на целевых хостах.
  • Копирование и применение: Закончив рендеринг, Ansible копирует полученный файл-шаблон на целевые хосты и применяет его в соответствии с вашими задачами и плейбуками.
Пример использования шаблона в плейбуке Ansible
---
- name: Configure Nginx
  hosts: web_servers
  tasks:
    - name: Create Nginx configuration file
      template:
        src: nginx.conf.j2  # Исходный шаблон
        dest: /etc/nginx/nginx.conf  # Путь, куда будет скопирован конфигурационный файл
      notify:
        - Reload Nginx

  handlers:
    - name: Reload Nginx
      service:
        name: nginx
        state: reloaded

# В этом примере мы используем задачу template для создания конфигурационного файла Nginx. 
# Файл nginx.conf.j2 - это шаблон, который может содержать переменные и выражения Jinja2. 
# Ansible рендерит этот шаблон, заменяя переменные, и копирует результат в /etc/nginx/nginx.conf на целевых хостах.

Tag

Tag позволяет пометить какую-либо сущность ansible для отдельного исполнения.

Их можно выставлять для:

  • Отдельной task.
  • Группы tasks.
  • Include.
  • Play.
  • Role.
  • Import

Существует два выделенных tags:

  • always - выполняется всегда, если явно не указано пропустить.
  • never - не выполняется никогда, если явно не указано запустить

Список ключей для использования tags:

  • --tags all
  • --tags tagged
  • --tags untagged
  • --tags [tag1, tag2]
  • --skip-tags [tag1, tag2]
  • --list-tags
  • --list-tasks (with --tags or --skip-tags)

Facts

Ansible Facts - это информация о хосте, на котором выполняется автоматизация с помощью Ansible. Эта информация автоматически собирается Ansible и предоставляется в виде переменных, которые можно использовать в плейбуках и ролях. Факты Ansible полезны для:

  • Динамической конфигурации: Факты позволяют плейбукам и ролям адаптироваться к хостам на основе их характеристик. Например, вы можете использовать факты, чтобы определить, на какой операционной системе работает хост, и выполнить разные действия в зависимости от этого.
  • Универсальности и переносимости: Используя факты, вы можете писать более универсальные и переносимые плейбуки, которые могут работать на разных хостах с разными конфигурациями.
  • Определения ресурсов и задач: Факты могут использоваться для определения доступных ресурсов на хосте, таких как диски, память, CPU и т. д., что позволяет плейбукам принимать решения на основе доступных ресурсов.
  • Сбора информации: Факты могут быть использованы для сбора информации о хосте, которая затем может быть передана другим системам или использована для отчетности и мониторинга.
  • Управления настройками и конфигурацией: Факты могут использоваться для настройки параметров и конфигураций на хосте в зависимости от его характеристик.

Примеры фактов в Ansible включают информацию о версии операционной системы, IP-адресах, физических и логических ядрах CPU, объемах дисков, установленных пакетах, переменных окружения и многом другом.

Факты Ansible доступны в переменной ansible_facts:

  • ansible_facts['ansible_distribution']: Информация о дистрибутиве операционной системы, например, "Ubuntu" или "CentOS".
  • ansible_facts['ansible_distribution_version']: Версия дистрибутива операционной системы.
  • ansible_facts['ansible_architecture']: Архитектура хоста, например, "x86_64".
  • ansible_facts['ansible_os_family']: Семейство операционных систем, к которому принадлежит хост (например, "Debian" или "RedHat").
  • ansible_facts['ansible_kernel']: Версия ядра операционной системы.
  • ansible_facts['ansible_hostname']: Имя хоста.
  • ansible_facts['ansible_fqdn']: Полное доменное имя (Fully Qualified Domain Name) хоста.
  • ansible_facts['ansible_default_ipv4']: Информация о первом IPv4-интерфейсе хоста, включая IP-адрес, маску подсети и другие атрибуты.
  • ansible_facts['ansible_default_ipv6']: Аналогичная информация для IPv6-интерфейса.
  • ansible_facts['ansible_processor_vcpus']: Количество виртуальных процессоров (vCPUs) на хосте.
  • ansible_facts['ansible_memtotal_mb']: Общее количество оперативной памяти (в мегабайтах) на хосте.
  • ansible_facts['ansible_swaptotal_mb']: Общий объем подкачки (в мегабайтах) на хосте.
  • ansible_facts['ansible_userspace_arch']: Архитектура пространства пользователя (например, "x86_64" или "i686").
  • ansible_facts['ansible_date_time']: Текущее дата и время на хосте.
  • ansible_facts['ansible_interfaces']: Список сетевых интерфейсов на хосте и их характеристики.
  • ansible_facts['ansible_users']: Информация о пользователях на хосте.

В следующем примере мы используем модуль debug для вывода значения факта ansible_distribution. Важно использовать одинарные кавычки внутри скобок для обращения к факту.

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

print ansible fact
---
- name: Пример обращения к факту
  hosts: my_host
  tasks:
    - name: Вывести значение факта ansible_distribution
      debug:
        var: ansible_facts['ansible_distribution']

When - условный оператор

В Ansible для применения условных операторов можно использовать модуль when, который позволяет выполнять задачи в зависимости от значений переменных, результатов предыдущих задач и других условий.

Проверка значения переменной
- name: Проверка значения переменной
  debug:
    msg: "Переменная value равна 10"
  when: value == 10
Проверка наличия файла
- name: Проверка наличия файла
  stat:
    path: /path/to/file
  register: file_info
  # Проверка, что файл существует
  when: file_info.stat.exists
Комбинирование условий с использованием логических операторов
- name: Проверка условий
  debug:
    msg: "Значение переменной равно 10 и файл существует"
  when: (value == 10) and (file_info.stat.exists)
Использование фильтров и функций для проверки условий
- name: Проверка условий с использованием фильтра length
  debug:
    msg: "Список не пустой"
  when: my_list | length > 0
Использование результата предыдущих задач
- name: Получение списка файлов
  find:
    paths: /path/to/directory
  register: files_list

- name: Проверка наличия файлов
  debug:
    msg: "Найдены файлы в директории"
  when: files_list.files | length > 0

Errors

Методы обработки ошибок в Ansible.

Использование блока block с секциями rescue и always
- name: Пример обработки ошибок
  block:
    - name: Задача 1
      command: /path/to/failing/command
rescue:
  - name: Обработка ошибок
    debug:
      msg: "Произошла ошибка при выполнении задачи"
always:
  - name: Завершающее действие
    debug:
      msg: "Выполнение завершено"
Использование модуля fail для явного генерирования ошибки
- name: Пример генерации ошибки
  command: /path/to/failing/command
  register: result
  failed_when: result.rc != 0
  ignore_errors: true

- name: Обработка ошибок
  fail:
    msg: "Произошла ошибка при выполнении задачи"
  when: result is failed
Использование директивы ignore_errors
- name: Пример игнорирования ошибок
  command: /path/to/failing/command
  ignore_errors: true
Использование модуля assert для проверки условий и генерации ошибки, если условие не выполнено
- name: Пример использования модуля assert
  hosts: localhost
  tasks:
    - name: Проверка значения переменной
      assert:
        that:
          - my_variable == "expected_value"
        fail_msg: "my_variable не соответствует ожидаемому значению"
        success_msg: "my_variable соответствует ожидаемому значению"
      vars:
        my_variable: "unexpected_value"

# Модуль assert используется для проверки того, что переменная my_variable равна ожидаемому значению "expected_value".
# Если условие не выполнено (то есть my_variable не равно "expected_value"),
# будет сгенерирована ошибка с сообщением "my_variable не соответствует ожидаемому значению".
# Если условие выполнено (то есть my_variable равно "expected_value"),
# будет выведено сообщение "my_variable соответствует ожидаемому значению".