Ansible osnove, brez katerih bodo vaše knjige z igrami le kepa lepljive testenine

Veliko pregledujem kodo Ansible drugih ljudi in tudi sam veliko pišem. Pri analiziranju napak (tako tujih kot svojih) in številnih intervjujih sem spoznal glavno napako, ki jo delajo uporabniki Ansiblea – spuščajo se v kompleksne stvari, ne da bi obvladali osnovne.

Da bi popravil to univerzalno krivico, sem se odločil napisati uvod v Ansible za tiste, ki ga že poznajo. Opozarjam vas, da to ni pripovedovanje moških, to je dolgo branje z veliko črkami in brez slik.

Pričakovana raven bralca je, da je že napisanih več tisoč vrstic yamle, nekaj je že v izdelavi, a »nekako je vse pokvarjeno«.

Imena

Glavna napaka, ki jo naredi uporabnik Ansible, je, da ne ve, kako se nekaj imenuje. Če ne poznate imen, ne morete razumeti, kaj piše v dokumentaciji. Živ primer: med intervjujem oseba, ki je rekla, da veliko piše v Ansibleu, ni znala odgovoriti na vprašanje "iz katerih elementov je sestavljena knjiga iger?" In ko sem predlagal, da je bil "odgovor pričakovan, da je knjiga iger sestavljena iz igre," je sledil obsojajoč komentar "tega ne uporabljamo". Ljudje pišejo Ansible za denar in ne uporabljajo igre. Dejansko ga uporabljajo, vendar ne vedo, kaj je.

Pa začnimo z nečim preprostim: kako se imenuje? Morda to veste ali pa ne, ker niste bili pozorni, ko ste prebrali dokumentacijo.

ansible-playbook izvede playbook. Playbook je datoteka s pripono yml/yaml, znotraj katere je nekaj takega:

---
- hosts: group1
  roles:
    - role1

- hosts: group2,group3
  tasks:
    - debug:

Ugotovili smo že, da je ta celotna datoteka knjiga iger. Lahko pokažemo, kje so vloge in kje naloge. Toda kje je igra? In kakšna je razlika med igro in vlogo ali igro?

Vse je v dokumentaciji. In to pogrešajo. Začetniki - ker je preveč in se ne boste spomnili vsega naenkrat. Izkušeni - ker "neznatne stvari". Če ste izkušeni, ponovno preberite te strani vsaj enkrat na šest mesecev in vaša koda bo postala vodilna v razredu.

Torej, ne pozabite: Playbook je seznam, sestavljen iz play in import_playbook.
To je ena igra:

- hosts: group1
  roles:
    - role1

in to je tudi druga igra:

- hosts: group2,group3
  tasks:
    - debug:

Kaj je igra? Zakaj je?

Igra je ključni element za playbook, saj igra in samo igra povezuje seznam vlog in/ali nalog s seznamom gostiteljev, na katerih jih je treba izvesti. V globokih globinah dokumentacije lahko najdete omembo delegate_to, vtičniki lokalnega iskanja, nastavitve, specifične za omrežje, gostitelji skokov itd. Omogočajo vam, da nekoliko spremenite kraj izvajanja nalog. Ampak, pozabi na to. Vsaka od teh pametnih možnosti ima zelo specifično uporabo in vsekakor ni univerzalna. In govorimo o osnovnih stvareh, ki bi jih moral poznati in uporabljati vsak.

Če želite »nekaj« izvesti »nekje«, napišite igra. Ni vloga. Ni vloga z moduli in pooblaščenci. Vzameš in napišeš play. V katerem v polju gostitelji navedete, kje izvajati, v vlogah/nalogah pa kaj izvajati.

Preprosto, kajne? Kako bi lahko bilo drugače?

Eden od značilnih trenutkov, ko imajo ljudje željo po tem, ne skozi igro, je »vloga, ki vse postavlja«. Želel bi imeti vlogo, ki konfigurira strežnike prve vrste in strežnike druge vrste.

Arhetipski primer je spremljanje. Rad bi imel nadzorno vlogo, ki bo konfigurirala spremljanje. Vloga spremljanja je dodeljena nadzornim gostiteljem (glede na igranje). Toda izkazalo se je, da moramo za spremljanje dostaviti pakete gostiteljem, ki jih spremljamo. Zakaj ne bi uporabili delegata? Prav tako morate konfigurirati iptables. delegat? Prav tako morate napisati/popraviti konfiguracijo za DBMS, da omogočite nadzor. delegat! In če manjka ustvarjalnosti, potem lahko naredite delegacijo include_role v ugnezdeni zanki z uporabo zapletenega filtra na seznamu skupin in znotraj include_role zmoreš več delegate_to ponovno. In gremo...

Dobra želja - imeti eno samo nadzorno vlogo, ki »dela vse« - nas pripelje v popolni pekel, iz katerega je najpogosteje le en izhod: vse napisati na novo.

Kje se je tukaj zgodila napaka? V trenutku, ko ste odkrili, da morate za izvedbo naloge "x" na gostitelju X iti na gostitelj Y in tam narediti "y", ste morali narediti preprosto vajo: iti in napisati predvajanje, kar na gostitelju Y naredi y. Ne dodajajte nečesa k "x", ampak napišite od začetka. Tudi s trdo kodiranimi spremenljivkami.

Zdi se, da je vse v zgornjih odstavkih povedano pravilno. Ampak to ni vaš primer! Ker želite napisati kodo za večkratno uporabo, ki je SUHA in podobna knjižnici, in morate poiskati metodo, kako to narediti.

Tu se skriva še ena resna napaka. Napaka, ki je marsikateri projekt iz znosno napisanega (lahko bi bilo bolje, a vse deluje in je enostavno dokončati) spremenila v popolno grozo, ki je niti avtor ne zna razbrati. Deluje, ampak bog ne daj, da kaj spremeniš.

Napaka je: vloga je knjižnična funkcija. Ta analogija je uničila toliko dobrih začetkov, da je preprosto žalostno gledati. Vloga ni funkcija knjižnice. Ne zna računati in ne more sprejemati odločitev na ravni igre. Opomni me, kakšne odločitve sprejema igra?

Hvala, prav imaš. Play se odloči (natančneje, vsebuje informacije), katere naloge in vloge bo izvajal na katerem gostitelju.

Če to odločitev preneseš na vlogo, pa še z izračuni, obsojaš sebe (in tistega, ki bo poskušal razčleniti tvojo kodo) na beden obstoj. Vloga ne odloča o tem, kje se izvaja. To odločitev sprejme igra. Vloga naredi, kar se ji reče, kjer se ji reče.

Zakaj je nevarno programirati v Ansibleu in zakaj je COBOL boljši od Ansiblea bomo govorili v poglavju o spremenljivkah in jinji. Za zdaj povejmo eno stvar - vsak vaš izračun pusti za seboj neizbrisno sled sprememb v globalnih spremenljivkah in proti temu ne morete storiti ničesar. Takoj, ko sta se »sledi« križali, je vse izginilo.

Opomba za nezaželene: vloga lahko zagotovo vpliva na tok nadzora. Jejte delegate_to in ima razumno uporabo. Jejte meta: end host/play. Ampak! Se spomnite, da poučujemo osnove? Pozabil na delegate_to. Govorimo o najpreprostejši in najlepši Ansible kodi. Ki je enostaven za branje, enostaven za pisanje, enostaven za odpravljanje napak, enostaven za testiranje in enostaven za dokončanje. Torej, še enkrat:

play in samo play odloča, na katerih gostiteljih se kaj izvaja.

V tem delu smo se ukvarjali z nasprotjem med igro in vlogo. Zdaj pa se pogovorimo o razmerju med nalogami in vlogami.

Naloge in vloge

Razmislite o igri:

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

Recimo, da morate narediti foo. In izgleda kot foo: name=foobar state=present. Kam naj to napišem? v pred? objaviti? Ustvariti vlogo?

...In kam so izginile naloge?

Spet začenjamo z osnovami – igralno napravo. Če lebdite pri tem vprašanju, igre ne morete uporabiti kot osnovo za vse ostalo in vaš rezultat bo "šibak".

Naprava za predvajanje: direktiva o gostiteljih, nastavitve za samo predvajanje in odseki pre_tasks, tasks, roles, post_tasks. Preostali parametri za igro nam zdaj niso pomembni.

Vrstni red njihovih razdelkov z nalogami in vlogami: pre_tasks, roles, tasks, post_tasks. Ker je pomensko vrstni red izvedbe med tasks и roles ni jasno, potem najboljše prakse pravijo, da dodajamo razdelek tasks, samo če ne roles... Če obstaja roles, nato pa so vse priložene naloge uvrščene v razdelke pre_tasks/post_tasks.

Ostane le še to, da je vse pomensko jasno: prvi pre_taskstorej rolestorej post_tasks.

Še vedno pa nismo odgovorili na vprašanje: kje je klic modula? foo napisati? Ali moramo za vsak modul napisati celotno vlogo? Ali pa je bolje imeti debelo vlogo za vse? In če ne vloga, kje naj pišem - v pred ali po?

Če na ta vprašanja ni utemeljenega odgovora, potem je to znak pomanjkanja intuicije, torej tistih istih »majhnih temeljev«. Ugotovimo. Najprej varnostno vprašanje: Če igra ima pre_tasks и post_tasks (in ni nalog ali vlog), potem se lahko kaj pokvari, če izvedem prvo nalogo iz post_tasks Bom premaknil do konca pre_tasks?

Seveda formulacija vprašanja namiguje, da se bo zlomilo. Toda kaj točno?

... Vodniki. Branje osnov razkrije pomembno dejstvo: vsi obdelovalci se samodejno izpraznijo po vsakem razdelku. Tisti. vse naloge iz pre_tasks, nato vsi upravljavci, ki so bili obveščeni. Nato se izvedejo vse vloge in vsi obdelovalci, ki so bili obveščeni v vlogah. Po post_tasks in njihovi skrbniki.

Torej, če povlečete opravilo iz post_tasks в pre_tasks, potem ga boste potencialno izvedli, preden se izvede upravljalnik. na primer, če v pre_tasks je spletni strežnik nameščen in konfiguriran ter post_tasks ji je nekaj poslano, nato to nalogo prenesite v razdelek pre_tasks bo privedlo do dejstva, da v času "pošiljanja" strežnik še ne bo deloval in se bo vse pokvarilo.

Zdaj pa pomislimo še enkrat, zakaj jih potrebujemo pre_tasks и post_tasks? Na primer, da dokončate vse potrebno (vključno z vodji), preden izpolnite vlogo. A post_tasks nam bo omogočil delo z rezultati izvajanja vlog (vključno z upravljavci).

Bistroumni strokovnjak za Ansible nam bo povedal, kaj je to. meta: flush_handlers, ampak zakaj potrebujemo flush_handlerje, če se lahko zanesemo na vrstni red izvajanja odsekov v igri? Poleg tega nam lahko uporaba meta: flush_handlers povzroči nepričakovane stvari s podvojenimi ročevalniki, ki nam ob uporabi dajo nenavadna opozorila when у block itd. Bolje ko poznate ansible, več nians lahko poimenujete za "zapleteno" rešitev. In preprosta rešitev - uporaba naravne delitve med pred/vloge/post - ne povzroča nians.

In nazaj k našemu 'foo'. Kam naj ga dam? Pred, post ali vloge? Očitno je to odvisno od tega, ali potrebujemo rezultate obdelovalca za foo. Če jih ni, potem foo ni treba postaviti v pre ali post - ti razdelki imajo poseben pomen - izvajanje nalog pred in za glavnim delom kode.

Zdaj se odgovor na vprašanje "vloga ali naloga" zmanjša na to, kar je že v igri - če so tam naloge, jih morate dodati nalogam. Če obstajajo vloge, morate ustvariti vlogo (tudi iz ene naloge). Naj vas spomnim, da se naloge in vloge ne uporabljajo hkrati.

Razumevanje osnov Ansible nudi razumne odgovore na navidezna vprašanja okusa.

Naloge in vloge (drugi del)

Zdaj pa se pogovorimo o situaciji, ko šele začenjate pisati knjigo iger. Narediti morate foo, bar in baz. So to tri naloge, ena vloga ali tri vloge? Če povzamem vprašanje: na kateri točki bi morali začeti pisati vloge? Kakšen smisel ima pisanje vlog, ko pa lahko pišete naloge?... Kaj je vloga?

Ena največjih napak (o tem sem že govoril) je misliti, da je vloga kot funkcija v knjižnici programa. Kako izgleda generični opis funkcije? Sprejema vhodne argumente, sodeluje s stranskimi vzroki, izvaja stranske učinke in vrne vrednost.

Zdaj pa pozor. Kaj je mogoče narediti iz tega v vlogi? Vedno ste dobrodošli, da pokličete stranske učinke, to je bistvo celotnega Ansiblea - ustvariti stranske učinke. Ali imate stranske vzroke? Osnovno. Toda s "predaj vrednost in jo vrni" - tukaj ne deluje. Prvič, vlogi ne morete posredovati vrednosti. V razdelku vars za vlogo lahko nastavite globalno spremenljivko z doživljenjsko velikostjo igranja. Znotraj vloge lahko nastavite globalno spremenljivko z življenjsko dobo. Ali celo z življenjsko dobo knjig (set_fact/register). Vendar ne morete imeti "lokalnih spremenljivk". Ne morete "vzeti vrednosti" in jo "vrniti".

Iz tega sledi glavna stvar: v Ansible ne morete napisati nečesa, ne da bi povzročili stranske učinke. Spreminjanje globalnih spremenljivk je vedno stranski učinek funkcije. V Rustu je na primer spreminjanje globalne spremenljivke unsafe. In v Ansibleu je to edina metoda za vplivanje na vrednosti za vlogo. Upoštevajte uporabljene besede: ne "predajte vrednost vlogi", ampak "spremenite vrednosti, ki jih vloga uporablja". Med vlogami ni izolacije. Med nalogami in vlogami ni izolacije.

Skupaj: vloga ni funkcija.

Kaj je dobrega v vlogi? Prvič, vloga ima privzete vrednosti (/default/main.yaml), drugič, vloga ima dodatne imenike za shranjevanje datotek.

Kakšne so prednosti privzetih vrednosti? Ker so v Maslowovi piramidi, Ansiblejevi precej izkrivljeni tabeli spremenljivih prioritet, privzete vloge najbolj nizko prioritetne (brez parametrov ukazne vrstice Ansible). To pomeni, da če morate zagotoviti privzete vrednosti in vas ne skrbi, da bodo preglasile vrednosti iz inventarja ali skupinskih spremenljivk, potem so privzete vloge edino pravo mesto za vas. (Malo lažem - več jih je |d(your_default_here), če pa govorimo o stacionarnih mestih, potem samo privzete vloge).

Kaj je še super pri vlogah? Ker imajo svoje kataloge. To so imeniki za spremenljivke, stalne (tj. izračunane za vlogo) in dinamične (obstaja vzorec ali anti-vzorec - include_vars s {{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml.). To so imeniki za files/, templates/. Prav tako vam omogoča, da imate lastne module in vtičnike (library/). Toda v primerjavi z nalogami v playbooku (ki ima lahko tudi vse to) je tukaj edina prednost ta, da se datoteke ne zložijo na en kup, ampak na več ločenih kupčkov.

Še ena podrobnost: lahko poskusite ustvariti vloge, ki bodo na voljo za ponovno uporabo (preko galaksije). S pojavom zbirk lahko razdelitev vlog štejemo za skoraj pozabljeno.

Tako imajo vloge dve pomembni lastnosti: imajo privzete vrednosti (edinstvena lastnost) in vam omogočajo strukturiranje kode.

Če se vrnem k prvotnemu vprašanju: kdaj delati naloge in kdaj vloge? Naloge v knjigi se najpogosteje uporabljajo kot »lepilo« pred/za vlogami ali kot samostojni gradbeni element (takrat v kodi ne sme biti vlog). Kopica običajnih opravil, pomešanih z vlogami, je nedvoumna šlamparija. Držati se morate določenega stila - bodisi naloge bodisi vloge. Vloge zagotavljajo ločevanje entitet in privzetih vrednosti, naloge vam omogočajo hitrejše branje kode. Običajno je bolj »stacionarna« (pomembna in zapletena) koda postavljena v vloge, pomožni skripti pa so napisani v slogu nalog.

Import_role je mogoče izvesti kot nalogo, a če to napišete, bodite pripravljeni razložiti svojemu čutu za lepoto, zakaj želite to narediti.

Pronicljiv bralec lahko reče, da lahko vloge uvažajo vloge, vloge imajo lahko odvisnosti prek galaxy.yml, obstaja pa tudi grozen in grozen include_role — Opomnim vas, da izboljšujemo veščine v osnovni Ansible in ne v figurni gimnastiki.

Voditelji in naloge

Pogovorimo se še o eni očitni stvari: o upravljalcih. Znati jih pravilno uporabljati je skoraj umetnost. Kakšna je razlika med vodnikom in vlečenjem?

Ker smo se spomnili osnov, je tukaj primer:

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

Rokovniki vloge se nahajajo v imerologe/obdelovalci/main.yaml. Obdelovalci brskajo med vsemi udeleženci igre: pre/post_task lahko potegnejo obdelovalce vlog, vloga pa lahko pritegne obdelovalce iz igre. Vendar pa klici "navzkrižnih vlog" za obdelovalce povzročajo veliko več težav kot ponavljanje trivialnega obdelovalca. (Drugi element najboljših praks je, da ne ponavljate imen obdelovalcev).

Glavna razlika je v tem, da se naloga vedno izvaja (idempotentno) (oznake plus/minus in when), in upravljalnik - s spremembo stanja (obvestilo se sproži samo, če je bilo spremenjeno). Kaj to pomeni? Na primer dejstvo, da ob ponovnem zagonu, če ni bilo sprememb, potem ne bo nobenega obdelovalca. Zakaj bi morda morali izvesti upravljalnik, ko ni bilo nobene spremembe v generiranem opravilu? Na primer, ker se je nekaj pokvarilo in spremenilo, vendar izvedba ni dosegla upravljavca. Na primer, ker omrežje začasno ni delovalo. Konfiguracija je bila spremenjena, storitev ni bila ponovno zagnana. Ko ga naslednjič zaženete, se konfiguracija ne spremeni več in storitev ostane s staro različico konfiguracije.

Situacije s konfiguracijo ni mogoče rešiti (natančneje, sami si lahko izmislite poseben protokol ponovnega zagona z zastavicami datotek itd., vendar to ni več "osnovno antabilno" v nobeni obliki). Je pa še ena pogosta zgodba: namestili smo aplikacijo, posneli .service-datoteko, in zdaj jo želimo daemon_reload и state=started. In zdi se, da je naravno mesto za to vodnik. Toda če ga ne naredite za obdelovalca, temveč za nalogo na koncu seznama opravil ali vloge, se bo vsakič izvedla idempotentno. Tudi če bi se igralna knjiga zlomila na sredini. To sploh ne reši težave s ponovnim zagonom (ne morete izvesti naloge z atributom ponovnega zagona, ker je idempotenca izgubljena), vsekakor pa je vredno narediti state=started, splošna stabilnost knjig iger se poveča, ker število povezav in dinamično stanje se zmanjša.

Druga pozitivna lastnost obdelovalca je, da ne zamaši izhoda. Ni bilo nobenih sprememb - nobenih dodatnih preskočenih ali ok v izpisu - lažje branje. To je tudi negativna lastnost - če najdete tipkarsko napako v linearno izvedenem opravilu že ob prvem zagonu, se bodo obdelovalci izvedli šele, ko bodo spremenjeni, tj. pod nekaterimi pogoji - zelo redko. Na primer, prvič v življenju pet let kasneje. In seveda bo prišlo do tipkarske napake v imenu in vse se bo pokvarilo. In če jih drugič ne zaženete, ni nobene spremembe.

Ločeno moramo govoriti o razpoložljivosti spremenljivk. Na primer, če o nalogi obvestite z zanko, kaj bo v spremenljivkah? Lahko ugibate analitično, vendar to ni vedno nepomembno, še posebej, če spremenljivke prihajajo iz različnih krajev.

... Torej so upravljalci veliko manj uporabni in veliko bolj problematični, kot se zdi. Če lahko nekaj lepo (brez dodatkov) napišete brez rokovalcev, je bolje, da to storite brez njih. Če ne uspe lepo, je bolje z njimi.

Jedki bralec upravičeno opozarja, da nismo razpravljali listenda lahko obravnavalec pokliče notify za drugega obdelovalca, da lahko obravnavalec vključuje import_tasks (ki lahko naredi include_role z with_items), da je sistem obdelovalcev v Ansible Turing-complete, da se obdelovalci iz include_role na nenavaden način križajo z obdelovalci iz igre, itd. .d. - vse to očitno ni "osnove").

Čeprav obstaja en poseben WTF, ki je pravzaprav funkcija, ki jo morate imeti v mislih. Če je vaša naloga opravljena z delegate_to in ima obvestilo, potem se ustrezen vodnik izvede brez delegate_to, tj. na gostitelju, kjer je predvajanje dodeljeno. (Čeprav je upravljavec seveda morda delegate_to Enako).

Ločeno bi rad povedal nekaj besed o vlogah za večkratno uporabo. Preden so se pojavile zbirke, je obstajala ideja, da bi lahko naredili univerzalne vloge, ki bi lahko bile ansible-galaxy install in šel. Deluje na vseh OS vseh različic v vseh situacijah. Torej, moje mnenje: ne deluje. Vsaka vloga z maso include_vars, ki podpira 100500 ohišij, je obsojen na brezno zakotnih hroščev ohišij. Zajeti so lahko z obsežnim testiranjem, vendar kot pri vsakem testiranju imate bodisi kartezični produkt vhodnih vrednosti in skupne funkcije ali pa imate »pokrite posamezne scenarije«. Moje mnenje je, da je veliko bolje, če je vloga linearna (ciklomatska kompleksnost 1).

Čim manj če-jev (eksplicitnih ali deklarativnih – v obliki when ali obliki include_vars glede na nabor spremenljivk), boljša je vloga. Včasih je treba narediti veje, ampak, ponavljam, manj jih je, bolje je. Torej se zdi dobra vloga z galaksijo (deluje!) s kupom when morda manj zaželena kot "lastna" vloga iz petih nalog. Trenutek, ko je vloga z galaxyjem boljša, je, ko začneš nekaj pisati. Trenutek, ko postane slabše, je, ko se nekaj pokvari in imaš sum, da je to zaradi »vloge z galaksijo«. Odprete ga in tam je pet vključkov, osem listov z nalogami in kup when'ov... In to moramo ugotoviti. Namesto 5 nalog, linearni seznam, v katerem ni ničesar za prelomiti.

V naslednjih delih

  • Nekaj ​​o inventarju, skupinskih spremenljivkah, vtičniku host_group_vars, hostvars. Kako zavezati gordijski vozel s špageti. Spremenljivke obsega in prednosti, Ansible spominski model. "Torej, kje shranimo uporabniško ime za bazo podatkov?"
  • jinja: {{ jinja }} — mehki plastelin nosql notype nosense. Povsod je, tudi tam, kjer ga ne pričakuješ. Malo o !!unsafe in slasten jaml.

Vir: www.habr.com

Dodaj komentar