Ansible'i põhialused, mille puudumisel on teie playbookid — ühes tükis kokku kleepunud makaronid

Ma teen palju ülevaateid teiste Ansible'i koodide kohta ja kirjutan palju ise. Vigade analüüsi käigus (nii oma kui ka teiste) ning mitmete intervjuude tõttu aru sain, et peamine viga, mida Ansible'i kasutajad teevad, on see, et nad sukeldusid keerulistesse asjadesse, ilma et oleks esmalt alustanud põhialustest.

Selle universaalse ebaõigluse parandamiseks otsustasin kirjutada Ansible'i sissejuhatuse neile, kes seda juba tunnevad. Hoian teid kursis, see ei ole lihtsalt manuaali ümberjutustamine, vaid pika formaadi kirjutamine, kus on palju teksti ja ei ole pilte.

Oodatav lugejatase — on kirjutatud juba mitu tuhat rida YAML-i, on tehtud midagi tootmises, kuid "kõik on kuidagi viltu".

Nimed

Ansible'i kasutaja peamine viga on mitte teada, kuidas asju nimetatakse. Kui te ei tea nimetusi, ei saa te aru dokumentatsioonist. Elav näide: intervjuus ütles inimene, kes väitis, et on palju Ansible'iga töötanud, ei suutnud vastata küsimusele "millistest elementidest playbook koosneb?". Kui ma ütlesin, et "oodati vastust, et playbook koosneb play'ist", siis järgnes hirmus kommentaar "me ei kasuta seda". Inimesed kirjutavad Ansible'iga raha eest ja ei kasuta play'd. Tegelikult nad kasutavad, kuid ei tea, mis see on.

Alustame seega lihtsast: kuidas asju nimetatakse. Võib-olla teate seda, aga võib-olla ei tea, sest ei pööranud tähelepanu, kui lugesite dokumentatsiooni.

ansible-playbook täidab playbook'i. Playbook on fail, millel on laiendus yml/yaml, mille sees on midagi sellist:

---
- hosts: group1
  roles:
    - role1

- hosts: group2,group3
  tasks:
    - debug:

Me oleme juba aru saanud, et kogu see fail on playbook. Saame näidata, kus on rollid (roles) ja kus on ülesanded (tasks). Kuid kus on siin play? Ja kuidas erineb play rollist või playbook'ist?

Dokumentatsioonis on kõik olemas. Ja seda jäetakse tähelepanuta. Algajad — sest seal on liiga palju ja kõik korraga ei jää meelde. Kogenud — sest "ilmsed asjad". Kui olete kogenud — loe need lehed vähemalt kord kuue kuu jooksul ja teie kood muutub palju paremaks.

Nii et pidage meeles: Playbook on nimekiri, mis koosneb play'st ja import_playbook.
See on üks play:

- hosts: group1
  roles:
    - role1

Ja see on veel üks play:

- hosts: group2,group3
  tasks:
    - debug:

Mis on siis play? Miks see on vajalik?

Play on playbook'i põhielement, sest play ja ainult play sidub nimekirja rollidest ja/või ülesannetest nimekirjaga hostidest, kus neid täita tuleb. Dokumentatsiooni sügavas osas võib leida viite delegate_to, kohalikud lookup-pluginid, network-cli-spetsiifilised seaded, jump-hostid jne. Need võimaldavad veidi muuta ülesannete täitmise kohta. Aga unustage see. Igal neist kavalatest valikutest on väga spetsiifilised rakendused ja nad ei ole kindlasti universaalsed. Ja räägime põhilistest asjadest, mida kõik peaksid teadma ja kasutama.

Kui soovite "midagi" täita "kuskil" — kirjutate play. Mitte roll. Mitte roll koos moodulite ja delegeerijatega. Te lihtsalt kirjutate play. Milles, väljas hosts, loetlete, kus täita, ja roles/tasks — mida täita.

Lihtne, eks? Kuidas muidu oleks?

Üks iseloomulik hetk, kui inimesi hakkab tõukama tegema seda mitte läbi play, on "roll, mis kõik seadistab". Sooviks olla roll, mis seadistab ja serverilt esmase tüübi ja teise tüübi serverid.

Arhetüüpiline näide on jälgimine. Sooviks olla roll monitoring, mis seadistab jälgimise. Roll monitoring määratakse jälgimishostidele (vastavas play-s). Kuid selgub, et jälgimiseks peame paigaldama paketid hostidesse, mida me jälgime. Miks mitte kasutada delegate'i? Ja veel tuleb seadistada iptables. delegate? Ja veel tuleb kirjutada/korrigeerida andmebaasi seadistust, et jälgimine oleks lubatud. delegate! Ja kui loominguline inspiratsioon tuleb, siis saab teha delegeerimise include_role ka sisemise tsükli keerulise filtri kaudu gruppide nimekirja, ja sees include_role saab veel teha delegate_to uuesti. Ja edasi läheb...

Hea soov — omada ühte ainukest rolli monitoring, mis "teeb kõik" — viib meid põrgusse, millest kõige sagedamini on ainult üks väljapääs: kirjutada kõik nullist ümber.

Kus siin juhtus viga? Hetkel, kui avastasite, et ülesande "x" täitmiseks hostis X peate minema hosti Y ja tegema seal "y", oleksite pidanud tegema lihtsa harjutuse: minema ja kirjutama play, mis hostis Y teeb y. Mitte lisama midagi "x" sisse, vaid kirjutama nullist. Isegi kõvakooditud muutujatega.

Tundub, et ülaltoodud lõikudes on kõik õige. Aga see ei ole teie juhtum! Sest te soovite kirjutada taaskasutatavat koodi, mis on DRY ja sarnaneb raamatukoguga, ja tuleb otsida viis, kuidas seda teha.

Siin peitub veel üks tõsine viga. Viga, mis muutis hulga projekte talutavast (saab paremini, aga kõik töötab ja on lihtne täiendada) täielikuks õuduseks, milles isegi autor ei saa aru. See töötab, aga jumal hoidku, kui midagi muuta.

See viga kõlab nii: roll on raamatukogu funktsioon. See analoogia on hävitanud nii palju häid algatusi, et on lihtsalt kurb vaadata. Roll ei ole raamatukogu funktsioon. Ta ei saa teha arvutusi ja ta ei saa langetada otsuseid tasemel play. Tuletage mulle meelde, milliseid otsuseid teeb play?

Aitäh, sa oled õigus. Play teeb otsuseid (täpsemalt, sisaldab teavet) selle kohta, milliseid ülesandeid ja rolle, millistel hostidel täita.

Kui te delegeerite selle otsuse rollile, veelgi enam arvutustega, olete teinud endale (ja sellele, kes teie koodi püüab lahti harutada) õnnetu eksistentsi. Roll ei otsusta, kus ta täidetakse. Selle otsuse teeb play. Roll teeb seda, mida talle öeldi, seal, kus talle öeldi.

Miks on Ansible'i programmeerimisega tegelemine ohtlik ja miks on COBOL parem kui Ansible, räägime muutujaid ja jinja käsitlevas peatükis. Siiski ütleme ühe asja — iga teie arvutus jätab endast maha kustutamatud jäljed globaalsete muutujate muutmisest ja te ei saa sellega midagi ette võtta. Kui kaks "jälge" kokku põrkuvad — on kõik kadunud.

Tähelepanek neile, kes peavad kõike täpselt: roll võib kindlasti mõjutada juhtimisvoogu. On olemas delegate_to ja sellel on mõistlikud rakendused. On meta: end host/play. Aga! Pea meeles, et me õpetame aluseid? Unustasite delegate_to. Räägime kõige lihtsamatest ja ilusamatest koodidest Ansible’is. Mis on kergesti loetavad, kergesti kirjutatavad, kergesti silutavad, kergesti testitavad ja kergesti täiendatavad. Nii et, veel kord:

play ja ainult play otsustab, millistel hostidel mida teostatakse.

Selles osas oleme arutanud play ja rolle. Nüüd räägime ülesannetest vs rollist.

Ülesanded ja Rollid

Vaadakem playd:

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

Oletame, et peate tegema foo. Ja see näeb välja nagu foo: name=foobar state=present. Kuhu see kirjutada? pre? post? Luua roll?

… Ja kuhu kadusid ülesanded?

Alustame taas alustest — play struktuurist. Kui te selles küsimuses ujujate, ei saa te kasutada play't aluseks kõikidele muudele osadele ja teie tulemus muutub "ebastabiilseks".

Play-seade: hosts-diktriiv, play-i seadistused ning pre_tasks, tasks, roles, post_tasks jaotused. Teised parameetrid play jaoks ei ole praegu olulised.

Nende sektsioonide järjekord ülesannete ja rollide jaoks: pre_tasks, roles, tasks, post_tasks. Kuna semantiliselt ei ole täitmise järjekord selge, siis parimad praktikad ütlevad, et lisame sektsiooni tasks ja roles , ainult juhul, kui pole tasks. Kui on olemas roles, siis kõik vastavad ülesanded paigutatakse sektsioonidesse. rolesJääb ainult see, mis semantiliselt on selge: kõigepealt pre_tasks/post_tasks.

Kuid me ei ole ikka veel vastanud küsimusele: kuhu kirjutada moduli pre_tasks, siis roles, siis post_tasks.

kutsumine? Kas peame iga mooduli jaoks kirjutama kogu rolli? Või on parem omada paksu rolli kõike jaoks? Ja kui ei ole rolli, siis kuhu kirjutada — pre-sse või post-sse? foo Kui nendele küsimustele ei ole põhjendatud vastust, siis see on märk intuitsiooni puudumisest, ehk need "ebakindlad alused". Alustame kontrollküsimusega: Kui play-l on

(ja ei ole ei ülesandeid ega rolle), siis kas midagi võib katki minna, kui ma esimese ülesande pre_tasks ja post_tasks viin lõpupoole? post_tasks Muidugi, küsimuse sõnastus viitab, et midagi läheb katki. Aga mis täpselt? pre_tasks?

… Käitlejad. Põhjaliku lugemise avab olulise fakti: kõik käitlejad tühjenevad automaatselt pärast iga sektsiooni. See tähendab, et kõigepealt täidetakse kõik ülesanded

, siis kõik käitlejad, mida on teatatud. Siis täidetakse kõik rollid ja kõik käitlejad, mida on teatatud rollides. Seejärel pre_tasksja nende käitlejad. post_tasks Seega, kui te tõstate ülesande

üles, siis potentsiaalselt täidate selle enne käitleja täitmist. Näiteks, kui post_tasks ja pre_taskspaigaldatakse ja konfigureeritakse pre_tasks , ning doole midagi saadetakse, siis selle ülesande viimine sektsiooni veebiserverikstoob kaasa selle, et "saadetamise" hetkel server ei ole veel käivitatud ja kõik läheb katki. post_tasks Ja nüüd mõelgem veel kord, miks on meil pre_tasks kui võimaldab töötada rollide täitmise tulemustega (sealhulgas käitlejatega).

Peen Ansible'i ekspert ütleb meile, et on olemas pre_tasks ja post_tasks? Например, для того, чтобы выполнить всё нужное (включая хэндлеры) до выполнения роли. А post_tasks meta: flush_handlers

, aga miks on meil flush_handlers, kui saame toetuda sektsioonide täitmise järjekorrale play-s? Veelgi enam, meta: flush_handlers'i kasutamine võib tuua meile ootamatuid korduvaid käitlejaid, teha meile kummalisi hoiatuse juhtudel, kui kasutame whenblock jne. Mida rohkem te Ansible'i tunnete, seda rohkem nüansse saate öelda "kavalate" lahenduste kohta. Ja lihtne lahendus — loomulik jaotus pre/roles/post vahel — ei tekita nüansse. on plokk ja nii edasi. Mida paremini te Ansible'it tunnete, seda rohkem nüansse suudate nimetada "nipp" lahenduse jaoks. Ja lihtne lahendus — kasutada loomulikku eraldust pre/roles/post — ei tekita nüansse.

Ja naaseame meie ‘foo’ juurde. Kuhu see paigutada? Pre, post või rollidesse? Ilmselgelt sõltub see sellest, kas meil on vaja käsitleja tulemusi foo jaoks. Kui neid pole, siis foo ei pea olema ei pre-s ega post-s — need sektsioonid on erilise tähendusega — ülesannete täitmine enne ja pärast peamist koodimassi.

Nüüd on vastus küsimusele "roll või ülesanne" seotud juba olemasolevaga mängus — kui seal on ülesandeid, siis tuleb need lisada ülesannetesse. Kui on rollid, siis tuleb luua roll (isegi kui see koosneb ainult ühest ülesandest). Tuletan meelde, et ülesanded ja rollid ei kasutata samal ajal.

Ansible’i põhialuste mõistmine annab põhjendatud vastused, näiliselt maitselikeks küsimusteks.

Ülesanded ja rollid (teine osa)

Nüüd arutame olukorda, kus hakkate alles mängu looma. Te peate tegema foo, bar ja baz. Need on kolm ülesannet, üks roll või kolm rolli? Üldistades küsimust: millal peaks hakata rolle kirjutama? Mis mõte on rollide kirjutamisel, kui saab kirjutada ülesandeid?… Ja mis on roll?

Üks suurimaid vigu (mille kohta ma juba rääkisin) — arvata, et roll on nagu funktsioon programmi raamatukogus. Kuidas näeb välja funktsiooni üldine kirjeldus? See võtab argumente sisendina, suhtleb kõrvalmõjudega, tekitab kõrvalmõjusid ja tagastab väärtuse.

Nüüd, tähelepanu. Mis sellest saab teha rollis? Kõrvalmõjude kutsumine — alati, palun, see ongi kogu Ansible’i olemus — tekitada kõrvalmõjusid. Omada kõrvalmõjusid? Elementaarne. Aga väljendi "edastada väärtus ja tagastada see" — see on koht, kus pole. Esiteks, te ei saa väärtust rolli edastada. Te saate seada globaalmuutuja elutsükliga, mis vastab mängu sektsioonile vars rollis. Te saate seada globaalmuutuja elutsükliga mängu sees rollis. Või isegi mängu plaanis.set_fact/register). Aga te ei saa omada "kohalikku muutujat". Te ei saa "vastu võtta väärtust" ja "tagastada seda".

Sellest tuleneb peamine asi: Ansible’is ei saa kirjutada midagi, mis ei tekitaks kõrvalmõjusid. Globaalsete muutujate muutmine on alati kõrvalmõju funktsioonile. Näiteks Rustis on globaalse muutuja muutmine ohtlik. Ansible’is on see ainus viis mõjutada rolle. Pange tähele soovitud sõnu: mitte "edastada väärtus rolli", vaid "muuta väärtusi, mida roll kasutab". Rollide vahel ei ole isoleerimist. Ülesannete ja rollide vahel ei ole isoleerimist.

Kokkuvõtteks: roll on see ei ole funktsioon.

Mis head on rollis? Esiteks on rollil default values (/default/main.yaml), teiseks on rollil täiendavad kataloogid failide säilitamiseks.

Miks on default values head? Sest Maslow püramiidis on Ansible'i muutuja prioriteetide tabelis rolli defaults – kõige vähem prioriteetsed (välja arvatud Ansible'i käsurea parameetrid). See tähendab, et kui peate esitama vaikeväärtused ja mitte muretsema, et need ületavad inventaarist või grupimuutujatest tulenevad väärtused, siis rollide vaikeseaded – on see ainus õige koht. (Ma natuke vale räägin – on veel |d(your_default_here), aga kui rääkida staatilistest kohtadest – siis ainult rollide default values).

Mis veel on rollides head? Sellega, et neil on oma kataloogid. Need on kataloogid muutujatele, nii püsivatele (st. arvutatud rolli jaoks) kui ka dünaamilistele (on selline musternäide või -anti-muster – include_vars koos {{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml.). Need on kataloogid files/, templates/. Veelgi enam, see võimaldab rollidel omada oma mooduleid ja pluginaid (library/). Kuid võrreldes ülesannetega playbook'is (kus kõik see võib samuti olla), on kasu siin ainult selles, et failid ei ole kuhjunud ühte hunnikusse, vaid mitmesse eraldi hunnikusse.

Veel üks detail: saab proovida teha rolle, mis oleksid taaskasutatavad (galaxy kaudu). Pärast kogude ilmumist võib rollide levikut peaaegu unustatud pidada.

Seega on rollidel kaks olulist omadust: neil on defaultid (ainulaadne omadus) ja nad võimaldavad kode struktuuri.

Tagasi algse küsimuse juurde: millal teha ülesandeid ja millal rolle? Ülesandeid kasutatakse playbook'is kõige sagedamini kas "liimiks" enne/peale rolle või iseseisvate ehituselementidena (siis ei tohiks koodis olla rolle). Tavaliste ülesannete hunnik segatuna rollidega – see on kindlasti hooletus. Tuleks järgida konkreetset stiili – kas ülesanded või rollid. Rollid annavad olemuste ja defaultide jaotuse, ülesanded võimaldavad koodi kiiremini lugeda. Tavaliselt viiakse rollidesse rohkem "staatilist" (olulist ja keerulist) koodi, samas kui ülesannete stiilis kirjutatakse abiskripte.

On olemas võimalus import_role'i teha ülesandena, kuid kui te seda kirjutate, olge valmis selgitama, miks te seda teha soovite.

Tüütud lugejad võivad öelda, et rollid saavad importida rolle, rollidel võib olla sõltuvus galaxy.yml-st, ja lisaks on veel hirmus ja kohutav include_role — meenutan, et me arendame oskusi põhilises Ansible'is, mitte iluuisutamises.

Hüüdurid ja ülesanded

Räägime veel ühest iseenesestmõistetavast asjast: hüüdurid. Nende õigesti kasutamine on pea nagu kunst. Mis vahe on hüüduri ja ülesande vahel?

Kuna meenutame põhialuseid, siis siin on näide:

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

Roli hüüdurid asuvad rolinimes/handlers/main.yaml. Hüüdurid jagunevad kõigi mängijate vahel: pre/post_tasks võivad kutsuda rolli hüüüre, ja roll võib kutsuda hüüüre mängust. Kuid „ristsõnades“ hüüdurite kutsumise taga on palju suurem wtf, kui triviaalne hüüdur. (Veel üks parimate praktikate element on vältida hüüduri nimede kordamist).

Põhiline erinevus on see, et ülesanne tehakse (idempotentselt) alati (pluss/muus oktoober ja jne. Mida rohkem te Ansible'i tunnete, seda rohkem nüansse saate öelda "kavalate" lahenduste kohta. Ja lihtne lahendus — loomulik jaotus pre/roles/post vahel — ei tekita nüansse.), hüüdur aga muutub olekuks (notify töötab ainult siis, kui on toimunud muutus). Mida see endaga kaasa toob? Näiteks seda, et kui käivitamine toimub uuesti ja muutusi pole olnud, ei toimu ka hüüdurit. Kuid miks võib olla nii, et meil on vaja hüüdurit käivitada, kui algses ülesandes muutusi pole olnud? Näiteks seetõttu, et midagi on katki ja muutus oli olemas, aga enne hüüduri täitmist ei jõutud. Näiteks seetõttu, et võrk oli ajutiselt maas. Konfiguratsioon muutus, teenust ei taaskäivitatud. Järgmise käivitamise korral konfiguratsioon enam ei muutu ja teenus jääb vana konfiguratsiooni versiooniga.

Konfiguratsiooniga seonduv olukord ei ole lahendatav (täpsemalt, võite ise välja mõelda spetsiaalse taaskäivitamise protokolli faililippudega jne, aga see ei ole enam 'põhianne', mitte mingil kujul). Kuid teine sage lugu on see: me installisime rakenduse, salvestasime selle .service-faili ja nüüd soovime, et see daemon_reload ja state=started. Ja loomulik koht selle jaoks näib olevat käsitleja. Kuid kui muuta see mitte käsitlejaks, vaid ülesandeks lõpus ülesannete loendis või rolliks, siis see tehakse idempotentselt igal korral. Isegi kui mänguraamat purunes keset. See ei lahenda probleemi, kui taaskäivitamine (ülesannet ei saa teha taaskäitusatribuudiga, kuna idempotentsus kaob), kuid kindlasti tasub teha state=started, kuna üldine mänguraamatute stabiilsus suureneb, vähendades seoseid ja dünaamilist seisundit.

Veel üks positiivne omadus käsitlejast on see, et see ei ummistu väljundiga. Muudatusi ei olnud - ei ole üleliigseid skipped või ok väljendites - seda on lihtsam lugeda. Samuti on see negatiivne omadus - kui leiate trükivea lineaarset täitmist ülesandes esimesel läbilõikel, siis käsitlejad täidetakse ainult измененных korral - ehk mõnel tingimusel - väga harva. Näiteks esmakordselt oma elus pärast viit aastat. Ja muidugi on seal nime trükiviga ning kõik puruneb. Teisel korral ei saa neid käivitada - muutusi pole.

Eraldi tuleb rääkida muutujate kättesaadavusest. Näiteks, kui te teavitada ülesande jaoks, millel on tsükkel, siis mis juhtub muutujatega? Saate seda analüütiliselt välja mõelda, kuid see ei ole alati triviaalne, eriti kui muutujad tulevad erinevatest kohtadest.

… Nii et käsitlejad on palju vähem kasulikud ja palju problemaatilisemad, kui tundub. Kui midagi saab kenasti (ilma keerutamata) kirjutada ilma käsitlejateta, siis on parem teha ilma nendeta. Kui ilus ei õnnestu - siis on parem koos nendega.

Peenekoeline lugeja õigustatult märgib, et me ei arutanud kuulamine, et käsitleja võib kutsuda teisi käsitlejaid teavitama, et käsitleja võib sisaldada import_tasks (mis võib teha include_role c with_items), et käsitlejate süsteem Ansible'is on Turingi täielik, et include_role'i käsitlejad põnevalt ristuvad mängu käsitlejatega jne - see kõik ei ole kindlasti "põhitõed".

Kuigi on üks kindel WTF, mis tegelikult on funktsioon ja mida tuleb meeles pidada. Kui teil on ülesanne, mis täidetakse delegate_to ja sellel on teavitamine, siis vastav käsitleja täidetakse ilma delegate_to, st masinas, kus mäng on määratud. (Kuigi käsitlejal võib muidugi olla delegate_to samuti).

Eraldi tahan öelda paar sõna korduvkasutatavatest rollidest. Enne kollektsioonide ilmumist oli idee, et võiks luua universaalseid rolle, mida saab ansible-galaxy install ja läks. Töötab kõikidel OS-idel, kõikides variatsioonides kõigis olukordades. Nii et minu arvamus: see ei tööta. Iga roll massilise include_vars, mis toetab 100500 juhtumit, on hukule määratud nurga juhtumite vigade tõttu. Neid saab katta massilise testimisega, kuid nagu iga testimise puhul, kas teil on dekartaadi produkti sisendväärtused ja totaalfunktsioon, või katta teatud stsenaariumid. Minu arvamus on, et parem on, kui roll on lineaarne (tsiklomaatiline keerukus 1).

Mida vähem if-e (olgu need siis väljendatud või deklaratiivsed — vormis jne. Mida rohkem te Ansible'i tunnete, seda rohkem nüansse saate öelda "kavalate" lahenduste kohta. Ja lihtne lahendus — loomulik jaotus pre/roles/post vahel — ei tekita nüansse. või vormis include_vars sisendite komplektis), seda parem on roll. Mõnikord tuleb teha hargnemisi, kuid, kordame, mida vähem neid, seda parem. Nii et kuigi tundub, et hea roll galaktika (see töötab ju!) koos hulga jne. Mida rohkem te Ansible'i tunnete, seda rohkem nüansse saate öelda "kavalate" lahenduste kohta. Ja lihtne lahendus — loomulik jaotus pre/roles/post vahel — ei tekita nüansse. võib olla vähem eelistatud kui „oma” roll viiest ülesandest. Moment, mil roll galaktikaga on parem — kui hakkate midagi kirjutama. Moment, mil see halveneb — kui midagi läheb katki ja kahtlustate, et see on „galaktika rolli” tõttu. Avate selle ja seal on viis inkluusi, kaheksa ülesande nimekirja ja virna jne. Mida rohkem te Ansible'i tunnete, seda rohkem nüansse saate öelda "kavalate" lahenduste kohta. Ja lihtne lahendus — loomulik jaotus pre/roles/post vahel — ei tekita nüansse.‘... Ja seda tuleb mõista. Viie ülesande lineaarne loend, kus pole midagi katki minemas.

Järgmistes osades

  • Veidi inventuuri, rühmaparameetreid, host_group_vars plugin, hostvars. Kuidas spagetti siduda Gordiumi sõlme. Muutujate ulatus ja eelisarv, Ansible'i mälumudel. "Kus siis ikkagi hoida andmebaasi kasutajanime?"
  • jinja: {{ jinja }} — nosql notype nosense pehme plastiliin. See on igal pool, isegi seal, kus te seda ei oota. Veidi !!unsafe ja maitsev yaml.

Allikas: habr.com

Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster