ProHoster > Blogi > antaminen > Mennä? Lyödä! Tapaa shell-operaattori (arvostelu ja videoraportti KubeCon EU'2020:sta)
Mennä? Lyödä! Tapaa shell-operaattori (arvostelu ja videoraportti KubeCon EU'2020:sta)
Tänä vuonna Euroopan pääkonferenssi Kubernetes - KubeCon + CloudNativeCon Europe 2020 - oli virtuaalinen. Tällainen muotomuutos ei kuitenkaan estänyt meitä toimittamasta pitkään suunniteltua raporttiamme "Go? Lyödä! Tapaa Shell-operaattori”, joka on omistettu Open Source -projektillemme kuorioperaattori.
Tämä keskustelun inspiroima artikkeli esittelee lähestymistavan operaattoreiden luomisprosessin yksinkertaistamiseen Kubernetesille ja näyttää, kuinka voit tehdä omasi mahdollisimman pienellä vaivalla shell-operaattorilla.
Esittelyssä video raportista (~23 minuuttia englanniksi, huomattavasti informatiivisempi kuin artikkeli) ja pääote siitä tekstimuodossa. Mennä!
Flantilla optimoimme ja automatisoimme kaiken jatkuvasti. Tänään puhumme toisesta jännittävästä konseptista. Tavata: pilvi-natiivi komentosarjakirjoitus!
Aloitetaan kuitenkin kontekstista, jossa tämä kaikki tapahtuu: Kubernetes.
Kubernetes API ja ohjaimet
Kubernetesin API voidaan esittää eräänlaisena tiedostopalvelimena, jossa on hakemistoja jokaiselle objektityypille. Tämän palvelimen objektit (resurssit) esitetään YAML-tiedostoina. Lisäksi palvelimessa on perussovellusliittymä, jonka avulla voit tehdä kolme asiaa:
saada resurssi lajinsa ja nimensä mukaan;
muuttaa resurssi (tässä tapauksessa palvelin tallentaa vain "oikeat" objektit - kaikki väärin muodostetut tai muihin hakemistoihin tarkoitetut hylätään);
seurata resurssille (tässä tapauksessa käyttäjä saa välittömästi sen nykyisen/päivitetyn version).
Siten Kubernetes toimii eräänlaisena tiedostopalvelimena (YAML-luetteloille) kolmella perusmenetelmällä (kyllä, itse asiassa on muitakin, mutta jätämme ne pois toistaiseksi).
Ongelmana on, että palvelin voi tallentaa vain tietoja. Jotta se toimisi, tarvitset ohjain - Kubernetesin maailman toiseksi tärkein ja perustavin konsepti.
Säätimiä on kahta päätyyppiä. Ensimmäinen ottaa tiedot Kubernetesista, käsittelee ne sisäkkäisen logiikan mukaisesti ja palauttaa ne K8s:lle. Toinen ottaa tietoja Kubernetesista, mutta toisin kuin ensimmäinen tyyppi muuttaa joidenkin ulkoisten resurssien tilaa.
Käyttöönoton ohjain (sisältyy kube-controller-manager) vastaanottaa tietoja käyttöönotosta ja luo ReplicaSetin.
ReplicaSet luo kaksi kopiota (kaksi podia) näiden tietojen perusteella, mutta näitä podeja ei ole vielä ajoitettu.
Ajastin ajoittaa podeja ja lisää solmutiedot niiden YAML:eihin.
Kubeletit tekevät muutoksia ulkoiseen resurssiin (kuten Docker).
Sitten tämä koko sarja toistetaan käänteisessä järjestyksessä: kubelet tarkistaa säiliöt, laskee podin tilan ja lähettää sen takaisin. ReplicaSet-ohjain vastaanottaa replikajoukon tilan ja päivittää sen tilan. Sama tapahtuu Deployment Controllerin kanssa ja käyttäjä saa vihdoin päivitetyn (nykyisen) tilan.
Shell-operaattori
Osoittautuu, että Kubernetes perustuu eri valvojien yhteistyöhön (Kubernetes-operaattorit ovat myös valvojia). Herää kysymys, kuinka luoda oma operaattori minimaalisella vaivalla? Ja tässä kehittämämme tulee apuun kuorioperaattori. Sen avulla järjestelmänvalvojat voivat luoda omia lausuntojaan tutuilla menetelmillä.
Yksinkertainen esimerkki: salaisuuksien kopioiminen
Katsotaanpa yksinkertaista esimerkkiä.
Oletetaan, että meillä on Kubernetes-klusteri. Siinä on nimiavaruus default jollain salaisuudella mysecret. Lisäksi klusterissa on muita nimiavaruuksia. Joihinkin niistä on kiinnitetty erityinen etiketti. Tavoitteenamme on kopioida Secret nimiavaruuksiin, joissa on tunniste.
Tehtävää vaikeuttaa se, että klusteriin saattaa ilmestyä uusia nimiavaruuksia, joista joissakin voi olla tämä nimike. Toisaalta, kun tarra poistetaan, myös Secret tulee poistaa. Tämän lisäksi itse salaisuus voi myös muuttua: tässä tapauksessa uusi Secret on kopioitava kaikkiin nimiavaroihin, joissa on tunnisteet. Jos Secret poistetaan vahingossa jostain nimiavaruudesta, operaattorimme tulee palauttaa se välittömästi.
Nyt kun tehtävä on muotoiltu, on aika aloittaa sen toteuttaminen shell-operaattorilla. Mutta ensin on syytä sanoa muutama sana itse shell-operaattorista.
Kuinka shell-operaattori toimii
Kuten muutkin Kubernetes-työkuormat, komentotulkkioperaattori toimii omassa kotelossaan. Tässä hakemistossa olevassa podissa /hooks suoritettavat tiedostot tallennetaan. Nämä voivat olla skriptejä Bashissa, Pythonissa, Rubyssa jne. Kutsumme tällaisia suoritettavia tiedostoja koukkuiksi (koukut).
Shell-operaattori tilaa Kubernetes-tapahtumat ja suorittaa nämä koukut vastauksena tarvitsemiimme tapahtumiin.
Mistä shell-operaattori tietää, mikä koukku ajaa ja milloin? Pointti on, että jokaisessa koukussa on kaksi vaihetta. Käynnistyksen aikana komentotulkkioperaattori suorittaa kaikki koukut argumentin avulla --config Tämä on konfigurointivaihe. Ja sen jälkeen koukut laukeavat normaalisti - vastauksena tapahtumiin, joihin ne on kiinnitetty. Jälkimmäisessä tapauksessa koukku vastaanottaa sitovan kontekstin (sitova konteksti) - JSON-muodossa olevat tiedot, joista puhumme yksityiskohtaisemmin alla.
Operaattorin tekeminen Bashissa
Nyt olemme valmiita toteutukseen. Tätä varten meidän on kirjoitettava kaksi funktiota (muuten suosittelemme kirjasto shell_lib, mikä yksinkertaistaa suuresti koukkujen kirjoittamista Bashissa):
ensimmäinen tarvitaan konfigurointivaiheessa - se näyttää sidoskontekstin;
toinen sisältää koukun päälogiikan.
#!/bin/bash
source /shell_lib.sh
function __config__() {
cat << EOF
configVersion: v1
# BINDING CONFIGURATION
EOF
}
function __main__() {
# THE LOGIC
}
hook::run "$@"
Seuraava askel on päättää, mitä esineitä tarvitsemme. Meidän tapauksessamme meidän on seurattava:
lähdesalaisuus muutoksia varten;
kaikki klusterin nimitilat, jotta tiedät, mihin niistä on liitetty tunniste;
kohdesalaisuuksia varmistaaksesi, että ne ovat synkronoitu lähdesalaisuuden kanssa.
Tilaa salainen lähde
Sen sidontakokoonpano on melko yksinkertainen. Ilmoitamme nimellä, että olemme kiinnostuneita Secretistä mysecret nimiavaruudessa default:
Kuten näet, kokoonpanoon on ilmestynyt uusi kenttä nimellä jqFilter. Kuten nimikin kertoo, jqFilter suodattaa pois kaikki tarpeettomat tiedot ja luo uuden JSON-objektin meitä kiinnostavilla kentillä. Koukku, jolla on samanlainen kokoonpano, saa seuraavan sidoskontekstin:
Se sisältää taulukon filterResults jokaiselle klusterin nimiavaruudelle. Boolen muuttuja hasLabel ilmaisee, onko tiettyyn nimiavaruuteen liitetty tarra. Valitsin keepFullObjectsInMemory: false ilmaisee, että täydellisiä objekteja ei tarvitse säilyttää muistissa.
Kohteen salaisuudet
Tilaamme kaikki salaisuudet, joihin on määritetty huomautus managed-secret: "yes" (nämä ovat tavoitteemme dst_secrets):
Tässä tapauksessa jqFilter suodattaa pois kaikki tiedot paitsi nimitilan ja parametrin resourceVersion. Viimeinen parametri välitettiin annotaatiolle salaisuutta luotaessa: sen avulla voit verrata salaisuuksien versioita ja pitää ne ajan tasalla.
Tällä tavalla määritetty koukku vastaanottaa suoritettuaan kolme yllä kuvattua sidoskontekstia. Niitä voidaan pitää eräänlaisena tilannekuvana (kuva) -klusteri.
Kaiken tämän tiedon perusteella voidaan kehittää perusalgoritmi. Se toistuu kaikissa nimiavaruuksissa ja:
jos hasLabel asioissa true nykyiselle nimiavaruudelle:
vertaa globaalia salaisuutta paikalliseen:
jos ne ovat samat, se ei tee mitään;
jos ne eroavat - suorittaa kubectl replace tai create;
jos hasLabel asioissa false nykyiselle nimiavaruudelle:
varmistaa, että Secret ei ole annetussa nimiavaruudessa:
Jos paikallinen Secret on olemassa, poista se käyttämällä kubectl delete;
jos paikallista salaisuutta ei havaita, se ei tee mitään.
Näin pystyimme luomaan yksinkertaisen Kubernetes-ohjaimen käyttämällä 35 riviä YAML-konfiguraatioita ja suunnilleen yhtä paljon Bash-koodia! Shell-operaattorin tehtävänä on yhdistää ne toisiinsa.
Salaisuuksien kopioiminen ei kuitenkaan ole apuohjelman ainoa sovellusalue. Tässä on vielä muutama esimerkki osoittamaan, mihin hän pystyy.
Esimerkki 1: Muutosten tekeminen ConfigMapiin
Katsotaanpa käyttöönottoa, joka koostuu kolmesta kotelosta. Podit käyttävät ConfigMap-ohjelmaa joidenkin asetusten tallentamiseen. Kun podit käynnistettiin, ConfigMap oli tietyssä tilassa (kutsutaanko sitä v.1:ksi). Näin ollen kaikki podit käyttävät tätä tiettyä ConfigMap-versiota.
Oletetaan nyt, että ConfigMap on muuttunut (v.2). Podit käyttävät kuitenkin ConfigMapin aiempaa versiota (v.1):
Kuinka saan ne siirtymään uuteen ConfigMapiin (v.2)? Vastaus on yksinkertainen: käytä mallia. Lisätään osioon tarkistussummamerkintä template Käyttöönottomääritykset:
Tämän seurauksena tämä tarkistussumma rekisteröidään kaikkiin koteloihin, ja se on sama kuin käyttöönoton tarkistussumma. Nyt sinun tarvitsee vain päivittää huomautus, kun ConfigMap muuttuu. Ja shell-operaattori on hyödyllinen tässä tapauksessa. Sinun tarvitsee vain ohjelmoida koukku, joka tilaa ConfigMapin ja päivittää tarkistussumman.
Jos käyttäjä tekee muutoksia ConfigMapiin, shell-operaattori huomaa ne ja laskee tarkistussumman uudelleen. Sen jälkeen Kubernetesin taika astuu peliin: orkesteri tappaa podin, luo uuden, odottaa, että siitä tulee Ready, ja siirtyy seuraavaan. Tämän seurauksena käyttöönotto synkronoituu ja siirtyy ConfigMapin uuteen versioon.
Esimerkki 2: Mukautettujen resurssimääritelmien käyttäminen
Kuten tiedät, Kubernetes antaa sinun luoda mukautettuja objektityyppejä. Voit esimerkiksi luoda ystävällisiä MysqlDatabase. Oletetaan, että tällä tyypillä on kaksi metatietoparametria: name и namespace.
apiVersion: example.com/v1alpha1
kind: MysqlDatabase
metadata:
name: foo
namespace: bar
Meillä on eri nimiavaruilla varustettu Kubernetes-klusteri, johon voimme luoda MySQL-tietokantoja. Tässä tapauksessa shell-operaattoria voidaan käyttää resurssien seurantaan MysqlDatabase, yhdistämällä ne MySQL-palvelimeen ja synkronoimalla klusterin halutut ja havaitut tilat.
Esimerkki 3: Cluster Network Monitoring
Kuten tiedät, pingin käyttäminen on yksinkertaisin tapa valvoa verkkoa. Tässä esimerkissä näytämme kuinka tällainen valvonta toteutetaan shell-operatorilla.
Ensinnäkin sinun on tilattava solmut. Shell-operaattori tarvitsee kunkin solmun nimen ja IP-osoitteen. Heidän avullaan hän pingoittaa nämä solmut.
Parametri executeHookOnEvent: [] estää koukun suorittamisen vastauksena mihin tahansa tapahtumaan (eli vastauksena solmujen muuttamiseen, lisäämiseen tai poistamiseen). Kuitenkin hän juoksee (ja päivitä solmuluettelo) Aikataulutettu - joka minuutti kentän ohjeiden mukaan schedule.
Nyt herää kysymys, kuinka tarkalleen tiedämme pakettien katoamisen kaltaisista ongelmista? Katsotaanpa koodia:
function __main__() {
for i in $(seq 0 "$(context::jq -r '(.snapshots.nodes | length) - 1')"); do
node_name="$(context::jq -r '.snapshots.nodes['"$i"'].filterResult.name')"
node_ip="$(context::jq -r '.snapshots.nodes['"$i"'].filterResult.ip')"
packets_lost=0
if ! ping -c 1 "$node_ip" -t 1 ; then
packets_lost=1
fi
cat >> "$METRICS_PATH" <<END
{
"name": "node_packets_lost",
"add": $packets_lost,
"labels": {
"node": "$node_name"
}
}
END
done
}
Selaamme solmuluettelon läpi, hankimme niiden nimet ja IP-osoitteet, pingimme ne ja lähetämme tulokset Prometheukselle. Shell-operaattori voi viedä mittareita Prometheukseen, tallentaa ne tiedostoon, joka sijaitsee ympäristömuuttujassa määritetyn polun mukaisesti $METRICS_PATH.
näin voit tehdä operaattorin yksinkertaista verkon valvontaa varten klusterissa.
Jonotusmekanismi
Tämä artikkeli olisi epätäydellinen kuvaamatta toista tärkeää mekanismia, joka on sisäänrakennettu shell-operaattoriin. Kuvittele, että se suorittaa jonkinlaisen koukun vastauksena klusterin tapahtumaan.
Mitä tapahtuu, jos samaan aikaan klusterissa tapahtuu jotain? yksi vielä tapahtuma?
Suorittaako shell-operaattori koukun toisen esiintymän?
Entä jos vaikkapa viisi tapahtumaa tapahtuu klusterissa kerralla?
Käsitteleekö shell-operaattori niitä rinnakkain?
Entä kulutetut resurssit, kuten muisti ja prosessori?
Onneksi shell-operaattorissa on sisäänrakennettu jonotusmekanismi. Kaikki tapahtumat ovat jonossa ja käsitellään peräkkäin.
Havainnollistetaan tätä esimerkein. Oletetaan, että meillä on kaksi koukkua. Ensimmäinen tapahtuma menee ensimmäiseen koukkuun. Kun sen käsittely on valmis, jono siirtyy eteenpäin. Seuraavat kolme tapahtumaa ohjataan toiseen koukkuun - ne poistetaan jonosta ja sisällytetään siihen "nippuna". Tuo on koukku vastaanottaa joukon tapahtumia - tai tarkemmin sanottuna joukko sitovia konteksteja.
Myös nämä tapahtumat voidaan yhdistää yhdeksi suureksi. Parametri on vastuussa tästä group sitovassa kokoonpanossa.
Voit luoda minkä tahansa määrän jonoja/koukkuja ja niiden erilaisia yhdistelmiä. Esimerkiksi yksi jono voi toimia kahdella koukulla tai päinvastoin.
Sinun tarvitsee vain määrittää kenttä vastaavasti queue sitovassa kokoonpanossa. Jos jonon nimeä ei ole määritetty, koukku toimii oletusjonossa (default). Tämän jonotusmekanismin avulla voit ratkaista täysin kaikki resurssienhallintaongelmat koukkujen kanssa työskennellessäsi.
Johtopäätös
Selitimme mitä shell-operaattori on, osoitimme kuinka sen avulla voidaan luoda nopeasti ja vaivattomasti Kubernetes-operaattoreita ja annoimme useita esimerkkejä sen käytöstä.
Yksityiskohtaiset tiedot shell-operaattorista sekä nopea opetusohjelma sen käyttöön ovat saatavilla vastaavassa arkistot GitHubissa. Älä epäröi ottaa meihin yhteyttä, jos sinulla on kysyttävää: voit keskustella niistä erityisessä Telegram ryhmä (venäjäksi) tai kielellä tämä foorumi (englanniksi).
Ja jos pidit siitä, olemme aina iloisia nähdessämme uusia numeroita/PR/tähtiä GitHubissa, josta muuten löydät muita mielenkiintoisia projekteja. Niistä kannattaa korostaa addon-operaattori, joka on shell-operaattorin isoveli. Tämä apuohjelma käyttää Helm-kaavioita lisäosien asentamiseen, voi toimittaa päivityksiä ja valvoa erilaisia kaavioparametreja/arvoja, ohjaa kaavioiden asennusprosessia ja voi myös muokata niitä vastauksena klusterin tapahtumiin.