Системный подход к переменным в Ansible

  • Tutorial

ansible devops codestyle


Hey! Меня зовут Денис Калюжный я работаю инженером в отделе автоматизации процессов разработки. Каждый день новые сборки приложений раскатываются на сотнях серверов кампании. И в этой статье я делюсь опытом использования Ansible для этих целей.


Этот гайд предлагает способ организации переменных в деплое. Рассчитан данный гайд на тех кто уже использует роли в своих плейбуках и читал BestPractices, но сталкивается с подобными проблемами:


  • Найдя переменную в коде, невозможно сходу понять за что она отвечает;
  • Есть несколько ролей, и переменные нужно связать одним значением, но никак не получается;
  • Возникают трудности в объяснении другим, как устроена логика переменных в ваших плейбуках

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


image


Переменные в ролях


Роль — это отдельный Объект системы деплоя. Как и любой объект системы, он должен иметь интерфейс взаимодействия с остальной системой. Таким интерфейсом являются переменные роли.


Возьмём, для примера, роль api, которая устанавливает Java приложение на сервер. Какие переменные у неё могут быть?


image


Переменные роли можно разделить на 2 вида по типу:


1. Свойства
    a) независимые от среды
    б) зависимые от среды
2. Связи
    a) слушатели 
    б) запросы внутри системы
    в) запросы в среду

Переменные свойства — это переменные, которые определяют поведение роли.


Переменные запроса — это переменные, значение которых используется для обозначения внешних, по отношению к роли, ресурсов.


Переменные слушатели — это переменные, значение которых, используется для формирования переменных запроса.


С другой стороны, 1а, 2а, 2б — это переменные, которые не зависят от среды (железо, внешние ресурсы и т.д.) и могут быть заполнены дефолтными значениями в defaults роли. Однако переменные типа 1.б и 2.в заполнить кроме как 'example' значениями невозможно, так как они будут меняться от стенда к стенду в зависимости от окружения.


Code style


  • Название переменной обязательно должно начинаться с названия роли. Это позволит в дальнейшем легко разобраться, из какой роли переменная и за что она отвечает.
  • При использовании переменных в ролях вы должны обязательно следовать принципу инкапсуляции и использовать переменные определённые либо в самой роли, либо в ролях, от которых текущая зависит.
  • Старайтесь не использовать словари для переменных. Ansible не позволяет удобно переопределять отдельные значения в словаре.


    Пример плохой переменной:


    myrole_user:
        login: admin
        password: admin

    Здесь login — средонезависимая переменная, а password — зависимая. Но
    поскольку они объединены в словарь, вам придётся задавать её полностью
    всегда. Что очень неудобно. Лучше так:


    myrole_user_login: admin
    myrole_user_password: admin


Переменные в плейбуках деплоя


При составлении плейбука деплоя (далее плейбук), мы придерживаемся правила, что он должен размещаться в отдельном репозитории. Так же, как и роли: каждая в своем git репозитории. Это позволяет осозновать, что роли и плейбук — это разные независимые объекты системы деплоя, и изменения в одном объекте не должны влиять на работу другой. Достигается это изменением дефолтных значений переменных.


При составлении плейбука, если обобщить, существует возможность переопределять дефолтные значения переменных роли в двух местах: в переменных плейбука и в переменных инвентори.


mydeploy                        # Каталог деплоя
├── deploy.yml                  # Плейбук деплоя
├── group_vars                  # Каталог переменных плейбука
│   ├── all.yml                 # Файл для переменных связи всей системы
│   └── myapi.yml               # Файл переменных свойств группы myapi
└── inventories                 #
    └── prod                    # Каталог окружения prod
        ├── prod.ini            # Инвентори файл
        └── group_vars          # Каталог для переменных инвентори
            └── myapi           #
                ├── vars.yml    # Средозависимые переменные группы myapi
                └── vault.yml   # Секреты (всегда средозависимы) *

* — Variables and Vaults


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


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


Это означает, что уже на этом этапе нужно определиться является ли переменная средозависимой или нет и разместить ее в положенном месте.


Например, в одном проекте переменная, отвечающая за включение SSL долго была средозависимой, поскольку мы не могли включить SSL по независящим от нас причинам на одном из стендов. После того, как мы устранили эту проблему, она стала средонезависимой и переместилась в переменные плейбука.


Переменные свойств для групп


Расширим нашу модель на рисунке 1, добавив 2 группы серверов с другим Java приложением, но с разными настройками.


image


Представим, как будет выглядить плейбук в этом случае:


- hosts: myapi
  roles:
    - api

- hosts: bbauth
  roles:
    - auth

- hosts: ghauth
  roles:
    - auth

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


Code Style


  • Старайтесь вообще не использовать host_vars переменные, поскольку они не описывают систему, а только частный случай, что в перспективе приведёт к вопросам: "А почему этот хост отличается от остальных?", ответ на который не всегда легко найти.

Переменные связи


Однако это то, что касается переменных свойств, но как быть с переменными связи?
Их отличие в том, что они должны иметь одинаковое значение в разных группах.


По началу была идея использовать монструозную конструкцию вида:
hostvars[groups['bbauth'][0]]['auth_bind_port'], но от неё сразу отказались
поскольку она имеет недостатки. Во-первых, громоздкость. Во-вторых, зависимость от определенного хоста в группе. В-третьих, необходимо перед началом деплоя собрать факты со всех хостов, если мы не хотим получить ошибку неопределённой переменной.


В итоге решено было использовать переменные связи.


Переменные связи — это переменные, которые принадлежат плейбуку, и нужны для связи объектов системы.


Переменные связи заполняются в общих переменных системы group_vars/all/vars и образуются путём выноса всех переменных слушателей из каждой группы, и добавлением в начало переменной название группы откуда слушатель был вынесен.


Таким образом обеспечивается однотипность и непересекаемость имён.


Попробуем связать переменные из примера выше:


image


Представим, что у нас есть переменные, которые друг от друга зависят:


# roles/api/defaults:
# Переменная запроса
api_auth1_address: "http://example.com:80"
api_auth2_address: "http://example2.com:80"

# roles/auth/defaults:
# Переменная слушатель
auth_bind_port: "20000"

Вынесем в общие переменные group_vars/all/vars всех слушателей, и добавим в название имя группы:


# group_vars/all/vars
bbauth_auth_bind_port: "20000"
ghauth_auth_bind_port: "30000"

# group_vars/bbauth/vars
auth_bind_port: "{{ bbauth_auth_bind_port }}"

# group_vars/ghauth/vars
auth_bind_port: "{{ ghauth_auth_bind_port }}"

# group_vars/myapi/vars
api_auth1_address: "http://{{ bbauth_auth_service_name }}:{{ bbauth_auth_bind_port }}"
api_auth2_address: "http://{{ ghauth_auth_service_name }}:{{ ghauth_auth_bind_port }}"

Теперь, меняя значение коннектора, мы будем уверены, что запрос будет обращаться туда же, где расположен порт.


Code Style


  • Поскольку роли и группы это разные объекты системы, нужно чтобы они имели разные названия, тогда переменные связи будут точно показывать, что они принадлежат конкретной группе серверов, а не роли в системе.

Средозависимые файлы


В ролях могут использоваться файлы, которые отличаются от среды к среде.


Примером таких файлов можно назвать SSL-сертификаты. Хранить их в текстовом виде
в переменной не очень удобно. Зато удобно хранить путь до них внутри переменной.


Например, используем переменную api_ssl_key_file: "/path/to/file".


Поскольку очевидно, что сертификат ключа будет меняться от среды к среде, то это средозависимая переменная, а значит она должна расположиться в файле
group_vars/myapi/vars инвентори переменных, и содержать значение 'для примера'.


Удобнее всего в этом случае положить файл ключа в репозиторий плейбука по пути
files/prod/certs/myapi.key, тогда значение переменной будет:
api_ssl_key_file: "prod/certs/myapi.key". Удобство же заключается в том, что люди отвечающие за разворачивание системы на конкретном стенде, так же имеют своё выделенное место в репозитории для хранения своих файлов. В то же время остаётся возможность указать абсолютный путь до сертификата на сервере, на случай если сертификаты поставляются другой системой.





Несколько стендов в одной среде


Часто возникает потребность развернуть несколько практически идентичных стендов в одной среде с минимальными различиями. В этом случае мы делим средозависимые переменные на те, что не меняются в рамках этой среды и те, что меняются. И выносим последние непосредственно в сами инвентори файлы. После этой манипуляции становится возможным создать ещё один инвентори прямо в каталоге окружения.


Он будет переиспользовать инвентори group_vars, а также иметь возможность переопределить некоторые переменные непосредственно под себя.





Окончательная структура каталогов для проекта деплоя:


mydeploy                        # Каталог деплоя
├── deploy.yml                  # Плейбук деплоя
├── files                       # Каталог для файлов деплоя
│   ├── prod                    # Католог для средозависимых файлов стенда prod
│   │   └── certs               # 
│   │       └── myapi.key       #
│   └── test1                   # Каталог для средозависимых файлов стенда test1
├── group_vars                  # Каталог переменных плейбука
│   ├── all.yml                 # Файл для переменных связи всей системы
│   ├── myapi.yml               # Файл переменных свойств группы myapi
│   ├── bbauth.yml              # 
│   └── ghauth.yml              #
└── inventories                 #
    ├── prod                    # Каталог окружения prod
    │   ├── group_vars          # Каталог для переменных инвентори
    │   │   ├── myapi           #
    │   │   │   ├── vars.yml    # Средозависимые переменные группы myapi
    │   │   │   └── vault.yml   # Секреты (всегда средозависимы)
    │   │   ├── bbauth          # 
    │   │   │   ├── vars.yml    #
    │   │   │   └── vault.yml   #
    │   │   └── ghauth          #
    │   │       ├── vars.yml    #
    │   │       └── vault.yml   #
    │   └── prod.ini            # Инвентори стенда prod
    └── test                    # Каталог окружения test
        ├── group_vars          #
        │   ├── myapi           #
        │   │   ├── vars.yml    #
        │   │   └── vault.yml   #
        │   ├── bbauth          #
        │   │   ├── vars.yml    #
        │   │   └── vault.yml   #
        │   └── ghauth          #
        │       ├── vars.yml    #
        │       └── vault.yml   #
        ├── test1.ini           # Инвентори стенда test1 в среде test
        └── test2.ini           # Инвентори стенда test2 в среде test

Подведение итога


После организации переменных в соответствии со статьёй: каждый файл с переменными отвечает за определённую задачу. А раз у файла есть определённые задачи, то стало возможным назначить ответственного за правильность каждого файла. Например, за правильность заполнения переменных плейбука ответственным становится разработчик деплоя системы, в то время как за заполнение инвентори переменных отвечает непосредственно администратор, стенд которого описан в инвентори.


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


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


Литература


  1. Документация

Автор


Калюжный Денис Александрович

Комментарии 10

    +3

    Вы честно пытаетесь программировать на Ансибл, даже с java сравниваете. Минимальный набор соглашений по именованиям хорошо иметь, но чем больше вы в него вкладываете усилий, тем больше вы хотите на него полагаться. А этого делать нельзя. Почему?


    Потому что:
    а) Роли не изолированы друг от друга. vars/main одной роли перебивает defaults/main другой. Вы можете старательно избегать этого используя переменные с префиксами, но это как на ассемблере писать — одна ошибка (или опечатка) и вы уже меняете переменную другой роли самым неожиданным образом. Особенно весело это при выполнени register/set_fact для чужого имени где-то в задворках второстепенной роли
    б) Роли не изолированы друг от друга по handler'ам. Это означает, что рано или поздно у вас две роли будут иметь два разных хэндлера с одинаковым названием, и какой-то из них выполнится.
    в) Хранение ролей отдельно от плейбук чаще всего смысла не имеет, потому что роль пишется под проект. Чем больше роль пишется как универсальная библиотека, тем больше там желания получить изоляцию, а её в Ansible нет, то есть тем более ужасной становится роль.
    г) Не всё можно реализовать на Ансибл и сохранить при этом чистоту кода. Deal with this.


    Мы только что закончили рефакторинг куска кода, в котором использовалось перекладывание переменных (связующие переменные), вместо этого переменные используются ровно там, где нужны и не больше. Код стал раз в пять понятнее и проще.


    Ансибл — не для программирования.

      0
      Написание кода имеет схожие проблемы независимо от языка или инструмента. Если вы начинаете дублировать самого себя (а написание ролей под каждый проект, приводит к дублированию одинаковых мест стека), будет трудно найти ошибку и трудно внести изменения — поскольку вместо одной роли вам нужно перелопатить k (количество проектов в компании) ролей. Также нарушаются принципы унификации и стандартизации.
      Поэтому, так важно все же добиться изоляции, чтобы зона влияния роли не выходила за свои рамки, из-за пересечения имен переменных. Ошибки же ловятся и исправляются на тех же этапах, что и в программировании: тестирование и проверка pr. А те, что вышли в свет, оперативно исправляются поскольку ошибка локализована в коде.

      Если ansible не может сделать что-то красиво из коробки, нужно делать в виде python модуля с теми же принципами заложенными ansible.
        0
        а написание ролей под каждый проект, приводит к дублированию одинаковых мест стека

        А нету двух абсолютно одинаковых проектов и требования в них постоянно меняются, поэтому универсальными ролями усеяна дорога в ад, над которым висит неоновая табличка с надписью Ansible Galaxy.


        В 99% случаев проще писать роли под каждый проект — пусть их будет 100500, НО при этом каждая из них должна быть максимально простой, настолько, чтобы за пару минут можно было с нуля в неё врубиться. Условно, это 1-2 страницы тупого стандартного ямла с минимумом переменных и максимумом хардкода.


        Вот здесь, а не в универсальности ролей, должна быть унификация и стандартизация. Ансибл — не для программирования.

          +2
          А нету двух абсолютно одинаковых проектов и требования в них постоянно меняются

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


          Допустим на проектах используется haproxy, на одном как балансировщик, на другом как реверс-прокси. Пишется роль покрывающая все эти случаи и используется одна, на всех проектах.


          Конфигурация в данном случае выглядит достаточно просто:


          - hosts: proxy
            roles:
              - haproxy
            vars:
              haproxy:
                defaults:
                  log: 'global'
                  mode: 'http'
                  option:
                    - 'httplog'
                    - 'dontlognull'
          
                servers:
                  frontend:
                    proxy:
                      bind: '0.0.0.0:80'
                      default_backend: 'service'
                      mode: 'http'
                  backend:
                    service:
                      mode: 'http'
                      option:
                        - 'forwardfor'
                        - 'httpchk HEAD / HTTP/1.1\r\nHost:localhost'
                      servers:
                        local_service:
                          url: '127.0.0.1:8080'
          
          - hosts: balance
            roles:
              - haproxy
            vars:
              haproxy:
                servers:
                  frontend:
                    proxy:
                      bind: '0.0.0.0:80'
                      default_backend: 'service'
                      mode: 'http'
                  backend:
                    service:
                      mode: 'http'
                      balance: 'leastconn'
                      timeout:
                        queue: '3s'
                        connect: '3s'
                        server: '5s'
                      servers:
                        global:
                          - 'maxconn 500'
                          - 'check'
                          - 'fall 1'
                          - 'rise 3'
                          - 'inter 5s'
                        service1:
                          url: 'service1.fqdn.example:80'
                        service2:
                          url: 'service2.fqdn.example:80'
                          parameters:
                            - 'maxconn 200'
                            - 'check'
                            - 'fall 2'
                            - 'rise 4'
                            - 'inter 10s'

          Формируется данный конфиг в процессе шаблонизации jinja:


          {% for server_type,configuration in haproxy_install.servers.items() %}
            {% for server,parameters in configuration.items() %}
          
          {{ server_type }} {{ server }}
              {% for parameter,properties in parameters.items() %}
                {% if parameter == 'servers' %}
                  {% for instance,params in properties.items() %}
                    {% if instance is not in [ 'global' ] %}
            server {{ instance }} {{ params.url }} {{ (params.parameters|default(properties.global|default([])))|join(' ') }}
                    {% endif %}
                  {% endfor %}
                {% else %}
                  {% if properties is mapping %}
                    {% for key,value in properties.items() %}
            {{ parameter }} {{  key }} {{ value }}
                    {% endfor %}
                  {% elif ((properties is not mapping) and (properties is not string) and (properties is iterable)) %}
                    {% for value in properties %}
            {{ parameter }} {{ value }}
                    {% endfor %}
                  {% else %}
            {{ parameter }} {{ properties }}
                  {% endif %}
                {% endif %}
              {% endfor %}
            {% endfor %}
          {% endfor %}

          Естественно, что в роли есть еще дефолты других параметров, например параметры запуска демона для юнита, логирования, пользователи, cipher-сьюты сервера для SSL и так далее, и тому подобное. Все они записываются в дефолты и, при желании переписываются в плейбуке. Ansible не самый удобный CM инструмент для подобных вещей, как например SaltStack или тот же Puppet, однако, при желании, можно и его заставить делать то, что требуется:


          Старайтесь не использовать словари для переменных. Ansible не позволяет удобно переопределять отдельные значения в словаре.

          nspk — Списки нет, а словари запросто.


          haproxy_default:
            daemon:
              options: []
            dirs:
              conf: '/etc/haproxy'
              defaults: "/etc/{{ 'default' if ansible_os_family|lower == 'debian' else 'sysconfig' }}"
              services: '/lib/systemd/system'
              run: '/run/haproxy'
              home: '/var/lib/haproxy'
            shell: "{{ '/usr' if ansible_os_family|lower == 'debian' else '' }}/sbin/nologin"
            global:
              log:
                - '/dev/log local0'
              user: 'haproxy'
              group: 'haproxy'
              ssl:
                ciphers:
                  - 'ECDHE-ECDSA-AES128-GCM-SHA256'
                  - 'ECDHE-RSA-AES128-GCM-SHA256'
                  - 'ECDHE-ECDSA-AES256-GCM-SHA384'
                  - 'ECDHE-RSA-AES256-GCM-SHA384'
                  - 'ECDHE-ECDSA-CHACHA20-POLY1305'
                  - 'ECDHE-RSA-CHACHA20-POLY1305'
                  - 'DHE-RSA-AES128-GCM-SHA256'
                  - 'DHE-RSA-AES256-GCM-SHA384'
                options:
                  - 'no-sslv3'
                  - 'no-tlsv10'
                  - 'no-tlsv11'
                  - 'no-tls-tickets'
          
          haproxy_install: "{{ haproxy_default|combine(haproxy|default({}), recursive=True) }}"

          Далее в роли используем мерженные значения из "{{ haproxy_install }}"


          Собственно, пример выше, как раз таки показывает как использовать именно унифицированные роли и отказаться от копи-паста и тонн мусорных репозиториев.


          Ансибл — не для программирования.

          amarao — Нет конечно, но вот шаблонизации через jinja вполне себе.

            +1

            На нынешней работе один бывший коллега написал универсальную роль для nginx в подобном стиле — нехилый такой Jinja-темплейт и весь конфиг… всего в одной переменной. Но зато какой переменной — понять потом, куда что нужно добавить, чтобы в окончательном конфиге появилось вот то-то и то-то, было очень непросто.


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

              0

              Наворотить, конечно, можно что угодно — "бумага все терпит".


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


              Была у нас конфигурация iptables и, очевидно, использовалась повсеместно — сотни серверов, физических и виртуальных("облака" отнесем виртуальным), разные заказчики и конечно разные среды, так вот за 5 лет эта конфигурация прошла лишь 1 мажорную и 2 минорных версии — в первом случае совместимость конфигурации была частично потеряна, ввиду типа передачи параметров, в двух других случаях, естественно нет, хоть и добавлялась поддержка всех таблиц и цепочек, управление ipset.


              P.S. Подобный подход в написании конфигураций успешно применяю более 5 лет, работали с ними мои сотрудники разных уровней компетенции и проблем не возникало, кроме того поставленный процесс CI, содержащий тестирование позволял легко проверить конечный результат в считаные минуты, что делало внесение тех или иных изменений простым и предсказуемым.
              P.P.S Сталкивался в работе и с подходом одна конфигурация — один енв, максимум хардкода, копипаст между енвами, неправильный уровень абстракции конфигурационной единицы. Одна закравшаяся ошибка разлеталась на кучи енвов благодаря копипасту, управление становилось настолько проблематичным, что в подавляющем большинстве, внесение изменений осуществлялось вручную, а затем подгонялось в конфигурации под сделанное вручную, в лучшем случае — в худшем оставлялось как есть.
              Десятки часов работы, зато все при деле=)

                +1

                Именно. Хорошо написанный Ансибл не должен привлекать внимания а гордость им должна вызывать удивление "и чем тут гордиться-то? Всё тривиально".


                Буквально недавно была история на MR, когда человек написал потрясающе изящную конструкцию, в которой использовались set_fact с делегацией по списку и делегацией фактов.


                После 15-минутного обсуждения, вся эта изящная конструкция схлопнулась на перенос переменной в инвентори из одного места в другое и тривиальную роль с loop'ом по собственной переменной.


                Было "wow", стало настолько скучно, что ревьюить нечего.


                И вот это — хороший Ансибл.

            0

            Смотрите, любой язык программирования, за вычетом первых экспериментов и basic'а уровня gwbasic, предоставляет критически важную функцию для написания проекта. Эта функция — изоляция.


            Вы пишите модуль (функцию) и никого, кроме ревьюера, не волнует, как называются ваши локальные переменные. Даже если у вас 10 функций в которых есть одна и та же переменая (server_ip), то они друг другу не мешают.


            В Ансибле — не так. В ансибле у вас они не просто друг другу мешают, они ещё и создают удивительный мир оверлейных переменных, когда одна роль задаёт server_ip в дефолтах, вторая в vars, третья в set_fact, и догадаться, чья тут переменная сейчас главная — невозможно.


            Вы можете сколько угодно бороться за "зону влияния", но вы не можете поменять один простой факт: любая операция с перменной в ансибле даёт side effect. Вы не можете писать чистые функции, вы не можете использовать изоляцию.


            И даже если вы свою бизнес-логику вынесете в плагин (не модуль! Модуль вам не поможет), то вы заткнёте один конкретный кейс.


            Человечество прошло большой путь от машинного кода для Эниака к языкам с изоляцией и типизацией. Изоляция стала настолько очевидной, что её даже перестали писать в фичах новых языков. Но тут появился Ансибл, который не язык программирования, при применении которого в качестве языка программирования оказывается, что он не предоставляет изоляции. Всё. Добро пожаловать в ассемблер для x86… Хотя нет, там был стек и поддержка call/ret. Когда она появилась первый раз? Вот -1 от этого момента.

              0
              Да в Ansible один глобальный namespace в рамках одного хоста. Но ничего не мешает вам разделять одно пространство на множетсво — префиксами. Первый префикс — это имя вашего namespace. Удобнее всего, чтобы это было имя роли. Лично я даже создаю два namespace в одой роли: rolename_vars и _rolename_vars по аналогии с приватными полями в питоне. У меня не будет 10 переменных server_ip, потому что сначала идет название namespace. И в любом языке программирования это работает в коде также ( класс.поле = роль_переменная ). За исключением того, что в них есть локальные переменные, а в Ansible их нет. Кроме программирования эта проблема встречается везде, где есть быстрорастущее пространство имен, и везде оно решается префиксом. ( Например адреса домов: улицы везде одинаковые, но префиксы городов решают ).
              В итоге правила просты ( описаны в статье ):
              — Не создавайте переменных «в глобальном» namespace.
              — Используйте переменные только из своего namespace или dependency.

              На самом деле в статье больше акцент на разбор мест где переменные в принципе могут определяться и храниться. И поскольку их много, каждое из них можно превратить в помойку переменных со всего деплоя довольно быстро, даже если сами роли просты.
                +1

                В Ансибле не просто глобальный неймспейс с глобальными переменными. У Ансибла несколько слоёв глобальных переменных, с разными lifetimes, которые перекрывают друг друга в довольно извращённом порядке.


                Вот вы пишите, "не создавайте переменные в глобальном namespace". Чтобы так сделать, нужно:


                1. Всегда использовать loop: index_name (что вызывает wtf в простых случаях, когда, казалось бы, item и вперёд).
                2. Никогда не дёргать setup (потому что setup меняет переменные в глобальном namespace).
                3. Вы себе закрываете любые шансы использовать роли с galaxy, потому что ни вашим принципам не следуют.
                4. Код ролей становится невозможно читать
                  - name: common_monitoring | configure dashboard provisioner
                  become: true
                  copy:
                  content: '{{ common_monitoring_node_exporter_dashboard_provider|to_nice_yaml }}'
                  dest: '{{ common_monitoring_grafana_outside_config_dir }}/provisioning/dashboards/node_exporter.yaml'
                  owner: '{{ common_monitoring_grafana_uid|string }}'
                  group: '{{ common_monitoring_grafana_gid|string }}'
                  vars:
                  common_monitoring_node_exporter_dashboard_provider:
                    apiVersion: 1
                    providers:
                      - name: 'node-exporter dashboard'
                        orgId: 1
                        folder: 'node-exporter'
                        folderUid: '5703111a-97a3-443d-9086-82312a2763a7'
                        type: file
                        disableDeletion: true
                        options:
                          path: '{{ common_monitoring_grafana_inside_data_dir }}/dashboards/node_exporter.json'
                  notify: restart grafana

                Хэндлеры у вас всё равно при этом имеют общий namespace.

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

        Самое читаемое