Сістэмны падыход да зменных у Ansible

ansible devops codestyle

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

Гэты гайд прапануе спосаб арганізацыі зменных у дэплоі. Разлічаны дадзены гайд на тых хто ўжо выкарыстоўвае ролі ў сваіх плэйбуках і чытаў BestPractices, але сутыкаецца з падобнымі праблемамі:

  • Знайшоўшы зменную ў кодзе, немагчыма адразу зразумець завошта яна адказвае;
  • Ёсць некалькі роляў, і зменныя трэба звязаць адным значэннем, але ніяк не атрымоўваецца;
  • Узнікаюць цяжкасці ў тлумачэнні іншым, як уладкованая логіка зменных у вашых плэйбуках

З гэтымі праблемамі мы сутыкнуліся на праектах у нашай кампаніі, у выніку чаго мы прыйшлі да правілаў афармлення зменных у нашых плэйбуках, якія ў нейкай меры вырашылі гэтыя праблемы.

Сістэмны падыход да зменных у Ansible

Пераменныя ў ролях

Роля - гэта асобны Аб'ект сістэмы дэплою. Як і любы аб'ект сістэмы, ён павінен мець інтэрфейс узаемадзеяння з астатняй сістэмай. Такім інтэрфейсам з'яўляюцца зменныя ролі.

Возьмем, для прыкладу, роля api, Якая ўстанаўлівае Java дадатак на сервер. Якія зменныя ў яе могуць быць?

Сістэмны падыход да зменных у Ansible

Пераменныя ролі можна падзяліць на 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 дадаткам, але з рознымі наладамі.

Сістэмны падыход да зменных у 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

Дадаць каментар