Системний підхід до змінних у Ansible

ansible devops codestyle

Hey! Мене звати Денис Калюжний я працюю інженером у відділі автоматизації процесів розробки. Щодня нові зборки програм розкочуються на сотнях серверів кампанії. І в цій статті я поділяюсь досвідом використання Ansible для цих цілей.

Цей гайд пропонує спосіб організації змінних у деплої. Розрахований цей гайд на тих, хто вже використовує ролі у своїх плейбуках і читав Кращі практики, але стикається з подібними проблемами:

  • Знайшовши змінну в коді, неможливо відразу зрозуміти за що вона відповідає;
  • Є кілька ролей, і змінні потрібно зв'язати одним значенням, але не виходить;
  • Виникають труднощі у поясненні іншим, як влаштована логіка змінних у ваших плейбуках

З цими проблемами ми зіткнулися на проектах у нашій компанії, внаслідок чого ми дійшли правил оформлення змінних у наших плейбуках, які певною мірою вирішили ці проблеми.

Системний підхід до змінних у Ansible

Змінні у ролях

Роль - це окремий об'єкт системи деплою. Як і будь-який об'єкт системи, він повинен мати інтерфейс взаємодії з рештою системи. Таким інтерфейсом є змінні ролі.

Візьмемо, наприклад, роль apiяка встановлює Java додаток на сервер. Які змінні можуть бути?

Системний підхід до змінних у Ansible

Змінні ролі можна розділити на 2 види на кшталт:

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

Змінні властивості - Це змінні, які визначають поведінку ролі.

Змінні запити — це змінні, значення яких використовується позначення зовнішніх, стосовно ролі, ресурсів.

Змінні слухачі — це змінні, значення яких використовується для формування змінних запитів.

З іншого боку, 1а, 2а, 2б - це змінні, які не залежать від середовища (залізо, зовнішні ресурси і т.д.) і можуть бути заповнені дефолтними значеннями в defaults ролі. Однак змінні типу 1.б і 2.в заповнити окрім як 'example' значеннями неможливо, тому що вони змінюватимуться від стенду до стенду в залежності від оточення.

Стиль коду

  • Назва змінної обов'язково має починатися із назви ролі. Це дозволить надалі легко розібратися, з якої ролі змінна та за що вона відповідає.
  • При використанні змінних у ролях ви повинні обов'язково дотримуватися принципу інкапсуляції і використовувати певні змінні або в самій ролі, або в ролях, від яких поточна залежить.
  • Намагайтеся не використовувати словники для змінних. 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 додатком, але з різними налаштуваннями.

Системний підхід до змінних у Ansible

Уявимо, як виглядатиме плейбук у цьому випадку:

- hosts: myapi
  roles:
    - api

- hosts: bbauth
  roles:
    - auth

- hosts: ghauth
  roles:
    - auth

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

Стиль коду

  • Намагайтеся взагалі не використовувати host_vars змінні, оскільки вони не описують систему, а лише окремий випадок, що в перспективі призведе до питань: "А чому цей хост відрізняється від інших?", відповідь на який не завжди легко знайти.

Змінні зв'язки

Однак це те, що стосується змінних властивостей, але як бути зі змінними зв'язками?
Їхня відмінність у тому, що вони повинні мати однакове значення в різних групах.

Спочатку була ідея використовувати монструозну конструкцію виду:
hostvars[groups['bbauth'][0]]['auth_bind_port'], але від неї одразу відмовилися
оскільки вона має недоліки. По-перше, громіздкість. По-друге, залежність від певного хоста у групі. По-третє, необхідно перед початком деплою зібрати факти з усіх хостів, якщо ми не хочемо отримати помилку невизначеної змінної.

У результаті було вирішено використовувати змінні зв'язку.

Змінні зв'язки — це змінні, що належать до плейбуку, і потрібні для зв'язку об'єктів системи.

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

Таким чином забезпечується однотипність і непересічність імен.

Спробуємо зв'язати змінні з прикладу:

Системний підхід до змінних у Ansible

Уявимо, що у нас є змінні, які залежать один від одного:

# 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 }}"

Тепер, змінюючи значення конектора, ми будемо впевнені, що запит буде звертатися туди, де розташований порт.

Стиль коду

  • Оскільки ролі та групи це різні об'єкти системи, потрібно щоб вони мали різні назви, тоді змінні зв'язки точно показуватимуть, що вони належать конкретній групі серверів, а не ролі в системі.

Середозалежні файли

У ролях можуть використовуватися файли, які відрізняються від середовища до середовища.

Прикладом таких файлів можна назвати сертифікати 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. Документація

Автор

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

Джерело: habr.com

Додати коментар або відгук