Ansible osnove, bez kojih će vaše knjige s igrama biti grumen ljepljive tjestenine

Puno recenziram tuđi Ansible kod i puno pišem sam. Tijekom analize grešaka (kako tuđih tako i vlastitih), kao i brojnih intervjua, shvatio sam glavnu grešku koju rade korisnici Ansiblea - ulaze u složene stvari, a da ne svladaju one osnovne.

Kako bih ispravio ovu univerzalnu nepravdu, odlučio sam napisati uvod u Ansible za one koji ga već poznaju. Upozoravam vas, ovo nije prepričavanje muškaraca, ovo je longread s puno slova i bez slika.

Očekivana razina čitatelja je da je već napisano nekoliko tisuća redaka yamle, nešto je već u proizvodnji, ali “nekako je sve krivo”.

imena

Glavna pogreška koju Ansible korisnik čini je neznanje kako se nešto zove. Ako ne znate imena, ne možete razumjeti što piše u dokumentaciji. Živi primjer: tijekom intervjua, osoba koja je rekla da je puno pisala u Ansibleu nije znala odgovoriti na pitanje "od kojih se elemenata sastoji knjiga igra?" A kad sam rekao da je "očekivan odgovor da se igra sastoji od igre", uslijedio je osuđujući komentar "mi to ne koristimo". Ljudi pišu Ansible za novac i ne koriste igru. Zapravo ga koriste, ali ne znaju što je to.

Pa počnimo s nečim jednostavnim: kako se to zove. Možda to znate, a možda i ne, jer niste obratili pažnju kada ste čitali dokumentaciju.

ansible-playbook izvršava playbook. Playbook je datoteka s ekstenzijom yml/yaml unutar koje se nalazi nešto poput ovoga:

---
- hosts: group1
  roles:
    - role1

- hosts: group2,group3
  tasks:
    - debug:

Već smo shvatili da je cijela ova datoteka igra. Možemo pokazati gdje su uloge, a gdje zadaci. Ali gdje je igra? A koja je razlika između igre i uloge ili playbooka?

Sve je u dokumentaciji. I nedostaje im. Početnici - jer ima previše i nećete se sjetiti svega odjednom. Iskusan - jer "trivijalne stvari". Ako imate iskustva, ponovno pročitajte ove stranice barem jednom svakih šest mjeseci i vaš će kôd postati vodeći u klasi.

Dakle, zapamtite: Playbook je popis koji se sastoji od play i import_playbook.
Ovo je jedna igra:

- hosts: group1
  roles:
    - role1

a ovo je još jedna igra:

- hosts: group2,group3
  tasks:
    - debug:

Što je igra? Zašto je ona?

Play je ključni element za playbook, jer play and only play povezuje popis uloga i/ili zadataka s popisom hostova na kojima se oni moraju izvršiti. U dubokim dubinama dokumentacije možete pronaći spomen delegate_to, lokalni dodaci za traženje, postavke specifične za mrežni cli, hostovi za skok itd. Omogućuju vam da malo promijenite mjesto na kojem se obavljaju zadaci. Ali, zaboravi na to. Svaka od ovih pametnih opcija ima vrlo specifične namjene i definitivno nije univerzalna. A mi govorimo o osnovnim stvarima koje bi svatko trebao znati i koristiti.

Ako želite izvesti “nešto” “negdje”, napišite svirati. Ne uloga. Nije uloga s modulima i delegatima. Uzmi i napiši igru. U kojem u polju domaćini navodite gdje izvršiti, au ulogama/zadacima - što izvršiti.

Jednostavno, zar ne? Kako bi moglo biti drugačije?

Jedan od karakterističnih trenutaka kada ljudi imaju želju da to učine ne kroz igru ​​je “uloga koja sve postavlja”. Želio bih imati ulogu koja konfigurira poslužitelje prve vrste i poslužitelje druge vrste.

Arhetipski primjer je praćenje. Želio bih imati nadzornu ulogu koja će konfigurirati nadzor. Uloga nadgledanja dodijeljena je nadzornim hostovima (prema igri). Ali ispada da za praćenje trebamo dostaviti pakete hostovima koje nadgledamo. Zašto ne koristiti delegat? Također morate konfigurirati iptables. delegat? Također morate napisati/ispraviti konfiguraciju za DBMS da biste omogućili praćenje. delegat! A ako nedostaje kreativnosti, onda možete napraviti delegaciju include_role u ugniježđenoj petlji pomoću lukavog filtra na popisu grupa i unutar include_role možeš više delegate_to opet. I idemo...

Dobra želja - imati jednu jedinu nadzornu ulogu, koja "sve radi" - vodi nas u potpuni pakao iz kojeg najčešće postoji samo jedan izlaz: prepisati sve ispočetka.

Gdje se ovdje dogodila greška? U trenutku kada ste otkrili da za obavljanje zadatka "x" na hostu X morate otići na host Y i tamo napraviti "y", morali ste napraviti jednostavnu vježbu: idite i napišite play, što na hostu Y radi y. Nemojte dodavati nešto na "x", već to napišite ispočetka. Čak i s tvrdo kodiranim varijablama.

Čini se da je sve u gornjim odlomcima točno rečeno. Ali ovo nije vaš slučaj! Zato što želite pisati kod za višekratnu upotrebu koji je SUHO i sličan biblioteci, i morate potražiti metodu kako to učiniti.

Ovdje se krije još jedna ozbiljna greška. Greška koja je mnoge projekte od podnošljivo napisanih (moglo bi i bolje, ali sve radi i lako se završi) pretvorila u potpuni horor koji ni autor ne može dokučiti. Radi, ali ne daj Bože da išta promijenite.

Pogreška je: uloga je funkcija knjižnice. Ova analogija je uništila toliko dobrih početaka da je jednostavno tužno gledati. Uloga nije funkcija knjižnice. Ona ne zna kalkulirati i ne može donositi odluke na razini igre. Podsjeti me koje odluke donosi igra?

Hvala, u pravu si. Play odlučuje (točnije, sadrži informacije) o tome koje će zadatke i uloge obavljati na kojem hostu.

Delegirate li tu odluku nekoj ulozi, pa još uz kalkulacije, osuđujete sebe (i onoga tko će pokušati raščlaniti vaš kod) na bijednu egzistenciju. Uloga ne odlučuje gdje će se izvoditi. Ova odluka se donosi igrom. Uloga radi što joj se kaže, gdje joj se kaže.

Zašto je opasno programirati u Ansibleu i zašto je COBOL bolji od Ansiblea govorit ćemo u poglavlju o varijablama i jinji. Recimo za sada jedno - svaki vaš izračun za sobom ostavlja neizbrisiv trag promjena globalnih varijabli i vi tu ne možete učiniti ništa. Čim su se dva "traga" ukrstila, sve je nestalo.

Napomena za gadljive: uloga svakako može utjecati na tok kontrole. Jesti delegate_to i ima razumnu upotrebu. Jesti meta: end host/play. Ali! Sjećate se da podučavamo osnove? Zaboravio na delegate_to. Govorimo o najjednostavnijem i najljepšem Ansible kodu. Koji je jednostavan za čitanje, jednostavan za pisanje, jednostavan za uklanjanje pogrešaka, jednostavan za testiranje i jednostavan za dovršavanje. Dakle, još jednom:

play i samo play odlučuje na kojem se hostu što izvršava.

U ovom dijelu bavili smo se suprotnošću između igre i uloge. Sada razgovarajmo o odnosu zadataka i uloga.

Zadaci i uloge

Razmislite o igranju:

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

Recimo da trebate učiniti foo. I izgleda kao foo: name=foobar state=present. Gdje da ovo napišem? u prije? post? Stvoriti ulogu?

...A gdje su nestali zadaci?

Opet počinjemo s osnovama - uređajem za reprodukciju. Ako lebdite po ovom pitanju, ne možete koristiti igru ​​kao osnovu za sve ostalo, a rezultat će vam biti "klimav".

Uređaj za reprodukciju: direktiva o hostovima, postavke za samu reprodukciju i odjeljke pre_tasks, tasks, roles, post_tasks. Preostali parametri za igru ​​sada nam nisu bitni.

Redoslijed njihovih odjeljaka sa zadacima i ulogama: pre_tasks, roles, tasks, post_tasks. Budući da je semantički redoslijed izvršenja između tasks и roles nije jasno, onda najbolje prakse kažu da dodajemo odjeljak tasks, samo ako ne roles... Ako ima roles, zatim su svi priloženi zadaci raspoređeni u odjeljke pre_tasks/post_tasks.

Ostaje samo da je sve semantički jasno: prvi pre_tasks, a zatim roles, a zatim post_tasks.

Ali još uvijek nismo odgovorili na pitanje: gdje je poziv modula? foo pisati? Moramo li napisati cijelu ulogu za svaki modul? Ili je bolje imati debelu ulogu za sve? A ako ne uloga, gdje da pišem - prije ili poslije?

Ako na ova pitanja nema argumentiranog odgovora, onda je to znak nedostatka intuicije, odnosno tih istih “klimavih temelja”. Hajdemo shvatiti. Prvo, sigurnosno pitanje: Ako play ima pre_tasks и post_tasks (i nema zadataka ni uloga), može li se onda nešto pokvariti ako izvršim prvi zadatak iz post_tasks Premjestit ću ga do kraja pre_tasks?

Naravno, formulacija pitanja daje naslutiti da će se slomiti. Ali što točno?

... Rukovatelji. Čitanje osnova otkriva važnu činjenicu: svi se rukovatelji automatski brišu nakon svakog odjeljka. Oni. svi zadaci iz pre_tasks, zatim svi rukovatelji koji su obaviješteni. Zatim se izvršavaju sve uloge i svi rukovatelji koji su bili obaviješteni u ulogama. Nakon post_tasks i njihovim rukovateljima.

Dakle, ako povučete zadatak iz post_tasks в pre_tasks, tada ćete ga potencijalno izvršiti prije nego što se izvrši rukovatelj. na primjer, ako je u pre_tasks web poslužitelj je instaliran i konfiguriran, i post_tasks nešto mu je poslano, a zatim prenesite ovaj zadatak u odjeljak pre_tasks će dovesti do činjenice da u trenutku "slanja" poslužitelj još neće biti pokrenut i sve će se pokvariti.

Sada razmislimo ponovno, zašto nam je potrebno pre_tasks и post_tasks? Na primjer, kako bi se dovršilo sve što je potrebno (uključujući rukovatelje) prije ispunjavanja uloge. A post_tasks omogućit će nam rad s rezultatima izvršavanja uloga (uključujući rukovatelje).

O čemu se radi reći će nam pronicljivi stručnjak za Ansible. meta: flush_handlers, ali zašto su nam potrebni flush_handleri ako se možemo osloniti na redoslijed izvođenja sekcija u igri? Štoviše, upotreba meta: flush_handlers može nam dati neočekivane stvari s dupliciranim rukovateljima, dajući nam čudna upozorenja kada se koristi when у block itd. Što bolje poznajete ansibl, to više nijansi možete navesti za "škakljivo" rješenje. A jednostavno rješenje - korištenje prirodne podjele između pred/uloge/post - ne uzrokuje nijanse.

I, vratimo se našem 'foo'. Gdje da ga stavim? U pre, post ili ulogama? Očito, to ovisi o tome trebaju li nam rezultati rukovatelja za foo. Ako ih nema, onda se foo ne mora stavljati ni u pre ni u post - ovi odjeljci imaju posebno značenje - izvršavanje zadataka prije i poslije glavnog dijela koda.

Sada se odgovor na pitanje "uloga ili zadatak" svodi na ono što je već u igri - ako tamo postoje zadaci, onda ih morate dodati zadacima. Ako postoje uloge, morate stvoriti ulogu (čak i iz jednog zadatka). Podsjećam vas da se zadaci i uloge ne koriste istovremeno.

Razumijevanje osnova Ansiblea pruža razumne odgovore na naizgled pitanja ukusa.

Zadaci i uloge (drugi dio)

Razmotrimo sada situaciju kada tek počinjete pisati knjigu igrokaza. Morate napraviti foo, bar i baz. Jesu li to tri zadatka, jedna uloga ili tri uloge? Da sažmemo pitanje: u kojem trenutku trebate početi pisati uloge? Koja je svrha pisanja uloga kad možete pisati zadatke?... Što je uloga?

Jedna od najvećih pogrešaka (o tome sam već govorio) jest misliti da je uloga poput funkcije u knjižnici programa. Kako izgleda generički opis funkcije? Prihvaća ulazne argumente, stupa u interakciju sa sporednim uzrocima, radi sporedne učinke i vraća vrijednost.

Sada, pozor. Što se od ovoga može napraviti u ulozi? Uvijek ste dobrodošli da pozovete nuspojave, to je bit cijelog Ansiblea - stvoriti nuspojave. Imate li nuspojave? Osnovno. Ali s "prenesi vrijednost i vrati je" - tu ne radi. Prvo, ne možete proslijediti vrijednost ulozi. Možete postaviti globalnu varijablu s doživotnom veličinom igre u odjeljku vars za ulogu. Možete postaviti globalnu varijablu s cijelim vijekom trajanja unutar uloge. Ili čak s životnim vijekom igranih knjiga (set_fact/register). Ali ne možete imati "lokalne varijable". Ne možete "uzeti vrijednost" i "vratiti je".

Glavna stvar slijedi iz ovoga: ne možete napisati nešto u Ansibleu, a da ne izazovete nuspojave. Promjena globalnih varijabli uvijek je nuspojava za funkciju. U Rustu je, na primjer, promjena globalne varijable unsafe. A u Ansibleu to je jedina metoda za utjecaj na vrijednosti za ulogu. Obratite pažnju na korištene riječi: ne "proslijedite vrijednost ulozi", već "promijenite vrijednosti koje uloga koristi". Nema izolacije između uloga. Ne postoji izolacija između zadataka i uloga.

Ukupno: uloga nije funkcija.

Što je dobro u ulozi? Prvo, uloga ima zadane vrijednosti (/default/main.yaml), drugo, uloga ima dodatne direktorije za pohranu datoteka.

Koje su prednosti zadanih vrijednosti? Budući da su u Maslowljevoj piramidi, Ansibleovoj prilično izopačenoj tablici varijabilnih prioriteta, zadane uloge najnižeg prioriteta (minus Ansible parametri naredbenog retka). To znači da ako trebate dati zadane vrijednosti i ne brinuti se o tome da će one nadjačati vrijednosti iz inventara ili grupnih varijabli, tada su zadane uloge jedino pravo mjesto za vas. (Malo lažem - ima ih još |d(your_default_here), ali ako govorimo o stacionarnim mjestima, onda samo zadane uloge).

Što je još sjajno u ulogama? Jer oni imaju svoje kataloge. Ovo su direktoriji za varijable, i konstantne (tj. izračunate za ulogu) i dinamičke (postoji ili obrazac ili anti-uzorak - include_vars zajedno s {{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml.). Ovo su imenici za files/, templates/. Također, omogućuje vam da imate vlastite module i dodatke (library/). No, u usporedbi sa zadacima u knjižici (koja također može imati sve to), ovdje je jedina prednost što se datoteke ne bacaju na jednu hrpu, već u nekoliko zasebnih hrpa.

Još jedan detalj: možete pokušati stvoriti uloge koje će biti dostupne za ponovno korištenje (putem galaksije). S pojavom zbirki, raspodjela uloga može se smatrati gotovo zaboravljenom.

Stoga uloge imaju dvije važne značajke: imaju zadane vrijednosti (jedinstvena značajka) i omogućuju vam strukturiranje koda.

Da se vratimo na izvorno pitanje: kada raditi zadatke, a kada uloge? Zadaci u priručniku najčešće se koriste ili kao “ljepilo” prije/poslije uloga ili kao samostalni građevni element (tada u kodu ne bi trebalo biti uloga). Gomila normalnih zadataka pomiješanih s ulogama nedvosmislena je aljkavost. Trebali biste se pridržavati određenog stila - bilo zadatka ili uloge. Uloge omogućuju odvajanje entiteta i zadanih postavki, zadaci vam omogućuju brže čitanje koda. Obično se više "stacionarnog" (važnog i složenog) koda stavlja u uloge, a pomoćne skripte se pišu u stilu zadatka.

Moguće je napraviti import_role kao zadatak, ali ako ovo napišete, budite spremni objasniti vlastitom osjećaju za ljepotu zašto to želite učiniti.

Pronicljivi čitatelj može reći da uloge mogu uvoziti uloge, uloge mogu imati ovisnosti putem galaxy.yml, a postoji i strašno i strašno include_role — Podsjećam vas da usavršavamo vještine u osnovnom Ansibleu, a ne u figurskoj gimnastici.

Rukovatelji i zadaci

Raspravljajmo o još jednoj očitoj stvari: rukovateljima. Znati ih ispravno koristiti gotovo je umjetnost. Koja je razlika između rukovatelja i draga?

Budući da se sjećamo osnova, evo primjera:

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

Rukovatelji uloge nalaze se u rolename/handlers/main.yaml. Rukovatelji preturaju između svih sudionika igre: prije/poslije_zadataka mogu povući rukovatelje ulogama, a uloga može povući rukovatelje iz igre. Međutim, "cross-role" pozivi rukovateljima uzrokuju mnogo više problema nego ponavljanje trivijalnog rukovatelja. (Još jedan element najbolje prakse je pokušaj ne ponavljanja imena rukovatelja).

Glavna razlika je u tome što se zadatak uvijek izvršava (idempotentno) (plus/minus oznake i when), a rukovatelj - promjenom stanja (obavijest se pali samo ako je promijenjeno). Što to znači? Na primjer, činjenica da kada ponovno pokrenete, ako nije bilo promjena, tada neće biti rukovatelja. Zašto možda moramo izvršiti rukovatelj kada nije bilo promjena u zadatku generiranja? Na primjer, jer se nešto pokvarilo i promijenilo, ali izvršenje nije stiglo do rukovatelja. Na primjer, jer je mreža privremeno bila u prekidu. Konfiguracija je promijenjena, usluga nije ponovno pokrenuta. Sljedeći put kada ga pokrenete, konfiguracija se više ne mijenja, a usluga ostaje sa starom verzijom konfiguracije.

Situacija s konfiguracijom se ne može riješiti (točnije, možete izmisliti poseban protokol ponovnog pokretanja za sebe s oznakama datoteka itd., ali to više nije 'osnovno antabilno' ni u kojem obliku). Ali postoji još jedna česta priča: instalirali smo aplikaciju, snimili je .service-file, a sada ga želimo daemon_reload и state=started. Čini se da je prirodno mjesto za to rukovatelj. Ali ako ga ne učinite rukovateljem nego zadatkom na kraju popisa zadataka ili uloge, tada će se svaki put izvršiti idempotentno. Pa makar se playbook slomio na sredini. Ovo uopće ne rješava problem ponovnog pokretanja (ne možete izvršiti zadatak s atributom ponovno pokrenutog, jer se gubi idempotencija), ali svakako vrijedi učiniti stanje = započeto, ukupna stabilnost playbooks se povećava, jer smanjuje se broj veza i dinamičko stanje.

Još jedno pozitivno svojstvo rukovatelja je da ne začepljuje izlaz. Nije bilo promjena - nema dodatnih preskočenih ili ok u ispisu - lakše za čitanje. To je također negativno svojstvo - ako pronađete grešku pri upisu u linearno izvršenom zadatku pri prvom pokretanju, tada će se rukovatelji izvršiti samo kada se promijene, tj. pod nekim uvjetima - vrlo rijetko. Na primjer, prvi put u životu pet godina kasnije. I, naravno, bit će greška u nazivu i sve će se pokvariti. I ako ih ne pokrenete drugi put, nema promjene.

Zasebno, moramo razgovarati o dostupnosti varijabli. Na primjer, ako obavijestite zadatak s petljom, što će biti u varijablama? Možete pogađati analitički, ali to nije uvijek trivijalno, pogotovo ako varijable dolaze s različitih mjesta.

... Dakle rukovatelji su mnogo manje korisni i mnogo problematičniji nego što se čine. Ako možete napisati nešto lijepo (bez ukrasa) bez rukovatelja, bolje je to učiniti bez njih. Ako ne ispadne lijepo, bolje je s njima.

Zajedljivi čitatelj s pravom ističe da nismo raspravljali listenda rukovatelj može pozvati notify za drugog rukovatelja, da rukovatelj može uključiti import_tasks (koji može učiniti include_role s with_items), da je sustav rukovatelja u Ansibleu Turing-kompletan, da se rukovatelji iz include_role križaju na čudan način s rukovateljima iz igre, itd. .d. - sve ovo očito nije "osnovno").

Iako postoji jedan specifičan WTF koji je zapravo značajka koju morate imati na umu. Ako je vaš zadatak izvršen sa delegate_to i ima notify, tada se odgovarajući rukovatelj izvršava bez delegate_to, tj. na hostu kojem je igra dodijeljena. (Iako rukovatelj, naravno, može imati delegate_to Isti).

Zasebno želim reći nekoliko riječi o višekratnim ulogama. Prije nego što su se pojavile kolekcije, postojala je ideja da možete napraviti univerzalne uloge koje bi mogle biti ansible-galaxy install i otišao. Radi na svim OS-ima svih varijanti u svim situacijama. Dakle, moje mišljenje: ne radi. Svaka uloga s masom include_vars, koji podržava 100500 slučajeva, osuđen je na ponor grešaka u kutnim slučajevima. Mogu se pokriti masivnim testiranjem, ali kao i kod svakog drugog testiranja, ili imate kartezijanski umnožak ulaznih vrijednosti i ukupne funkcije ili imate "pokrivene pojedinačne scenarije". Moje mišljenje je da je puno bolje ako je uloga linearna (ciklomatska složenost 1).

Što manje if-ova (eksplicitnih ili deklarativnih - u obliku when odnosno obliku include_vars prema skupu varijabli), to je uloga bolja. Ponekad morate napraviti grane, ali, ponavljam, što ih je manje, to bolje. Pa se čini kao dobra uloga s galaksijom (radi!) s hrpom when može biti manje poželjna od "vlastite" uloge iz pet zadataka. Trenutak kada je uloga s galaxyjem bolja je kada počnete nešto pisati. Trenutak kad postane gore je kad se nešto pokvari i posumnjate da je to zbog “uloge s galaksijom”. Otvorite ga, a tu je pet priloga, osam listova sa zadacima i hrpa when'ov... I ovo moramo shvatiti. Umjesto 5 zadataka, linearna lista u kojoj se nema što pokvariti.

U sljedećim dijelovima

  • Malo o inventaru, grupnim varijablama, host_group_vars dodatku, hostvars. Kako špagetima vezati Gordijev čvor. Varijable opsega i prioriteta, Ansible memorijski model. "Dakle, gdje ćemo pohraniti korisničko ime za bazu podataka?"
  • jinja: {{ jinja }} — nosql notype nosense meki plastelin. Ima ga posvuda, čak i tamo gdje ga ne očekujete. Malo o !!unsafe i ukusna jamla.

Izvor: www.habr.com

Dodajte komentar