Ansible változók és precedenciájuk

Prioritás pecsét kép a pixabay.com-ról

Az előző cikkben kaphattál egy átfogó képet az Ansible 2.8-ról, és a példák segítségével bele is kóstolhattál a használatába. Nagyon fontos viszont tudnod, hogyan használhatsz változókat és egyáltalán milyen változók vannak milyen precedenciával, avagy prioritással, illetve mi közöttük a különbség. Fontos azért, mert a dinamikus beállításokkal rugalmasabbá teszed a konfigurációt, de a helytelen alkalmazásukkal sok bosszúságot okozhatsz magadnak. Továbbra is a 2.8-as Ansible-nél maradva olyan ismeretekhez juthatsz a cikk segítségével, amivel már bátran készíthetsz dinamikusabb Ansible playbookokat is. A cikk-ben bemutatott példákat az itsziget/ansible-examples GitHub projektben is megtalálhatod. Ügyelj arra, hogy a cikkből kattintva jutsz el a projekthez, hogy biztosan a megfelelő verziót találd meg javítás esetén is.

Tartalomjegyzék

Különbség változók és kulcsszavak között

[Tartalom]

Fekete-fehér puzzle kép a pixabay.com-ról

A Playbookokat leíró YAML fájl többnyire kulcs-érték párokból épül fel. A kulcsokat alkotó kulcsszavakról egy teljes lista olvasható a dokumentációban.

A változók megértését az Ansible esetén így megnehezítheti a tény, hogy a kulcsokat az Ansible szempontjából akár tekinthetnénk változóknak. Megadjuk a "hosts" értékeként a célszerverek csoportjainak listáját, ami alapján az Ansible tudja, mely szervereken kell lefuttatni az utasításokat. De ezek inkább olyan paraméterek, amiknek a definicióját később nem írod felül a YAML dokumentumban és nem is hivatkozol azok értékeire többnyire. Ez persze nem jelenti azt, hogy a YAML hierarchiában egy alszekción belül ne lehetne eltérő értéket megadni. Egy nagyon egyszerű példa a parancssorból felülírásra a következő, ami ráadásul változókat is beregisztrál:

- hosts: remotes
  tasks
:
    - name
: Who am I without "become" attribute?
      command
: whoami
      register
: withoutBecome

    - name
: Who am I with "become" set to true
      command
: whoami
      become
: true
      register
: withBecomeTrue

    - name
: Who am I with "become" set to false
      command
: whoami
      become
: false
      register
: withBecomeFalse

    - name
: Debug commands
      debug
:
        msg
:
          - "Without becom - {{ withoutBecome.stdout }}"
          - "With become set to true - {{ withBecomeTrue.stdout }}"
          - "With become set to false - {{ withBecomeFalse.stdout }}"

A három task közül az alábbi utasítások csak annál befolyásolják a felhasználónév értékét, amelyiknél nem definiáltam a "become" paramétert.

echo 'Without --become'
ansible-playbook playbook.yml --inventory inventory.yml

echo 'With --become'
ansible-playbook playbook.yml --inventory inventory.yml --become

Ez azért van, mert a parancssorból átadott paraméterek precedenciája a legalacsonyabb. Kizárólag alapértelmezett értéket jelentenek, de a YAML fájlokban felülírhatók. Az --extra-vars paraméterrel viszont változót lehet átadni. Az extra változóknak pedig a legmagasabb a precedenciájuk, így mindent felülírnak, ami a YAML fájlokban van definiálva.

echo 'With extra variable: ansible_become=true'
ansible-playbook playbook.yml --inventory inventory.yml --extra-vars 'ansible_become=true'

A változóknak igen hosszú, hivatalosan 22 szintű a precedencia listája, ami még növelhető, ha a hasonlóságuk miatt összevont elemeket külön számítjuk. A fenti két példa csak a két véglet, a dokumentáció pedig csak egy listát ad a példák után különösebb magyarázat nélkül. Aggodalomra azonban semmi ok, mindet el fogom magyarázni.

Változók deklarációja a precedenciájuk sorrendjében

[Tartalom]

Sorban volnuló kacsák a pixabay.com-ról

Az előző cikkben már többféleképpen deklaráltam változót, de fent, a paraméterek demonstrálásához is regisztráltam a "register" kulcsszó segítségével. Az alábbiakban bemutatom az összes esetet sorban egymás után, amik közül az elsőt leszámítva mindegyik a 10-es számú példában található a GitHub-on. A fenti éppel ellentétben egyik sem lesz kacsa.

A korábbi gyakorlathoz hasonlóan most is létezik egy "run.sh" a példa mappájának gyökerében az egyszerűbb teszteléshez, de a tartalmára majd később térek ki. A precedencia szinteken visszafelé haladva a változódeklarációkat törölve vagy kikommentelve mindig az eggyel alacsonyabb prioritású deklarációban megadott értéknek kell megjelenni a szkriptet futtatva.

1. Parancssori paraméterek

[Tartalom]

Példa: 09-playbook-override-parameter

Az ansible-playbook parancsnak átadott paraméterek alapértelmezett értékek arra az esetre, ha nincs semmi beállítva konkrétabb helyen. Ilyen paraméterek például a "--user" (playbook kulcsszó: remote_user) és a "--become" is, ahogy utóbbira fentebb már láthattál példakódot is. Használhatod, ha nincs inventory fájlod vagy nincs benne definiálva az adott érték. Hosszú távon viszont érdemes inkább fájlban megadni ezeket az értékeket.

FONTOS: Ismét kihangsúlyoznám, hogy az --extra-vars paraméteren keresztül megadott változókkal ugyanakkor bármi felülírható, ezért is fontos ismerni a különbséget paraméter és változó között.

ansible-playbook playbook.yml --inventory inventory.yml --become

2. Alapértelmezett változók a role-okban

[Tartalom]

Vannak az Ansible által automatikusan felismert útvonalak, mappaszerkezetek. Ha egy általa ismert mappába írsz egy változót, azt automatikusan beolvassa. Ilyen a role-okban a "tasks" mappa melletti "defaults" mappa is. Ebben a "main.yml" nevű fájlban deklarálhatók olyan változók, amik magasabb prioritású változó híján adnak alapértlemezett értéket egy, a role-ban hivatkozott változónak. Már ez is felülírja a parancssori paramétereket.

A defaults/main.yml helyett egyébként a defaults/main/*.yml megoldás is alkalmazható, ezzel több fájlba rendezhetők az alapértelmezések. Egy jól megírt role viszont talán olyan rengeteg változót nem tartalmaz általában. Jogosan merülhet fel a kérdés, hogy miért lett a main.yml-ből main mappa és miért nem lehet egyszerűen további YAML fájlokat létrehozni. A kérdés jó, a válaszra csak egy jó sejtésem van, mi szerint ezt a hierarchiát az Ansible-ben máshol is meg lehet találni (group_vars), és így marad következetes. A group_vars-ra is találsz példát a következőkben.

A GitHub-on a 10-es példában a "roles/includeable/defaults/main.yml" a példa rá, ahol a "location" változót deklaráltam utalva a precedencia szintre. A további esetekben is ezt a változót fogom használni.

location: 2. role defaults

3. Inventory fájlban szervercsoportok változói

[Tartalom]

Az Ansible alapok cimű cikkben egy nagyon egyszerű inventory fájlt mutattam, ahol csak egy "remotes" csoport volt, abban pedig a "hosts" kulcsszóval jelölt szekcióban a beletartozó szerverek adatai. A "hosts" kulcsszóval egy szinten viszont a "vars" is használható pontosan úgy, ahogy azt a taskokban mutattam. Ezek a változók minden, a csoportba tartozó szerveren futó taskban elérhetők lesznek. Talán még emlékszel a mágikus "all" csoportra, ami még az inventory fájl előtt kellett. Az inventory fájlban is definiálható és minden további csoport alapértelmezései definiálhatók itt. Azaz elsőként az "all" alá tartozó változó lesz deklarálva, majd a konkrét csoportnál az felülírható. Így módosulna a fájl:

all:
  vars
:
    location
: 3.1. inventory file group vars all

remotes
:
  vars
:
    location
: 3.2. inventory file group vars
  hosts
:
    127.0.0.1
:
      # ...

Az átláthatóság kedvéért a felesleget töröltem az idézett kódból, viszont az "inventory/default/inventory.yml" fájlban megtalálod a teljes tartalmat. Az új útvonalnak a későbbi példáknál lesz csak jelentősége.

4. Külön mappában definiált inventory "all" csoportjának változói

[Tartalom]

Megeshet, hogy nem elég egy darab konfiguráció, mert lesz egy fejlesztői környezet és lesz egy éles. Esetleg ugyanahhoz a kódbázishoz két különböző éles környezet. Ilyenkor persze lehet több inventory fájlt létrehozni eltérő néven a gyökérkönyvtárban, vagy a projekt gyökerében egy "inventory" mappa is létrehozható, amiben almappák lesznek, amik mind egy-egy környezet fájljait tartalmazzák. Ez a struktrúra hasznos, amikor a különböző környezetekben nagyon eltérő értékeket kell definiálni, de mégis több, kisebb fájlba szeretnéd bontani a konfigurációt. Az előző megoldásban az inventory fájlban volt az "all" csoport alatt definiálva a változó, de az all csoportból az új inventory mappájában egy "group_vars/all.yml" vagy "group_vars/all/main.yml" fájl is létrehozható.

A GitHub-on levő példában az "inventory/default/group_vars/all.yml" fájlban az alábbi változó van definiálva:

location: 4. inventory group_vars/all

MEGJEGYZÉS: Az "all.yml" fájl helyett egy "all" mappát is létrehozhatsz, a benne levő összes fájlt be fogja tölteni az Ansible, akár csak a role defaults esetén.

5. Külön mappában definiált, inventory független "all" csoport változói

[Tartalom]

A playbook fájl szintjén is létrehozhatsz egy "group_vars" nevű mappát, ami az összes környezetre érvényes lesz. Ha itt definiálsz egy változót az "all.yml"-ben, az az inventory group_vars-ban levő all.yml változójánál is erősebb lesz.

location: 5. playbook group_vars/all

6. Külön mappában definiált inventory NEM "all" csoportjának változói

[Tartalom]

Az 4. szint mintájára az "all"-on kívül bármely konkrét csoportnak is létrehozható külön fájl vagy mappa egy-egy inventory-nak. Ez most esetünkben a "remotes.yml" lesz.

location: 6. inventory group_vars

7. Külön mappában definiált, inventory független NEM "all" csoport változói

Az 5. szint mintájára bármely konkrét csoportnak is létrehozható saját fájl vagy mappa. A remotes.yml így a playbook.yml melletti group_vars-on belül az alábbi lesz:

location: 7. playbook group_vars

8. Inventory fájlban definiált szerverek változói.

[Tartalom]

Egy fokkal most izgalmasabb rész jön., ugyanis kiderül, hogy amit az inventory fájlban definiáltunk a "hosts" kulcsszó alatt az SSH kapcsolat miatt a 127.0.0.1 -es IP című szerverhez, azok változók. Igen, annak ellenére, hogy erre semmi nem utal. Ez azt jelenti, hogy bármilyen változót megadhatsz szabadon, ami nem ütközik egy foglalt változónévvel. Mellesleg arról a tényről is lehull a lepel ezzel, hogy az "ansible_user"-t is úgy kell kezelni, mint egy változót, azaz akár egy task-on belül is meg lehet változtatni, amennyiben azt egy speciális felhasználóval kellene SSH-val authentikálni. Azt hiszem, erre ritkán lehet szükség, de előfordulhat. Az inventory fájl viszont ezután így néz ki:

all:
  vars
:
    location
: 3.1. inventory file group vars all

remotes
:
  vars
:
    location
: 3.2. inventory file group vars
  hosts
:
    127.0.0.1
:
      location
: 8. inventory file host_vars
      ansible_user
: vagrant
      ansible_port
: 2222
      ansible_ssh_private_key_file
: # SSH kEY PATH

Ez a változó az adott szerveren felülírja a korábbi szinteken megadott változók értékét, de csak az adott szerveren.

9. Külön mappában definiált inventory szervereinek változói

[Tartalom]

Létezik a group_vars mappához hasonló host_vars mappa, ahol a benne levő fájlok vagy mappanevek a szerverek címei lesznek. Az inventory szintű host_vars a 9. a preferencia listában, és még az inventory fájlban levő értéket is felülírja.

inventory/default/host_vars/127.0.0.1.yml:

location: 9. inventory host_vars

10. Külön mappában definiált inventory független, szerverszintű változók

[Tartalom]

Most már nyilván sejted, mi következik. Igen, jól gondolod! A playbook.yml melletti host_vars/127.0.0.1.yml változója felülírja az inventory host_vars deklarációit.

location: 10. play host_vars

11. A célszerverekről begyűjtött adatok változói

[Tartalom]

Szerverekről kép a pixabay.com-ról

Az Ansible automatikusan begyűjt információkat a célszerverekről, mielőtt bármilyen taskot lefuttatna a playbookban. Ezek az információk a "fact"-ek, amik egy része változóként is elérhető "ansible_" prefixszel. Célszerű ilyen prefixszel nem definiálni saját változót, de ha nincs szükséged ezekre a fact-ekre, letilthatod a begyűjtésüket a playbookban.

- hosts: remotes
  gather_facts
: false

Vagy letilthatod, hogy közvetlenül elérhetők legyenek, miközben továbbra is hivatkozhatsz rájuk az "ansible_facts" változón keresztül, ami egy asszociatív tömb vagy hashmap, kinek mi ismerős az általa ismert programnyelvekből.

Nincs "location" nevű fact, amit a teszt projekt megkívánna, viszont létre lehet hozni dinamikusan is fact-et egy task-ban

- name: Set fact
  set_fact
:
    custom_fact
: Custom fact

Az így deklarált fact viszont erősebb precedenciájú, kivéve, ha cache-ből töltődik be. A fact-ekkel ugyanis megtehető, ami hagyományos változókkal nem. cache-selni lehet őket, így nem kell minden egyes alkalommal lekérdezni a szerverekről, ezzel gyorsítva a futtatást, mégis elkerülve az információk manuális kinyerését. Többféle cache plugin van, de a legegyszerűbb talán a json. A saját gépeden, egy meghatározott útvonalon fog létrejönni minden szervernek egy json fájl, amiben a lementett fact-ek vannak. A következőképpen lehet környezeti változókkal bash-ben engedélyezni a playbook futtatása előtt:

export ANSIBLE_CACHE_PLUGIN=jsonfile
export ANSIBLE_CACHE_PLUGIN_CONNECTION="/tmp/ansible-cache"

MEGJEGYZÉS: Ha virtuális gépekkel dolgozol vagy VPN kapcsolattal, akkor a mentett szervercímek között lehet ütközés, ezért érdemes a cache mappa útvonalát minden projektnél egyedire állítani. Erre láthatsz példát a projektben mellékelt run.sh-ban.

Egy külön playbookot hoztam létre a teszt kedvéért playbook-cache-fact.yml néven, amit a többi feladatot tartalmazó playbook.yml előtt lehet lefuttatni, hogy már létezzen a cache-ben a változó. A task-ban pedig most már a "cacheable" logikai paramétert is használni kell:

- hosts: remotes
  tasks
:
    - name
: Cache location fact
      set_fact
:
        location
: 11. cached_facts
        cacheable
: true

Bánj óvatosan ezzel a lehetőséggel, mivel a cache-ből betöltött fact is felülírhat korábbi szinteken deklarált változókat, de közvetlenül a fact beállítása után a változónak magasabb prioritása van, amiről később lesz szó!

12. Play szinten definiált változó a playbookban

[Tartalom]

Most már profi vagy, úgyhogy kár is sok szót fecsérelni erre. Ismét a "vars" kulcsszó a barátunk, de most már a playbookban. Írjuk is felül az összes eddigi deklarációt minden task-ban, ami az adott playbookból indul el:

- hosts: remotes
  vars
:
    location
: 12. play vars
 # ...

13. Play szintű változók a felhasználótól bekérve

[Tartalom]

Ansible-ben több módon is be lehet kérni adatokat a felhasználótól, de a legegyszerűbb módja a "vars_prompt" kulcsszó használata a playbookban, ami erősebb lesz, mint az előző, egyszerű változólistánk.

- hosts: remotes
  vars
:
    location
: 12. play vars
  vars_prompt
:
    - name
: location
      prompt
: "Set the location information"
      private
: false
      default
: 13. play vars_prompt

A "private" paraméternek logikai értéket kell megadni, de a false vagy true helyett nyugodtan lehet a yes vagy no angol szavakat is írni. Bár normál esetben a playbook futtatásakor parancssorban megjelenne a prompt az adatok bekérésére, a mellékelt run.sh-ban ezt kényelmi szempontból letiltottam, mivel úgyis felül lesz írva a többi példa által.

14. Play szintű változók a "vars_files" kulcsszó segítségével fájlból betöltve

[Tartalom]

Ha nem akarsz felhasználói interakcióval bekérni adatot, de például lehetőséget akarsz adni a futtató felhasználónak, hogy fájlban mellékelje azokat, akkor a playbook.yml mellé helyezett vars_file.yml (a neve természetesen bármi lehet) betölthető az alábbi módon:

- hosts: remotes
  vars
:
    location
: 12. play vars
  vars_prompt
:
    - name
: location
      prompt
: "Set the location information"
      private
: false
      default
: 13. play vars_prompt
  vars_files
:
    - vars_file.yml

vars_files.yml:

location: 14. play vars_files

Akkor is jó ez, ha meg akarod viccelni a rendszergazdát, aki futtatja, és bármilyen értéket adott meg a promptban, azt szépen felülírod ezzel...

15. Role változók a "vars" mappában

[Tartalom]

Volt már role defaults, group_vars, host_vars, és most itt a role "vars" mappa. A role-on belül a "vars/main.yml" változója a role számára felülírja a korábbi deklarációkat. Így míg a defaults mappában olyan változókat érdemes megadni, amik gyakorlatilag felülírásra termettek, a vars-ban deklaráltakat a futtató már csak a parancssori paraméterrel fogja tudni megváltoztatni, no persze sajnos ahhoz sem kell sok erőfeszítés, de ez majd egy későbbi téma lesz. A main.yml viszont így néz ki:

location: 15.2. role vars in main.yml

Várjunk csak... miért az a 2-es a 15 után? A dokumentáció ebben nem bőbeszédű, de van még egy hely, a role-ok listája a playbookban, ahol lehet változót deklarálni. Arra még nem jöttem rá, mikor lehet hasznos, ugyanis úgy tűnik, nem csak a role számára deklarálja a változót, hanem azon kívül is elérhető lesz, a main.yml-ben megadott pedig felülírja a role-ban:

- hosts: remotes
  # ...
  roles
:
    - role
: precedence-test
      vars
:
        location
: 15.1. role vars in playbook

16. Blokk változók

[Tartalom]

Az előző cikkben szó volt a taskok blokkokba rendezéséről, ráadásul már változót is használtam blokk szinten, aminek az értéke csak a blokkon belül elérhető. Ebbe beleértve természetesen a blokkban betöltött további task fájlokat és template-eket. A "vars" kulcsszó sem újdonság, így ugorjunk is a példára, ami az includeable role-ban a variable-echo.yml task fájlban található:

- name: Variable echo block
  vars
:
    location
: 16. block vars
  block
:
    # ... list of tasks ...

17. Task változók

[Tartalom]

Ismét az ismerős "vars" kulcsszóval deklarálunk, de már a task-ban, ami nem írja felül a blokkban levő többi task változóját, csak az aktuális task változója kap új értéket.

- name: Variable echo block
  vars
:
    location
: 16. block vars
  block
:

    - name
: Echo variable
      vars
:
        location
: 17. task vars
      command
: echo {{ location }}

18. Task-ban, külső fájlból betöltött változók

[Tartalom]

Volt már szó a playbookban a vars_files -ról. Az viszont nem írható a taskokban. Légy óvatos ezzel az eszközzzel is, mert bár vonzó lehet a bevetése, az így betöltött változók célja éppen a taskon kívüli elérhetőség. Ez pedig nem korlátozódik sem blokkra, sem fájlra. Mivel egy igen magas prioritású változóról van szó, ami még a taskokban közvetlenül, a "vars" kulcsszóval deklarált változókat is felülírja, nagy az esély rá, hogy nem kívánt helyen is elrontod egy változód értékét.

- name: Variable echo block
  vars
:
    location
: 16. block vars
  block
:
  # ...
    - name
: Include vars
      include_vars
: vars.yml
   # ...

A vars.yml tartalma pedig az includeable role mappájában:

location: 18. include_vars

19. Fact beállítása taskban és task output regisztrálása változóba

[Tartalom]

Register gomb billentyűzeten a pixabay.com-ról

A set_fact kulcsszó már volt a 11. szinten, ott viszont egy teljesen új playbookban volt kizárólag a cache tesztelésére. A rögtön a deklaráció után levő taskok számára viszont nem a cache-ből jön az értéke. Ekkor pedig a prioritása is nagyobb. Az include_vars-szal betöltött változókhoz hasonlóan mindenhol elérhető lesz az értéke a teljes futtatás alatt, de erősebb annál. A másik hasonló kulcsszó a "register", amit bármely tasknál lehet használni. Ez az, amit a kimenet megjelenítésére használtam az előző cikkben és ami ebben a példában is szerepel a teljesség kedvéért, habár egyáltalán nem lett volna szükséges. Viszont amíg a set_fact -tel tetszőleges érték beállítható tetszőleges számú változónak, a register csak az aktuális taskban futtatott parancs visszatérési értékétnek regisztrál egy változónevet. A shell modul esetén ez például a standard outputra írt tartalom objektuma, tehát nem skalár típus.

Mindezekkel együtt a következőképpen néz ki a teljes variables-echo.yml:

- name: Variable echo block
  vars
:
    location
: 16. block vars
  block
:

    - name
: Set fact
      set_fact
:
        location
: 19. set_facts

    - name
: Include vars
      include_vars
: vars.yml

    - name
: Echo variable
      vars
:
        location
: 17. task vars
      command
: echo {{ location }}
      register
: location_output

    - name
: Debug output
      debug
:
        msg
: "{{ location_output.stdout_lines }}"

20. Role paraméter

[Tartalom]

Az include_role modullal betölthető taskban egy teljes role. Ilyenkor ennek a role-nak lehetnek saját változói, amiket viszont felül lehet bírálni a role-t betoltő taskban. Ez teljesen egyszerűen a már megszokott "vars" kulcsszóval működik. Lássuk hát a precedence-test role egyetlen taskját a main.yml-ben, ami betölti az "includeable" nevű role-t:

- name: Include includeable role
  vars
:
    location
: 20.1. include_role params
  include_role
:
    name
: includeable

A másik lehetőség viszont, ami azt gondolom, a gyakoribb eset, a playbookban a role-ok felsorolásánál átadott paraméter. A trükk az, hogy most viszont nem a "vars" kulcsszó kell, hiszen az már a 15. szinten ki lett tárgyalva. Sőt, semmilyen kulcsszó nem kell. A role paraméter valójában egy olyan változó, ami egy szinten van deklarálva a "role" és a vars kulcsszavakkal. A 15. szintnél megismert role lista tehát az alábbira változik:

 roles:
    - role
: precedence-test
      vars
:
        location
: 15.1. role vars in playbook
      location
: 20.2. role params

Mivel a paraméterek felülírják a betöltött role-ban deklarált alapértékeket, ez a megoldás használható olyan role-ok készítésére, amik akár feltétel alapján másképpen működnek. Például többféle előre létrehozott konfigurációból lehet választani a felhasználó igényétől függően.

21. Include paraméter

[Tartalom]

Nem csak role-okat lehet betölteni taskból, de másik task lista fájlt is. Ezért is nevezzük a fő task lista fájlt main.yml-nek, majd a többi betölthető szükség esetén. Az ilyen paraméter felülír minden azonos nevű változót a betöltött task lista fájlban. Az incldueable role egyetlen taskja pedig az alábbi:

- name: Include variable-echo.yml
  vars
:
    location
: 21. include params
  include
: variable-echo.yml

22. Parancssorból deklarált változó

[Tartalom]

Ezzel elértünk a precedencia lista végére. Az ansible-playbook programnak átadott --extra-vars paraméter egy stringet vár értékként, amiben több változó is deklarálható szóközzel elválasztva. Fontos ezért, hogy ne írj ilyen extra deklarációkat:

# A változó értéke "22." lesz. A többi új paramétert jelent bash-ben
ansible-playbook --extra-vars location=22. extra vars # ...

# Az idézőjeleket a bash értelmezi. A változó értéke marad "22."
ansible-playbook --extra-vars location="22. extra vars" # ...

# location, extra és vars változók deklarálása. A location értéke marad 22.
ansible-playbook --extra-vars "location=22. extra vars" # ...

A helyes megoldás:

ansible-playbook --extra-vars 'location="22. extra vars"' # ...

Ezzel garantáltan minden location változót mindenhol felülírsz, ami ritkán lehet cél. Maximum akkor, ha pontosan tudod, hogy az a változó ténylegesen mindenhol felülírandó. Például, az "ansible_user" nevű változó nagy valószínűséggel nem lesz taskonként felülírva annak ellenére, hogy van rá lehetőség. Ha hirtelen, új konfiguráció nélkül másik felhasználóval kellene dolgozni, akkor jól jöhet. Saját változók esetén érdemes egy szabályt alkotni magadnak, hogy például soha nem adsz át parancssori paraméterben kisbetűvel írt változót, és minden globálisan felülírandó paramétert nagybetűs névvel deklarálsz. Vagy egy prefixet használsz névtér gyanánt. Következetesen mindig ugyanazt. Így ha szükséges, könnyebben megtalálod mind, ha mégis módosítani kellene a prefixen.

Taskok környezeti változói

[Tartalom]

Bash shell kép a pixabay.com-ról

Oké, ez egy kicsit kakukktojás, ugyanis a parancssori változókról van szó és szinte semmi köze a fentebb tárgyalt változókhoz. A környezeti változókat az "environment" kulcsszóval lehet egy task-ban vagy akár playbookban deklarálni. Amikor egy Ansible változót deklarálsz, arra egy playbookban van template-ben hivatkozhatsz, majd az Ansible ez alapján legenerálja a végleges utasítást, amit a célszervereken a taskok segítségével lefuttatsz. A célszerverek így már nem változókat fognak látni. A környezeti változók viszont pont a célszerverek számára lesznek deklarálva és befolyásolhatják azok viselkedését. Ilyen lehet például a http_proxy változóval proxy szerver használata, vagy a PATH környezeti változó módosításával tetszőleges mappában található szkriptek teljes útvonal nélküli futtatása. Az is előfordulhat, hogy a futtatandó szkript veszi figyelembe a változót, ahogy például az ansible-playbook program konfigurációja is lehetséges környezeti változókkal. Ez azonban nem összetévesztendő a taskok környezeti változóival.

Tegyük fel, hogy a szkriptedben ellenőrzöd a WORKING_DIR változó értékét, és amennyiben van értéke, a szkripten belül abba a mapppába navigálsz. Írhatnád így is:

- name: Custom script and variable
  shell
: WORKING_DIR=/opt/custom/dir custom-script.sh

De különösen több változó esetén sokkal szebb az alábbi megoldás:

- name: Custom script and variable
  shell
: custom-script.sh
  environment
:
    WORKING_DIR
: /opt/custom/dir

Valójában ez is az előző megoldásra lesz átfordítva az Ansible által, de a playbook átláthatóbb lesz. Van egy, a dokumentációban nem nagyon említett jellemzője az environment kulcsszónak. Nem csak a fenti módon lehet változót deklarálni, hanem kulcs-érték párok helyett listák is átadhatók, amik kulcs-érték párokat tartalmaznak. A task szempontjából ennek nem nagyon van közvetlenül jelentősége, amíg nem próbálsz hivatkozni valami speciális okból kifolyólag a deklarált környeztei változókra Ansible template-ben.

   - name: Test environment
      shell
: |
       echo {{ environment[0].test1 }}
        echo $test2
        echo $test1

      environment
:
        - test1
: test1
          test2
: test2
        - test1
: test1 overridden
      register
: environment_test

    - name
: Debug environment
      debug
:
        msg
: "{{ environment_test.stdout_lines }}"

Látható, hogy az environment egy változó is, ami minden esetben egy kulcs-érték párokból álló listát tartalmaz, akkor is, ha te magad nem listákkal deklaráltad a változókat. Bár a később deklarált listában szereplő változó felülírja az előzőekben deklaráltakat, template-ból így továbbbra is elérheted bármelyik verzióját. A fenti kódrészletet megtalálod a GitHib-on is a 11-es példában.

Őszintén kíváncsi volnék a véleményedre, hogy a gyakorlatban mennyi esélyt látsz ennek szükségességére. Kommentben várom az ötleteket.

Összefoglalás

[Tartalom]

Nos, bármennyire is egyszerű témának tűnik a változókról beszélni általában, a valóság ezzel szemben sokkal komplexebb lehet. Azt hiszem az Anisble remek példa erre. Rengeteg heyen deklarálhatsz változót és mindnek más lehet a precedenciája és a hatóköre. Fel kell tenned magadnak az alábbi kérdéseket:

  • Szükségem van egyáltalán a változóra vagy csak egyetlen helyen hivatkozom az értékére és nem akarom, hogy felülírják?

  • Hány helyen és hol szeretnék hivatkozni az értékére a változónak?

  • Mennyire fontos, hogy ne írhassák felül az értékét a felhasználók?

  • Alapértéket szeretnék definiálni egy későbbi task számára, vagy éppen az ott deklarált alapértéket szeretném felülírni?

A változók megfelelő használata nagyban befolyásolja a kódod komplexitását és rugalmasságát. Azt gondolom, érdemesebb a kód egyszerűségére törekedni, és csak ott bonyolítani, ahol más szempontok ezt igénylik. Hasznos lehet mellékelni egy leírást a projektben használt változókról, illetve a deklarációnál megjegyzésben leírni, miért éppen ott lett deklarálva, ahol, amennyiben ez nem triviális. Ha már jól használod a változókat, a template-ek segítségével sokkal komolyabb playbookokat tudsz írni. A template-ekről is várható cikk, hiszen eddig épp hogy csak érintettük a témát, amennyire feltétlenül szükséges volt.

Ha téged is sokkolt elsőre a változók működésének és egymásra hatásának összetettsége, tudasd velem és mindenkivel kommentben. Ha sokkolt, hogy engem sokkolt, azt is. Észrevételeidet narya értékelem, különösen, ha hibát vélsz felfedezni. Ha tetszett a cikk, kattints a tetszik gombra ezzel is ingyen támogatva az oldalt és a cikkírói kedvemet :)

Linkek

[Tartalom]

Kategóriák: 
Megosztás/Mentés