Ansible základy, bez ktorých budú vaše zošity len hrudkou lepkavých cestovín

Robím veľa recenzií kódu Ansible iných ľudí a sám veľa píšem. V rámci rozoberania chýb (cudzích aj mojich), ako aj množstva rozhovorov som si uvedomil hlavnú chybu, ktorej sa užívatelia Ansible dopúšťajú – púšťajú sa do zložitých vecí bez toho, aby ovládali tie základné.

Aby som napravil túto univerzálnu nespravodlivosť, rozhodol som sa napísať úvod k Ansible pre tých, ktorí to už poznajú. Varujem vás, toto nie je prerozprávanie mužov, toto je dlhé čítanie s množstvom písmen a bez obrázkov.

Očakávaná úroveň čitateľa je taká, že už bolo napísaných niekoľko tisíc riadkov yamly, niečo sa už vyrába, ale „všetko je akosi pokrivené“.

mená

Hlavnou chybou používateľa Ansible je, že nevie, ako sa niečo volá. Ak nepoznáte mená, nemôžete pochopiť, čo hovorí dokumentácia. Živý príklad: počas rozhovoru osoba, ktorá akoby povedala, že veľa písal v Ansible, nedokázala odpovedať na otázku „z akých prvkov pozostáva zošit? A keď som navrhol, že „odpoveď sa očakávala, že učebnica pozostáva z hry“, nasledoval prekliaty komentár „to nepoužívame“. Ľudia píšu Ansible pre peniaze a nepoužívajú hru. V skutočnosti to používajú, ale nevedia, čo to je.

Začnime teda niečím jednoduchým: ako sa to volá. Možno to viete, možno nie, pretože ste tomu pri čítaní dokumentácie nevenovali pozornosť.

ansible-playbook spustí playbook. Playbook je súbor s príponou yml/yaml, vo vnútri ktorého je niečo takéto:

---
- hosts: group1
  roles:
    - role1

- hosts: group2,group3
  tasks:
    - debug:

Už sme si uvedomili, že celý tento súbor je playbook. Môžeme ukázať, kde sú role a kde sú úlohy. Ale kde je hra? A aký je rozdiel medzi hrou a rolou alebo playbookom?

Všetko je v dokumentácii. A chýba im to. Začiatočníci - pretože je toho príliš veľa a nebudete si pamätať všetko naraz. Skúsený - pretože „triviálne veci“. Ak máte skúsenosti, prečítajte si tieto stránky znova aspoň raz za šesť mesiacov a váš kód sa stane prvotriednym.

Takže, pamätajte: Playbook je zoznam pozostávajúci z play a import_playbook.
Toto je jedna hra:

- hosts: group1
  roles:
    - role1

a toto je tiež ďalšia hra:

- hosts: group2,group3
  tasks:
    - debug:

čo je hra? Prečo je?

Hra je kľúčovým prvkom knihy, pretože hra a iba hra spája zoznam rolí a/alebo úloh so zoznamom hostiteľov, na ktorých musia byť spustené. V hlbokých hĺbkach dokumentácie môžete nájsť zmienku o delegate_to, lokálne vyhľadávacie doplnky, nastavenia špecifické pre sieťové rozhranie, skokoví hostitelia atď. Umožňujú mierne zmeniť miesto, kde sa vykonávajú úlohy. Ale zabudni na to. Každá z týchto šikovných možností má veľmi špecifické využitie a rozhodne nie je univerzálna. A to hovoríme o základných veciach, ktoré by mal poznať a používať každý.

Ak chcete „niečo“ „niekde“ predviesť, napíšte play. Nie rola. Nie rola s modulmi a delegátmi. Vezmi to a napíš hru. V ktorej v poli hostitelia uvádzate, kde sa má vykonať, av rolách/úlohách - čo vykonať.

Jednoduché, však? Ako by to mohlo byť inak?

Jedným z charakteristických momentov, keď ľudia túžia robiť to nie prostredníctvom hry, je „úloha, ktorá všetko nastavuje“. Chcel by som mať rolu, ktorá konfiguruje servery prvého typu aj servery druhého typu.

Typickým príkladom je monitorovanie. Chcel by som mať rolu monitorovania, ktorá bude konfigurovať monitorovanie. Monitorovacia rola je priradená monitorovacím hostiteľom (podľa hry). Ukazuje sa však, že na monitorovanie musíme doručovať balíky hostiteľom, ktorých monitorujeme. Prečo nepoužiť delegáta? Musíte tiež nakonfigurovať iptables. delegovať? Ak chcete povoliť monitorovanie, musíte tiež napísať/opraviť konfiguráciu pre DBMS. delegát! A ak kreativita chýba, potom môžete urobiť delegáciu include_role vo vnorenej slučke pomocou zložitého filtra na zozname skupín a vo vnútri include_role môžete urobiť viac delegate_to znova. A ideme preč...

Dobré želanie – mať jednu jedinú monitorovaciu rolu, ktorá „robí všetko“ – nás vedie do úplného pekla, z ktorého je najčastejšie jediné východisko: prepísať všetko od nuly.

Kde sa tu stala chyba? Vo chvíli, keď ste zistili, že na vykonanie úlohy „x“ na hostiteľovi X musíte ísť na hostiteľa Y a urobiť tam „y“, museli ste urobiť jednoduché cvičenie: ísť a napísať hru, čo na hostiteľovi Y robí y. Nepridávajte niečo do "x", ale píšte to od začiatku. Dokonca aj s pevne zakódovanými premennými.

Zdá sa, že všetko v odsekoch vyššie je povedané správne. Ale to nie je váš prípad! Pretože chcete napísať opätovne použiteľný kód, ktorý je SUCHÝ a podobný knižniciam, a musíte hľadať spôsob, ako to urobiť.

Tu sa skrýva ďalšia vážna chyba. Chyba, ktorá premenila mnohé projekty zo znesiteľne napísaných (mohlo by to byť lepšie, ale všetko funguje a je ľahké dokončiť) na hotový horor, na ktorý nevie prísť ani autor. Funguje to, ale nedajbože, aby ste niečo zmenili.

Chyba je: rola je funkcia knižnice. Toto prirovnanie zničilo toľko dobrých začiatkov, že je jednoducho smutné to sledovať. Úloha nie je funkciou knižnice. Nevie počítať a robiť rozhodnutia na úrovni hry. Pripomeň mi, aké rozhodnutia robí hra?

Ďakujem, máš pravdu. Play rozhoduje (presnejšie obsahuje informácie) o tom, ktoré úlohy a roly bude vykonávať na ktorých hostiteľoch.

Ak toto rozhodnutie delegujete na rolu, a dokonca aj s výpočtami, odsúdite seba (a toho, kto sa pokúsi analyzovať váš kód) do mizernej existencie. Úloha nerozhoduje o tom, kde sa vykonáva. Toto rozhodnutie sa robí hrou. Rola robí to, čo sa jej povie, tam, kde sa jej povie.

Prečo je nebezpečné programovať v Ansible a prečo je COBOL lepší ako Ansible si povieme v kapitole o premenných a jinja. Zatiaľ si povedzme jednu vec – každý váš výpočet zanecháva za sebou nezmazateľnú stopu zmien globálnych premenných a vy s tým nemôžete nič robiť. Len čo sa tieto dve „stopy“ pretli, všetko bolo preč.

Poznámka pre maškrtníkov: rola môže určite ovplyvniť tok kontroly. Jedzte delegate_to a má rozumné využitie. Jedzte meta: end host/play. Ale! Pamätáte si, že učíme základy? Zabudli ste delegate_to. Hovoríme o najjednoduchšom a najkrajšom Ansible kóde. Ktorý sa ľahko číta, ľahko sa píše, ľahko sa ladí, testuje a ľahko sa dopĺňa. Takže ešte raz:

hrať a iba hra rozhoduje o tom, ktorí hostitelia, čo sa vykoná.

V tejto časti sme sa zaoberali protikladom medzi hrou a rolou. Teraz si povedzme o vzťahu úlohy a roly.

Úlohy a roly

Zvážte hru:

- hosts: somegroup
  pre_tasks:
    - some_tasks1:
  roles:
     - role1
     - role2
  post_tasks:
     - some_task2:
     - some_task3:

Povedzme, že musíte urobiť foo. A vyzerá to tak foo: name=foobar state=present. Kde to mám napísať? v pred? príspevok? Vytvoriť rolu?

...A kam zmizli úlohy?

Opäť začíname so základom – s hracím zariadením. Ak sa v tejto otázke vznášate, nemôžete hru použiť ako základ pre všetko ostatné a váš výsledok bude „roztrasený“.

Play device: direktíva hosts, nastavenia pre samotné hranie a pre_tasks, tasky, role, post_tasks sekcie. Zostávajúce parametre pre hru nie sú pre nás teraz dôležité.

Poradie ich sekcií s úlohami a rolami: pre_tasks, roles, tasks, post_tasks. Keďže sémanticky je poradie vykonávania medzi tasks и roles nie je jasné, potom osvedčené postupy hovoria, že pridávame sekciu tasks, iba ak nie roles... Ak existuje roles, potom sa všetky pripojené úlohy umiestnia do sekcií pre_tasks/post_tasks.

Zostáva len to, aby bolo všetko sémanticky jasné: po prvé pre_tasks, potom roles, potom post_tasks.

Stále sme však neodpovedali na otázku: kde je volanie modulu? foo písať? Musíme pre každý modul napísať celú rolu? Alebo je lepšie mať na všetko hustú rolu? A ak nie rolu, tak kde mám napísať - pred alebo po?

Ak na tieto otázky neexistuje žiadna odôvodnená odpoveď, potom je to znak nedostatku intuície, teda tých istých „chúlostivých základov“. Poďme na to. Najprv bezpečnostná otázka: Ak má hra pre_tasks и post_tasks (a nie sú tam žiadne úlohy ani roly), potom sa môže niečo zlomiť, ak vykonám prvú úlohu z post_tasks Presuniem to na koniec pre_tasks?

Znenie otázky samozrejme naznačuje, že sa to zlomí. Ale čo presne?

... Psovodi. Prečítanie základov odhalí dôležitý fakt: všetci psovodi sa po každej sekcii automaticky vypláchnu. Tie. všetky úlohy od pre_tasks, potom všetci správcovia, ktorí boli upozornení. Potom sa vykonajú všetky roly a všetky handlery, ktoré boli upozornené v rolách. Po post_tasks a ich manipulantov.

Ak teda pretiahnete úlohu z post_tasks в pre_tasks, potom ho potenciálne spustíte pred spustením obsluhy. napríklad ak v pre_tasks je nainštalovaný a nakonfigurovaný webový server a post_tasks niečo sa mu odošle, potom túto úlohu preneste do sekcie pre_tasks povedie k tomu, že v čase „odoslania“ server ešte nebude spustený a všetko sa pokazí.

Teraz sa znova zamyslime, prečo to potrebujeme pre_tasks и post_tasks? Napríklad, aby sa všetko potrebné (vrátane psovodov) skompletizovalo pred plnením úlohy. A post_tasks nám umožní pracovať s výsledkami vykonávania rolí (vrátane handlerov).

O čo ide, nám prezradí bystrý odborník z Ansible. meta: flush_handlers, ale prečo potrebujeme flush_handlers, ak sa môžeme spoľahnúť na poradie vykonávania sekcií v hre? Navyše, použitie meta: flush_handlers nám môže poskytnúť neočakávané veci s duplicitnými obslužnými nástrojmi, čo nám pri použití poskytne zvláštne varovania when у block atď. Čím lepšie poznáte ansible, tým viac nuancií môžete pomenovať pre „zložité“ riešenie. A jednoduché riešenie – použitie prirodzeného rozdelenia medzi pre/role/post – nespôsobuje nuansy.

A späť k nášmu 'foo'. Kam to mám dať? V pre, post alebo role? To samozrejme závisí od toho, či potrebujeme výsledky psovoda pre foo. Ak tam nie sú, potom foo nemusí byť umiestnené ani pred alebo po - tieto sekcie majú špeciálny význam - vykonávanie úloh pred a po hlavnej časti kódu.

Teraz odpoveď na otázku „rola alebo úloha“ prichádza na to, čo je už v hre - ak tam sú úlohy, musíte ich pridať do úloh. Ak existujú roly, musíte vytvoriť rolu (aj z jednej úlohy). Pripomínam, že úlohy a roly sa nepoužívajú súčasne.

Pochopenie základov Ansible poskytuje rozumné odpovede na zdanlivo otázky vkusu.

Úlohy a roly (druhá časť)

Teraz poďme diskutovať o situácii, keď práve začínate písať učebnicu. Potrebujete urobiť foo, bar a baz. Sú to tri úlohy, jedna rola alebo tri roly? Aby som zhrnul otázku: v akom bode by ste mali začať písať roly? Aký zmysel má písanie rolí, keď môžete písať úlohy?... Čo je to rola?

Jednou z najväčších chýb (už som o tom hovoril) je myslieť si, že rola je ako funkcia v knižnici programu. Ako vyzerá všeobecný popis funkcie? Prijíma vstupné argumenty, interaguje s vedľajšími príčinami, robí vedľajšie účinky a vracia hodnotu.

Teraz pozor. Čo sa z toho dá v úlohe urobiť? Vedľajšie účinky ste vždy vítaní, to je podstata celého Ansible – vytvárať vedľajšie účinky. Máte vedľajšie príčiny? Základné. Ale s "odovzdať hodnotu a vrátiť ju" - to je miesto, kde to nefunguje. Po prvé, role nemôžete priradiť hodnotu. V sekcii vars pre rolu môžete nastaviť globálnu premennú s celoživotnou veľkosťou hry. V rámci roly môžete nastaviť globálnu premennú s dobou trvania hry. Alebo dokonca s životnosťou príručiek (set_fact/register). Ale nemôžete mať „miestne premenné“. Nemôžete „vziať hodnotu“ a „vrátiť ju“.

Z toho vyplýva hlavná vec: v Ansible nemôžete napísať niečo bez vedľajších účinkov. Zmena globálnych premenných je vždy vedľajším efektom funkcie. V Ruste je napríklad zmena globálnej premennej unsafe. A v Ansible je to jediný spôsob, ako ovplyvniť hodnoty pre rolu. Všimnite si použité slová: nie „odovzdať hodnotu role“, ale „zmeniť hodnoty, ktoré rola používa“. Medzi rolami neexistuje žiadna izolácia. Neexistuje žiadna izolácia medzi úlohami a rolami.

Celkom: rola nie je funkcia.

Čo je na úlohe dobré? Po prvé, rola má predvolené hodnoty (/default/main.yaml), po druhé, rola má ďalšie adresáre na ukladanie súborov.

Aké sú výhody predvolených hodnôt? Pretože v Maslowovej pyramíde, pomerne skreslenej tabuľke premenných priorít Ansible, majú predvolené roly najnižšiu prioritu (mínus parametre príkazového riadku Ansible). To znamená, že ak potrebujete poskytnúť predvolené hodnoty a nemusíte sa obávať, že prepíšu hodnoty z inventára alebo skupinových premenných, potom sú predvolené hodnoty rolí tým jediným správnym miestom pre vás. (Trochu klamem - je ich viac |d(your_default_here), ale ak hovoríme o stacionárnych miestach, potom iba predvolené roly).

Čo je ešte skvelé na rolách? Pretože majú vlastné katalógy. Sú to adresáre pre premenné, konštantné (t. j. vypočítané pre rolu) a dynamické (existuje buď vzor alebo anti-vzor - include_vars s {{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml.). Toto sú adresáre pre files/, templates/. Tiež vám umožňuje mať svoje vlastné moduly a doplnky (library/). Ale v porovnaní s úlohami v playbooku (ktorý môže mať aj toto všetko) je tu jediná výhoda, že súbory sa nesypú na jednu, ale na niekoľko samostatných kôp.

Ešte jeden detail: môžete sa pokúsiť vytvoriť roly, ktoré budú k dispozícii na opätovné použitie (cez galaxiu). S príchodom kolekcií možno považovať rozdelenie rolí takmer za zabudnuté.

Roly teda majú dve dôležité vlastnosti: majú predvolené hodnoty (jedinečná vlastnosť) a umožňujú vám štruktúrovať váš kód.

Vráťme sa k pôvodnej otázke: kedy robiť úlohy a kedy úlohy? Úlohy v playbooku sa najčastejšie používajú buď ako „lepidlo“ pred/po rolách, alebo ako samostatný stavebný prvok (potom by v kóde nemali byť žiadne roly). Kopa normálnych úloh zmiešaných s rolami je jednoznačná lajdáckosť. Mali by ste dodržiavať špecifický štýl – buď úlohu, alebo rolu. Roly poskytujú oddelenie entít a predvolených hodnôt, úlohy umožňujú rýchlejšie čítanie kódu. Zvyčajne sa do rolí vkladá „stacionárnejší“ (dôležitý a komplexný) kód a pomocné skripty sa píšu v štýle úloh.

Import_role je možné urobiť ako úlohu, ale ak to napíšete, buďte pripravený vysvetliť svojmu zmyslu pre krásu, prečo to chcete urobiť.

Bystrý čitateľ môže povedať, že roly môžu importovať roly, roly môžu mať závislosti cez galaxy.yml a existuje aj hrozná a strašná include_role — Pripomínam, že zdokonaľujeme zručnosti v základnej Ansible, a nie v krasovej gymnastike.

Psovodi a úlohy

Poďme diskutovať o ďalšej očividnej veci: manipulátoroch. Vedieť ich správne používať je takmer umenie. Aký je rozdiel medzi handlerom a ťahačom?

Keďže si pamätáme základy, tu je príklad:

- hosts: group1
  tasks:
    - foo:
      notify: handler1
  handlers:
     - name: handler1
       bar:

Obslužné nástroje roly sa nachádzajú v rolename/handlers/main.yaml. Obslužné osoby sa prehrabávajú medzi všetkými účastníkmi hry: úlohy pred/po_úlohách môžu vytiahnuť používateľov úloh a rola vytiahnuť používateľov z hry. Avšak „cross-role“ volania pre handlerov spôsobujú oveľa viac wtf ako opakovanie triviálneho handlera. (Ďalším prvkom osvedčených postupov je snažiť sa neopakovať mená obslužných programov).

Hlavný rozdiel je v tom, že úloha sa vždy vykoná (idempotentne) (značky plus/mínus a when), a handler - zmenou stavu (oznamovať požiare iba vtedy, ak bol zmenený). Čo to znamená? Napríklad skutočnosť, že keď reštartujete, ak nedošlo k žiadnej zmene, nebude tam žiadny handler. Prečo by sa mohlo stať, že musíme spustiť handler, keď v úlohe generovania nedošlo k žiadnej zmene? Napríklad preto, že sa niečo pokazilo a zmenilo, ale prevedenie sa k psovodovi nedostalo. Napríklad preto, že sieť bola dočasne mimo prevádzky. Konfigurácia sa zmenila, služba nebola reštartovaná. Pri ďalšom spustení sa konfigurácia už nezmení a služba zostane so starou verziou konfigurácie.

Situacia s konfiguraciou sa neda vyriesit (presnejsie si pre seba mozes vymysliet specialny restart protokol s priznakmi suboru a pod., ale to uz nie je 'basic ansible' v ziadnej podobe). Existuje však ďalší spoločný príbeh: nainštalovali sme aplikáciu, nahrali ju .service-súbor a teraz to chceme daemon_reload и state=started. A zdá sa, že prirodzeným miestom na to je psovod. Ale ak to neurobíte ako handler, ale úlohu na konci zoznamu úloh alebo roly, potom sa to zakaždým vykoná idempotentne. Aj keby sa playbook v polovici zlomil. Tým sa problém reštartu vôbec nerieši (úlohu s atribútom reštartovať nemôžete, pretože sa stráca idempotencia), ale určite sa oplatí urobiť state=started, zvyšuje sa celková stabilita playbookov, pretože počet spojení a dynamický stav klesá.

Ďalšou pozitívnou vlastnosťou handlera je, že neupcháva výstup. Nenastali žiadne zmeny – žiadne extra preskočené alebo ok vo výstupe – ľahšie čitateľné. Je to aj negatívna vlastnosť - ak hneď pri prvom spustení nájdete preklep v lineárne vykonávanej úlohe, tak sa handlery vykonajú až pri zmene, t.j. za určitých podmienok - veľmi zriedkavo. Napríklad prvýkrát v živote o päť rokov neskôr. A samozrejme, že v názve bude preklep a všetko sa pokazí. A ak ich nespustíte druhýkrát, nič sa nezmenilo.

Samostatne musíme hovoriť o dostupnosti premenných. Ak napríklad upozorníte úlohu pomocou slučky, čo bude v premenných? Môžete hádať analyticky, ale nie je to vždy triviálne, najmä ak premenné pochádzajú z rôznych miest.

... Takže manipulátory sú oveľa menej užitočné a oveľa problematickejšie, ako sa zdá. Ak dokážete napísať niečo krásne (bez ozdôb) bez ovládačov, je lepšie to urobiť bez nich. Ak to nejde krásne, je to s nimi lepšie.

Žieravý čitateľ správne poukazuje na to, že sme nediskutovali listenže handler môže zavolať notifikáciu pre iného handlera, že handler môže zahrnúť import_tasks (čo môže urobiť include_role s with_items), že systém handlera v Ansible je Turing-complete, že handlery z include_role sa zvláštnym spôsobom prelínajú s handlermi zo hry, atď. .d. - toto všetko zjavne nie je „základ“).

Aj keď existuje jedna špecifická WTF, ktorá je v skutočnosti funkciou, ktorú musíte mať na pamäti. Ak je vaša úloha vykonaná pomocou delegate_to a má notify, potom sa príslušný handler vykoná bez delegate_to, t.j. na hostiteľovi, kde je priradená hra. (Aj keď psovod, samozrejme, môže mať delegate_to Rovnaké).

Samostatne by som chcel povedať pár slov o opakovane použiteľných rolách. Predtým, ako sa objavili kolekcie, existovala myšlienka, že by ste mohli vytvoriť univerzálne roly, ktoré by mohli byť ansible-galaxy install a šiel. Funguje na všetkých OS všetkých variantov vo všetkých situáciách. Takže môj názor: nefunguje to. Akákoľvek rola s hmotnosťou include_vars, podporujúci 100500 1 prípadov, je odsúdený na priepasť rohových chýb. Môžu byť pokryté masívnym testovaním, ale ako pri akomkoľvek testovaní, buď máte karteziánsky súčin vstupných hodnôt a celkovej funkcie, alebo máte pokryté „individuálne scenáre“. Môj názor je, že je oveľa lepšie, ak je rola lineárna (cyklomatická zložitosť XNUMX).

Čím menej ak (výslovných alebo deklaratívnych - vo forme when alebo formulár include_vars množinou premenných), tým lepšia je rola. Niekedy musíte robiť vetvy, ale opakujem, čím menej ich je, tým lepšie. Takže to vyzerá ako dobrá rola s galaxiou (funguje to!) s partiou when môže byť menej preferovaná ako „vlastná“ rola z piatich úloh. Moment, keď je rola s galaxiou lepšia, je, keď začnete niečo písať. Moment, kedy je to horšie, je, keď sa niečo pokazí a máte podozrenie, že je to kvôli „úlohe s galaxiou“. Otvoríte ho a je tam päť inklúzií, osem hárkov úloh a stoh when'ov... A musíme na to prísť. Namiesto 5 úloh lineárny zoznam, v ktorom nie je čo pokaziť.

V nasledujúcich častiach

  • Niečo málo o inventári, skupinové premenné, plugin host_group_vars, hostvars. Ako uviazať gordický uzol so špagetami. Premenné rozsahu a priority, Ansible pamäťový model. "Kde teda uložíme používateľské meno pre databázu?"
  • jinja: {{ jinja }} — nosql notype nosense mäkká plastelína. Je všade, aj tam, kde by ste to nečakali. Trochu o !!unsafe a lahodný yaml.

Zdroj: hab.com

Pridať komentár