Ansible basics, waarsonder jou speelboeke 'n klomp taai pasta is

Ek doen baie resensies vir ander mense se Ansible-kode en skryf baie self. In die loop van die ontleding van die foute (beide ander en my eie), sowel as 'n sekere aantal onderhoude, het ek die hooffout wat Ansible-gebruikers maak, besef - hulle klim in die kompleks sonder om die basiese een te bemeester.

Om hierdie universele onreg reg te stel, het ek besluit om 'n inleiding tot Ansible te skryf vir diegene wat dit reeds ken. Ek waarsku jou, dit is nie 'n hervertelling van mana nie, dit is 'n lang lees waarin daar baie letters en geen prente is nie.

Die verwagte vlak van die leser is dat 'n paar duisend reëls yaml reeds geskryf is, iets is reeds in produksie, maar "op een of ander manier is alles skeef."

name

Die grootste fout van 'n Ansible-gebruiker is om nie te weet wat genoem word nie. As jy nie die name ken nie, kan jy nie verstaan ​​wat in die dokumentasie geskryf staan ​​nie. 'n Lewende voorbeeld: by 'n onderhoud kon 'n persoon wat blykbaar verklaar het dat hy baie in Ansible geskryf het, nie die vraag "uit watter elemente bestaan ​​'n speelboek bestaan?" beantwoord nie. En toe ek voorstel dat “die antwoord verwag word dat die speelboek uit spel bestaan”, toe volg die moordenaar-kommentaar “ons gebruik dit nie” nie. Mense skryf Ansible vir geld en gebruik nie speel nie. Hulle gebruik dit eintlik, maar hulle weet nie wat dit is nie.

Kom ons begin dus met 'n eenvoudige een: wat word dit genoem. Miskien weet jy dit, of dalk nie, want jy het nie aandag gegee toe jy die dokumentasie gelees het nie.

ansible-speelboek voer die speelboek uit. Playbook is 'n yml/yaml-lêer met iets soos hierdie binne:

---
- hosts: group1
  roles:
    - role1

- hosts: group2,group3
  tasks:
    - debug:

Ons het reeds verstaan ​​dat hierdie hele lêer 'n speelboek is. Ons kan wys waar die rolle is, waar die take is. Maar waar is speel? En hoe verskil speel van rol of speelboek?

Dit is alles in die dokumentasie. En dit word oorgeslaan. Beginners - want daar is te veel en jy kan nie alles op een slag onthou nie. Ervare - omdat "onbelangrike dinge." As jy ervare is, herlees hierdie bladsye ten minste een keer elke halfjaar, en jou kode sal 'n klas beter word.

Onthou dus: 'n speelboek is 'n lys van speel en import_playbook.
Dit is een toneelstuk:

- hosts: group1
  roles:
    - role1

en dit is ook nog 'n toneelstuk:

- hosts: group2,group3
  tasks:
    - debug:

Wat is speel? Hoekom is sy?

Speel is 'n sleutelelement vir 'n speelboek, want speel en slegs speel bind 'n lys rolle en/of take aan 'n lys gashere waarop dit uitgevoer moet word. In die diep ingewande van die dokumentasie kan jy 'n melding vind van delegate_to, plaaslike opsoek-inproppe, netwerk-cli-spesifieke instellings, spring-gashere, ens. Hulle laat jou toe om die plek van uitvoering van take effens te verander. Maar, vergeet daarvan. Elkeen van hierdie moeilike opsies het baie spesifieke gebruike, en hulle is beslis nie universeel nie. En ons praat oor basiese dinge wat almal moet weet en gebruik.

As jy "iets" "iewers" wil opvoer - skryf jy toneelstuk. Nie 'n rol nie. Nie 'n rol met modules en afgevaardigdes nie. Jy neem en skryf toneelstuk. Waarin jy in die gashere-veld lys waar om uit te voer, en in rolle/take - wat om uit te voer.

Eenvoudig, reg? En hoe kan dit anders wees?

Een van die kenmerkende oomblikke wanneer mense 'n begeerte het om dit nie deur spel te doen nie, is "die rol wat alles opstel." Ek wil graag 'n rol hê wat beide bedieners van die eerste tipe en bedieners van die tweede tipe konfigureer.

'n Argetipiese voorbeeld is monitering. Ek wil graag 'n moniteringsrol hê wat monitering sal instel. Die moniteringsrol word aan die monitering van gashere toegeken (volgens speel). Maar dit blyk dat om te monitor, ons pakkies moet plaas op die gashere wat ons monitor. Hoekom nie afgevaardigde gebruik nie? Jy moet ook iptables konfigureer. delegeer? En steeds is dit nodig om 'n konfigurasie te skryf/korrigeer vir 'n DBBS wat monitering begin het. delegeer! En as die kreatiewe opduik, dan kan jy 'n afvaardiging maak include_role in 'n geneste lus deur 'n moeilike filter op die lys van groepe, en binne include_role kan jy meer doen delegate_to weer. En ons gaan...

'n Goeie wens - om 'n enkele moniteringsrol te hê wat "alles doen" - lei ons na 'n pikhel waaruit daar meestal een uitweg is: om alles van nuuts af te herskryf.

Waar het die fout gebeur? Die oomblik toe jy ontdek het dat om taak "x" op gasheer X uit te voer, jy na gasheer Y toe moes gaan en "y" daar moes doen, moes jy 'n eenvoudige oefening gedoen het: gaan skryf 'n toneelstuk wat y op gasheer Y doen. Moenie iets by "x" voeg nie, maar skryf van voor af. Selfs met hardgekodeerde veranderlikes.

Alles blyk korrek te wees in die paragrawe hierbo. Maar dit is nie jou geval nie! Omdat jy herbruikbare kode wil skryf wat DROOG en biblioteekagtig is, en jy moet 'n manier vind om dit te doen.

Hier is nog 'n groot fout. ’n Fout wat baie projekte verander het van draaglik geskryf (kan beter wees, maar alles werk en is maklik om by te voeg) tot ’n volslae gruwel wat selfs die skrywer nie kan uitmaak nie. Dit werk, maar God verhoed dat iets verander.

Hierdie fout klink soos volg: 'n rol is 'n biblioteekfunksie. Hierdie analogie het soveel goeie begin verwoes dat dit net hartseer is om te aanskou. 'n Rol is nie 'n biblioteekfunksie nie. Sy kan nie berekeninge doen nie en sy kan nie speelvlakbesluite neem nie. Herinner my watter besluite speel maak?

Dankie, jy is reg. Speel neem 'n besluit (meer presies, dit bevat inligting) oor watter take en rolle om op watter gashere te verrig.

As jy hierdie besluit aan 'n rol delegeer, en selfs met berekeninge, doem jy jouself (en wie jou kode ook al sal probeer ontleed) tot 'n miserabele bestaan. Die rol besluit nie waar om dit te bestuur nie. Hierdie besluit word deur spel geneem. Die rol doen wat dit vertel is, waar dit vertel is.

Hoekom programmering in Ansible gevaarlik is en hoe COBOL beter is as Ansible, sal ons in die hoofstuk oor veranderlikes en jinja bespreek. Kom ons sê vir eers een ding - elkeen van jou berekeninge laat 'n onuitwisbare spoor van veranderende globale veranderlikes, en jy kan niks daaraan doen nie. Sodra die twee “spore” gekruis het, was alles weg.

Let wel vir die korrosiewe: die rol kan natuurlik die beheervloei beïnvloed. Eet delegate_to en dit het redelike gebruike. Eet meta: end host/play. Maar! Onthou ons ons leer die basiese beginsels? Van vergeet delegate_to. Ons praat van die eenvoudigste en mooiste Ansible-kode. Wat maklik is om te lees, maklik om te skryf, maklik om te ontfout, maklik om te toets en maklik om te skryf. So, nog een keer:

speel en net speel besluit op watter gashere wat uitgevoer word.

In hierdie afdeling het ons die opposisie van spel en rol behandel. Kom ons praat nou oor die take vs rolverhouding.

Take en Rolle

Oorweeg speel:

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

Kom ons sê jy moet foo doen. En dit lyk soos foo: name=foobar state=present. Waar om dit te skryf? in voor? Post? Rol skep?

… En waar het die take gegaan?

Ons begin weer met die basiese beginsels – die speeltoestel. As jy in hierdie saak swem, kan jy nie speel as basis vir al die ander gebruik nie, en jou resultaat is "wankelrig".

Die speeltoestel: die gasheer-instruksie, instellings vir die toneelstuk self en die pre_tasks, take, rolle, post_tasks afdeling. Die oorblywende parameters vir spel is nie nou vir ons belangrik nie.

Die volgorde van hul afdelings met take en rolle: pre_tasks, roles, tasks, post_tasks. Aangesien, semanties, die volgorde van uitvoering tussen tasks и roles is nie duidelik nie, dan sê beste praktyke dat ons 'n afdeling byvoeg tasks, net indien nie roles... As daar is roles, dan word al die aangehegte take in die afdelings geplaas pre_tasks/post_tasks.

Dit bly net dat alles semanties duidelik is: eerstens pre_tasks, dan roles, dan post_tasks.

Maar ons het steeds nie die vraag beantwoord nie: waar is die module-oproep foo skryf? Moet ons 'n hele rol vir elke module skryf? Of is dit beter om 'n dik rol vir alles te hê? En indien nie 'n rol nie, waar om dan te skryf - in pre of in post?

As daar geen beredeneerde antwoord op hierdie vrae is nie, dan is dit 'n teken van 'n gebrek aan intuïsie, dit wil sê daardie baie "wankelrige fondamente". Kom ons vind dit uit. Veiligheidsvraag eerste: As speel het pre_tasks и post_tasks (en daar is nie take of rolle), dan kan iets breek as ek eerste taak van post_tasks beweeg na die einde pre_tasks?

Natuurlik dui die bewoording van die vraag daarop dat dit sal breek. Maar wat presies?

… Hanteerders. Die lees van die basiese beginsels openbaar 'n belangrike feit: alle hanteerders word outomaties na elke afdeling gespoel. Dié. alle take van pre_tasks, dan stel al die hanteerders in kennis. Dan word alle rolle en alle hanteerders wat in rolle in kennis gestel is, uitgevoer. Na post_tasks en hul hanteerders.

Dus, as jy 'n taak van sleep post_tasks в pre_tasks, dan voer jy dit moontlik uit voordat die hanteerder dit uitvoer. byvoorbeeld, as in pre_tasks installeer en konfigureer die webbediener, en post_tasks iets word daarheen gestuur, dan die oordrag van hierdie taak na die afdeling pre_tasks sal lei tot die feit dat die bediener op die oomblik van "stuur" nog nie begin word nie en alles sal breek.

Nou kom ons dink weer, hoekom doen ons pre_tasks и post_tasks? Byvoorbeeld, om alles wat nodig is (insluitend hanteerders) uit te voer voordat die rol uitgevoer word. A post_tasks sal ons toelaat om te werk met die resultate van die uitvoering van rolle (insluitend hanteerders).

Ansible fynproewer sal ons vertel wat is meta: flush_handlers, maar hoekom het ons flush_handlers nodig wanneer ons kan staatmaak op die uitvoeringsbevel van die afdelings in die spel? Boonop kan die gebruik van meta: flush_handlers onverwagte dinge aan ons lewer met herhaalde hanteerders, gee ons vreemde waarskuwings in die geval van gebruik when у block ens. Hoe beter jy die moontlike ken, hoe meer nuanses kan jy noem vir 'n "lastige" oplossing. En 'n eenvoudige oplossing - die gebruik van 'n natuurlike skeiding tussen voor / rolle / post - veroorsaak nie nuanses nie.

En, terug na ons 'foo'. Waar om dit te sit? In voor, pos of in rolle? Dit hang natuurlik daarvan af of ons die resultate van die hanteerder vir foo wil hê. As hulle nie daar is nie, hoef foo nie in pre of post geplaas te word nie - hierdie afdelings het 'n spesiale betekenis - om take voor en na die hoofkode-skikking uit te voer.

Nou kom die antwoord op die vraag "rol of taak" neer op wat reeds in die spel is - as daar take is, dan moet jy by take byvoeg. As daar rolle is, moet jy 'n rol maak (al is dit van een taak). Ek herinner jou daaraan dat take en rolle nie gelyktydig gebruik word nie.

Om die basiese beginsels van Ansible te verstaan, bied redelike antwoorde op oënskynlike smaakvrae.

Take en rolle (deel twee)

Kom ons bespreek nou die situasie wanneer jy net begin om 'n speelboek te skryf. Jy moet foo, bar en baz maak. Is dit drie take, een rol of drie rolle? Som die vraag op: wanneer moet jy rolle begin skryf? Wat is die punt daarvan om rolle te skryf wanneer jy take kan skryf? ... En wat is 'n rol?

Een van die grootste foute (ek het reeds hieroor gepraat) is om te dink 'n rol is soos 'n funksie in 'n program se biblioteek. Hoe lyk 'n generiese beskrywing van 'n funksie? Dit neem insette argumente, interaksie met newe-oorsake, doen newe-effekte, gee 'n waarde terug.

Nou, aandag. Wat kan hieruit in die rol gedoen word? Bel newe-effekte - jy is altyd welkom, dit is die essensie van die hele Ansible - om newe-effekte te doen. Het sy-oorsake? Elementêr. Maar met "pass a value and return it" - dit is waar dit nie is nie. Eerstens kan jy nie 'n waarde aan 'n rol oordra nie. Jy kan 'n globale veranderlike met 'n leeftyd van grootte speel in die vars-afdeling vir die rol stel. Jy kan 'n globale veranderlike stel met 'n leeftyd in die spel binne 'n rol. Of selfs met 'n speelboek leeftyd (set_fact/register). Maar jy kan nie "plaaslike veranderlikes" hê nie. Jy kan nie "'n waarde vat" en dit "teruggee" nie.

Die belangrikste ding volg hieruit: jy kan nie iets oor ansible skryf nie en nie newe-effekte veroorsaak nie. Die verandering van globale veranderlikes is altyd 'n newe-effek vir 'n funksie. In Rust, byvoorbeeld, is die verandering van 'n globale veranderlike unsafe. En in Ansible - die enigste manier om die waardes vir die rol te beïnvloed. Gee aandag aan die woorde wat gebruik word: nie "gee 'n waarde aan 'n rol oor nie", maar "verander die waardes wat die rol gebruik". Daar is geen isolasie tussen rolle nie. Daar is geen isolasie tussen take en rolle nie.

Totaal: rol is nie 'n funksie nie.

Wat is goed aan 'n rol? Eerstens het die rol verstekwaardes (/default/main.yaml), tweedens het die rol bykomende gidse om lêers te vou.

Waarom is standaardwaardes goed? Die feit dat in Maslow se piramide, Ansible se taamlik verdraaide veranderlike prioriteitstabel, rolversteurings die meeste nie-prioriteit is (minus die ansible opdragreëlparameters). Dit beteken dat as u verstekwaardes moet verskaf en nie bekommerd moet wees dat hulle voorraad- of groepveranderlikes oorheers nie, rolversteurings die enigste regte plek vir u is. (Ek jok 'n bietjie - daar is meer |d(your_default_here), maar as ons praat oor stilstaande plekke, dan is slegs verstek van rolle).

Wat is nog goed aan die rolle? Die feit dat hulle hul eie gidse het. Dit is gidse vir veranderlikes, beide konstant (d.w.s. bereken vir die rol) en dinamies (daar is so 'n patroon, of 'n anti-patroon - include_vars saam met {{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml.). Dit is gidse vir files/, templates/. Dit laat jou ook toe om rolle van jou eie modules en plugins te hê (library/). Maar in vergelyking met die take van die speelboek (wat ook dit alles kan hê), is die enigste voordeel hier dat die lêers nie in een hoop gestort word nie, maar verskeie afsonderlike hope.

Nog een detail: jy kan probeer om rolle te maak wat vir hergebruik beskikbaar sal wees (via sterrestelsel). Na die koms van versamelings kan rolleverdeling as amper vergete beskou word.

Rolle het dus twee belangrike kenmerke: hulle het verstekke ('n unieke kenmerk) en dit laat jou toe om jou kode te struktureer.

Keer terug na die oorspronklike vraag: wanneer om take te doen en wanneer om rolle te doen? Take in die speelboek word meestal óf as "gom" voor/na rolle gebruik, óf as 'n onafhanklike bouelement (dan behoort daar geen rolle in die kode te wees nie). ’n Hoop normale take gemeng met rolle is ’n ondubbelsinnige slordigheid. Jy moet by 'n spesifieke styl hou - óf take óf rolle. Rolle gee skeiding van entiteite en verstek, take laat jou toe om die kode vinniger te lees. Gewoonlik word meer "stasionêre" (belangrike en komplekse) kode in die rol uitgeneem, en hulpskrifte word in die styl van take geskryf.

Dit is moontlik om import_role as 'n taak te doen, maar as jy dit skryf, wees dan voorbereid vir 'n verduideliking vir jou eie skoonheidsin, hoekom jy dit wil doen.

Die skerpsinnige leser kan sê dat rolle rolle kan invoer, rolle kan 'n afhanklikheid hê via galaxy.yml, en dan is daar die skrikwekkende en verskriklike include_role - Ek herinner jou daaraan dat ons vaardighede verbeter in basiese Ansible, en nie in figuurgimnastiek nie.

Hanteerders en take

Kom ons bespreek nog een ooglopende ding: hanteerders. Om te weet hoe om hulle reg te gebruik, is amper 'n kuns. Wat is die verskil tussen 'n hanteerder en 'n taak?

Aangesien ons die basiese beginsels onthou, is hier 'n voorbeeld:

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

Die rol se hanteerders is in rolename/handlers/main.yaml. Hanteerders word tussen alle deelnemers aan die toneelstuk gevroetel: pre/post_tasks kan rolhanteerders trek, en 'n rol kan hanteerders uit die toneelstuk trek. "Kruisrol"-oproepe na hanteerders veroorsaak egter baie meer wtf as om 'n onbenullige hanteerder te herhaal. (Nog 'n element van beste praktyke is om die herhaling van hanteerdername te vermy).

Die belangrikste verskil is dat die taak altyd (idempotent) uitgevoer word (plus/minus tags en when), en die hanteerder - deur staatsverandering (kennisgewing werk slegs as dit verander is). Wat is die risiko? Byvoorbeeld, die feit dat wanneer jy herbegin, as dit nie verander is nie, daar geen hanteerder sal wees nie. En hoekom kan dit wees dat ons die hanteerder moet uitvoer wanneer die ouertaak nie verander het nie? Byvoorbeeld, omdat iets gebreek en verander is, maar die uitvoering het nie die hanteerder bereik nie. Byvoorbeeld, omdat die netwerk tydelik af was. Die konfigurasie het verander, die diens is nie herbegin nie. By die volgende begin verander die konfigurasie nie meer nie, en die diens bly by die ou weergawe van die konfigurasie.

Die situasie met die konfigurasie is nie oplosbaar nie (meer presies, jy kan vir jouself 'n spesiale herbegin protokol uitdink met lêervlae, ens., maar dit is nie meer 'basic ansible' in enige vorm nie). Maar daar is nog 'n algemene storie: ons het die toepassing geïnstalleer, dit opgeneem .service-lêer, en nou wil ons dit hê daemon_reload и state=started. En die natuurlike plek daarvoor blyk die hanteerder te wees. Maar as jy dit nie 'n hanteerder maak nie, maar 'n taak aan die einde van die taaklys of rol, dan sal dit elke keer idempotent uitgevoer word. Al het die speelboek in die middel gebreek. Dit los glad nie die herbegin-probleem op nie (jy kan nie 'n taak met die herbegin-kenmerk doen nie, want idempotensie is verlore), maar dit is beslis die moeite werd om state=started te doen, die algehele stabiliteit van die speelboek verhoog, want. die aantal verbindings en dinamiese toestand neem af.

Nog 'n positiewe eienskap van die hanteerder is dat dit nie die uitset besoedel nie. Daar was geen veranderinge nie - geen ekstra oorgeslaan of ok in die uitvoer nie - makliker om te lees. Dit is ook 'n negatiewe eienskap - as jy 'n tikfout in 'n liniêr uitgevoerde taak op die eerste lopie vind, sal hanteerders slegs uitgevoer word wanneer dit verander word, m.a.w. onder sekere omstandighede - baie selde. Byvoorbeeld, vyf jaar later vir die eerste keer in my lewe. En natuurlik sal daar 'n tikfout in die naam wees en alles sal breek. En die tweede keer dat hulle nie hardloop nie - verander is nie.

Afsonderlik is dit nodig om te praat oor die beskikbaarheid van veranderlikes. Byvoorbeeld, as jy in kennis stel vir 'n taak met 'n lus, wat sal in die veranderlikes wees? Jy kan analities raai, maar dit is nie altyd triviaal nie, veral as die veranderlikes van verskillende plekke af kom.

… Hanteerders is dus baie minder bruikbaar en baie meer problematies as wat hulle lyk. As jy iets pragtig (sonder fieterjasies) sonder hanteerders kan skryf, is dit beter om dit sonder hulle te doen. As dit nie mooi uitwerk nie, is dit beter met hulle.

Die skerpsinnige leser merk tereg op dat ons nie bespreek het nie listendat 'n hanteerder notify op 'n ander hanteerder kan oproep, dat 'n hanteerder import_tasks kan insluit (wat include_role c with_items kan doen), dat Ansible se hanteerderstelsel Turing-volledig is, dat include_role-hanteerders op nuuskierige maniere met spelhanteerders sny, ens. .d. - dit alles is duidelik nie "basics" nie.

Alhoewel daar een spesifieke WTF is wat eintlik 'n kenmerk is wat in gedagte gehou moet word. As jou taak uitgevoer word met delegate_to en dit in kennis gestel het, dan word die ooreenstemmende hanteerder uitgevoer sonder delegate_to, d.w.s. op die gasheer waar spel toegewys word. (Alhoewel die hanteerder natuurlik mag hê delegate_to Dieselfde).

Afsonderlik wil ek 'n paar woorde sê oor herbruikbare rolle. Voor versamelings was daar 'n idee dat jy generiese rolle kon maak wat jy kon ansible-galaxy install en het gegaan. Werk op alle OS van alle variante in alle situasies. So hier is my mening: dit werk nie. Enige rol met 'n massiewe include_vars, die ondersteuning van 100500 gevalle is gedoem tot afgronde van hoekkas goggas. Hulle kan geprop word met massiewe toetsing, maar soos met enige toetsing, het jy óf 'n Cartesiese produk van insetwaardes en 'n totale funksie, óf jy het "afsonderlike scenario's gedek". My mening is dat dit baie beter is as die rol lineêr is (siklomatiese kompleksiteit 1).

Hoe minder if'ov (eksplisiet of verklarend - in die vorm when of vorm include_vars op 'n stel veranderlikes), hoe beter die rol. Soms moet jy takke maak, maar, ek herhaal, hoe minder, hoe beter. So dit blyk 'n goeie rol te wees met die sterrestelsel (dit werk!) met 'n klomp when kan minder verkieslik wees as "eie" rol van vyf take. Die oomblik wanneer die rol met die sterrestelsel beter is, is wanneer jy iets begin skryf. Die oomblik wanneer dit erger word, is wanneer iets breek, en jy het 'n vermoede dat dit as gevolg van die "rol met die sterrestelsel" is. Jy maak dit oop, en daar is vyf insluitings, agt taaklyste en 'n stapel van when'ov ... En dit moet uitgesorteer word. In plaas van 5 take, 'n lineêre lys, waarin daar niks is om te breek nie.

In die volgende dele

  • 'n Bietjie oor voorraad, groepveranderlikes, host_group_vars-inprop, hostvars. Hoe om 'n Gordiese knoop uit spaghetti te bind. Omvang en voorrangsveranderlikes, Ansible geheue model. "So waar stoor jy die gebruikersnaam vir die databasis?".
  • jinja: {{ jinja }} - nosql notype nosense sagte plasticine. Dit is oral, selfs waar jy dit nie verwag nie. 'n Bietjie oor !!unsafe en heerlike yaml.

Bron: will.com

Voeg 'n opmerking