Поиск по сайту:

Как управлять многоступенчатыми средами с помощью Ansible


Введение

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

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

Неполные стратегии управления многоступенчатой средой с помощью Ansible

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

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

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

Использование исключительно групповых переменных

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

Однако пересечение групп создает для этой системы серьезные проблемы. Группы часто используются для категоризации более чем одного измерения. Например:

  • среды развертывания (локальные, разработки, этапы, рабочие и т. д.)
  • функции хоста (веб-серверы, серверы баз данных и т. д.)
  • регион центра обработки данных (NYC, SFO и т. д.)

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

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

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

Использование дочерних групп для установления иерархии

Ansible позволяет назначать группы другим группам, используя синтаксис [groupname:children] в инвентаре. Это дает вам возможность называть определенные группы членами других групп. Дочерние группы имеют возможность переопределять переменные, установленные родительскими группами.

Как правило, это используется для естественной классификации. Например, у нас может быть группа под названием environments, в которую входят группы dev, stage, prod. Это означает, что мы можем установить переменные в группе environment и переопределить их в группе dev. Аналогичным образом у вас может быть родительская группа с именем functions, которая содержит группы web, database и loadbalancer.

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

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

  • среда разработки
  • регион
  • функция

Вы можете назначить членство в группе следующим образом:

. . .
[function:children]
web
database
loadbalancer
region

[region:children]
nyc
sfo
environments

[environments:children]
dev
stage
prod

Здесь мы установили иерархию, которая позволяет региональным переменным переопределять функциональные переменные, поскольку группа region является дочерней группой function. Точно так же переменные, установленные в группах environments, могут переопределять любые другие. Это означает, что если мы установим для одной и той же переменной другое значение в группах dev, nyc и web, хост, принадлежащий каждой из они будут использовать переменную из dev.

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

Использование конструкций Ansible, допускающих явный порядок загрузки

В Ansible есть несколько конструкций, допускающих явное упорядочивание загрузки переменных, а именно vars_files и include_vars. Их можно использовать в играх Ansible для явной загрузки дополнительных переменных в порядке, определенном в файле. Директива vars_files действительна в контексте воспроизведения, а модуль include_vars может использоваться в задачах.

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

Например, несколько файлов group_vars могут выглядеть так:

---
env: dev
---
env: stage
---
function: web
---
function: database

Тогда у нас будет отдельный файл vars, определяющий важные переменные для каждой группы. Обычно они хранятся в отдельном каталоге vars для ясности. В отличие от файлов group_vars, при работе с include_vars файлы должны иметь расширение .yml.

Давайте представим, что нам нужно установить переменную server_memory_size в другое значение в каждом файле vars. Ваши серверы разработки, вероятно, будут меньше, чем ваши рабочие серверы. Кроме того, ваши веб-серверы и серверы баз данных могут иметь разные требования к памяти:

---
server_memory_size: 512mb
---
server_memory_size: 4gb
---
server_memory_size: 1gb
---
server_memory_size: 2gb

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

С vars_files пример воспроизведения будет выглядеть так:

---
- name: variable precedence test
  hosts: all
  vars_files:
    - "vars/{{ env }}.yml"
    - "vars/{{ function }}.yml"
  tasks:
    - debug: var=server_memory_size

Поскольку функциональные группы загружаются последними, значение server_memory_size будет взято из файлов var/web.yml и var/database.yml:

  1. ansible-playbook -i inventory example_play.yml
Output
. . . TASK [debug] ******************************************************************* ok: [host1] => { "server_memory_size": "1gb" # value from vars/web.yml } ok: [host2] => { "server_memory_size": "1gb" # value from vars/web.yml } ok: [host3] => { "server_memory_size": "2gb" # value from vars/database.yml } ok: [host4] => { "server_memory_size": "2gb" # value from vars/database.yml } . . .

Если мы изменим порядок загрузки файлов, мы можем сделать переменные среды развертывания более приоритетными:

---
- name: variable precedence test
  hosts: all
  vars_files:
    - "vars/{{ function }}.yml"
    - "vars/{{ env }}.yml"
  tasks:
    - debug: var=server_memory_size

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

  1. ansible-playbook -i inventory example_play.yml
Output
. . . TASK [debug] ******************************************************************* ok: [host1] => { "server_memory_size": "512mb" # value from vars/dev.yml } ok: [host2] => { "server_memory_size": "4gb" # value from vars/prod.yml } ok: [host3] => { "server_memory_size": "512mb" # value from vars/dev.yml } ok: [host4] => { "server_memory_size": "4gb" # value from vars/prod.yml } . . .

Эквивалентный плейбук с использованием include_vars, который работает как задача, будет выглядеть так:

---
- name: variable precedence test
  hosts: localhost
  tasks:
    - include_vars:
        file: "{{ item }}"
      with_items:
        - "vars/{{ function }}.yml"
        - "vars/{{ env }}.yml"
    - debug: var=server_memory_size

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

Прежде всего, использование vars_files и include_vars требует от вас размещения переменных, тесно связанных с группами, в другом месте. Расположение group_vars становится заглушкой для фактических переменных, расположенных в каталоге vars. Это еще раз добавляет сложности и снижает ясность. Пользователь должен сопоставить правильные файлы переменных с хостом, что Ansible делает автоматически при использовании group_vars.

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

Рекомендуемая стратегия Ansible: использование групп и нескольких инвентаризаций

До сих пор мы рассмотрели некоторые стратегии управления многоступенчатыми средами и обсудили причины, по которым они не могут быть полным решением. Тем не менее, проект Ansible предлагает некоторые предложения о том, как лучше всего абстрагировать вашу инфраструктуру от среды.

Рекомендуемый подход заключается в работе с многоступенчатыми средами путем полного разделения каждой операционной среды. Вместо того, чтобы хранить все ваши хосты в одном файле инвентаризации, инвентаризация ведется для каждой из ваших индивидуальных сред. Также поддерживаются отдельные каталоги group_vars.

Базовая структура каталогов будет выглядеть примерно так:

.
├── ansible.cfg
├── environments/         # Parent directory for our environment-specific directories
│   │
│   ├── dev/              # Contains all files specific to the dev environment
│   │   ├── group_vars/   # dev specific group_vars files
│   │   │   ├── all
│   │   │   ├── db
│   │   │   └── web
│   │   └── hosts         # Contains only the hosts in the dev environment
│   │
│   ├── prod/             # Contains all files specific to the prod environment
│   │   ├── group_vars/   # prod specific group_vars files
│   │   │   ├── all
│   │   │   ├── db
│   │   │   └── web
│   │   └── hosts         # Contains only the hosts in the prod environment
│   │
│   └── stage/            # Contains all files specific to the stage environment
│       ├── group_vars/   # stage specific group_vars files
│       │   ├── all
│       │   ├── db
│       │   └── web
│       └── hosts         # Contains only the hosts in the stage environment
│
├── playbook.yml
│
└── . . .

Как видите, каждая среда отличается и разделена на части. Каталоги среды содержат файл инвентаризации (с произвольным названием hosts) и отдельный каталог group_vars.

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

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

Установка переменных кросс-среды

Одна вещь, которая невозможна в рекомендуемой настройке, — это совместное использование переменных в разных средах. Существует несколько способов, которыми мы могли бы реализовать совместное использование переменных среды. Один из самых простых — использовать способность Ansible использовать каталоги вместо файлов. Мы можем заменить файл all в каждом каталоге group_vars на каталог all.

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

Начните с создания файла переменных среды где-то в иерархии. В этом примере мы поместим его в каталог environments. Поместите все переменные кросс-среды в этот файл:

  1. cd environments
  2. touch 000_cross_env_vars

Затем перейдите в один из каталогов group_vars, переименуйте файл all и создайте каталог all. Переместите переименованный файл в новый каталог:

  1. cd dev/group_vars
  2. mv all env_specific
  3. mkdir all
  4. mv env_specific all/

Затем вы можете создать символическую ссылку на файл переменных среды:

  1. cd all/
  2. ln -s ../../../000_cross_env_vars .

Когда вы выполните описанные выше шаги для каждой из ваших сред, ваша структура каталогов будет выглядеть примерно так:

.
├── ansible.cfg
├── environments/
│   │
│   ├── 000_cross_env_vars
│   │
│   ├── dev/
│   │   ├── group_vars/
│   │   │   ├── all/
│       │   │   ├── 000_cross_env_vars -> ../../../000_cross_env_vars
│   │   │   │   └── env_specific
│   │   │   ├── db
│   │   │   └── web
│   │   └── hosts
│   │
│   ├── prod/
│   │   ├── group_vars/
│   │   │   ├── all/
│   │   │   │   ├── 000_cross_env_vars -> ../../../000_cross_env_vars
│   │   │   │   └── env_specific
│   │   │   ├── db
│   │   │   └── web
│   │   └── hosts
│   │
│   └── stage/
│       ├── group_vars/
│       │   ├── all/
│       │   │   ├── 000_cross_env_vars -> ../../../000_cross_env_vars
│       │   │   └── env_specific
│       │   ├── db
│       │   └── web
│       └── hosts
│
├── playbook.yml
│
└── . . .

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

Настройка инвентаризации среды по умолчанию

В файле ansible.cfg можно установить файл инвентаризации по умолчанию. Это хорошая идея по нескольким причинам.

Во-первых, он позволяет не указывать явные флаги инвентаризации для ansible и ansible-playbook. Поэтому вместо ввода:

  1. ansible -i environments/dev -m ping

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

  1. ansible -m ping

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

Чтобы установить инвентарь по умолчанию, откройте файл ansible.cfg. Это может быть в корневом каталоге вашего проекта или в /etc/ansible/ansible.cfg в зависимости от вашей конфигурации.

Примечание. В приведенном ниже примере показано редактирование файла ansible.cfg в каталоге проекта. Если вы используете файл /etc/ansibile/ansible.cfg для своих изменений, измените указанный ниже путь редактирования. При использовании /etc/ansible/ansible.cfg, если ваши запасы ведутся за пределами каталога /etc/ansible, обязательно используйте абсолютный путь вместо относительного. путь при установке значения inventory.

  1. nano ansible.cfg

Как упоминалось выше, рекомендуется установить вашу среду разработки в качестве инвентаря по умолчанию. Обратите внимание, как мы можем выбрать весь каталог среды вместо содержащегося в нем файла hosts:

[defaults]
inventory = ./environments/dev

Теперь вы сможете использовать инвентарь по умолчанию без опции -i. Для инвентаризации не по умолчанию по-прежнему потребуется использовать -i, что помогает защитить их от случайных изменений.

Заключение

В этой статье мы рассмотрели гибкость, которую Ansible обеспечивает для управления хостами в нескольких средах. Это позволяет пользователям применять множество различных стратегий для обработки переменных приоритетов, когда хост является членом нескольких групп, но двусмысленность и отсутствие официальных указаний могут вызывать затруднения. Как и в случае с любой другой технологией, наиболее подходящая для вашей организации будет зависеть от ваших вариантов использования и сложности ваших требований. Лучший способ найти стратегию, которая соответствует вашим потребностям, — это поэкспериментировать. Поделитесь своим вариантом использования и подходом в комментариях ниже.