iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

Letos je bila glavna evropska Kubernetes konferenca – KubeCon + CloudNativeCon Europe 2020 – virtualna. Vendar pa nam takšna sprememba formata ni preprečila, da bi izdali dolgo načrtovano poročilo »Go? Bash! Spoznajte Shell-operatorja«, ki je namenjen našemu odprtokodnemu projektu lupina-operater.

Ta članek, ki ga je navdihnil pogovor, predstavlja pristop k poenostavitvi postopka ustvarjanja operaterjev za Kubernetes in prikazuje, kako lahko ustvarite svojega z minimalnim naporom z uporabo lupinskega operaterja.

iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

Predstavljamo video poročila (~23 minut v angleščini, opazno bolj informativno kot članek) in glavni izvleček iz njega v besedilni obliki. Pojdi!

Pri Flantu vse nenehno optimiziramo in avtomatiziramo. Danes bomo govorili o še enem vznemirljivem konceptu. Srečati: skriptna lupina v oblaku!

Vendar pa začnimo s kontekstom, v katerem se vse to zgodi: Kubernetes.

Kubernetes API in krmilniki

API v Kubernetesu je mogoče predstaviti kot nekakšen datotečni strežnik z imeniki za vsako vrsto predmeta. Objekti (viri) na tem strežniku so predstavljeni z datotekami YAML. Poleg tega ima strežnik osnovni API, ki vam omogoča tri stvari:

  • prejeti vir po vrsti in imenu;
  • spremeniti vir (v tem primeru strežnik shrani samo "pravilne" objekte - vsi nepravilno oblikovani ali namenjeni drugim imenikom se zavržejo);
  • skladbo za vir (v tem primeru uporabnik takoj prejme njegovo trenutno/posodobljeno različico).

Tako Kubernetes deluje kot nekakšen datotečni strežnik (za manifeste YAML) s tremi osnovnimi metodami (ja, dejansko obstajajo še druge, a jih bomo za zdaj izpustili).

iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

Težava je v tem, da lahko strežnik samo hrani informacije. Da bi delovalo, potrebujete krmilnik - drugi najpomembnejši in temeljni koncept v svetu Kubernetesa.

Obstajata dve glavni vrsti krmilnikov. Prvi vzame informacije iz Kubernetesa, jih obdela v skladu z ugnezdeno logiko in jih vrne v K8s. Drugi vzame informacije iz Kubernetesa, vendar za razliko od prvega spremeni stanje nekaterih zunanjih virov.

Oglejmo si podrobneje postopek ustvarjanja razmestitve v Kubernetesu:

  • Krmilnik za uvajanje (vključen v kube-controller-manager) prejme informacije o razmestitvi in ​​ustvari ReplicaSet.
  • ReplicaSet na podlagi teh informacij ustvari dve repliki (dva sklopa), vendar ti sklopi še niso načrtovani.
  • Razporejevalnik načrtuje pode in doda informacije o vozliščih v njihove YAML.
  • Kubelets spremeni zunanji vir (recimo Docker).

Nato se celotno zaporedje ponovi v obratnem vrstnem redu: kubelet preveri vsebnike, izračuna status stroka in ga pošlje nazaj. Krmilnik ReplicaSet prejme status in posodobi stanje nabora replik. Enako se zgodi s krmilnikom razmestitve in uporabnik končno dobi posodobljen (trenutni) status.

iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

Operater lupine

Izkazalo se je, da Kubernetes temelji na skupnem delu različnih krmilnikov (operatorji Kubernetes so tudi krmilniki). Postavlja se vprašanje, kako z minimalnim trudom ustvariti svojega operaterja? In tu na pomoč priskoči tisti, ki smo ga razvili lupina-operater. Sistemskim skrbnikom omogoča ustvarjanje lastnih izjav z uporabo znanih metod.

iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

Preprost primer: kopiranje skrivnosti

Poglejmo preprost primer.

Recimo, da imamo gručo Kubernetes. Ima imenski prostor default z nekaj skrivnosti mysecret. Poleg tega so v gruči še drugi imenski prostori. Nekateri od njih imajo posebno oznako. Naš cilj je kopirati Secret v imenske prostore z oznako.

Naloga je zapletena zaradi dejstva, da se lahko v gruči pojavijo novi imenski prostori in nekateri od njih imajo lahko to oznako. Po drugi strani, ko je oznaka izbrisana, je treba izbrisati tudi Secret. Poleg tega se lahko spremeni tudi sama skrivnost: v tem primeru je treba novo skrivnost kopirati v vse imenske prostore z oznakami. Če je Secret pomotoma izbrisan v katerem koli imenskem prostoru, ga mora naš operater takoj obnoviti.

Zdaj, ko je bila naloga oblikovana, je čas, da jo začnemo izvajati z uporabo lupinskega operaterja. Toda najprej je vredno povedati nekaj besed o samem operaterju lupine.

Kako deluje lupinski operater

Tako kot druge delovne obremenitve v Kubernetesu tudi shell-operator deluje v lastnem bloku. V tem razdelku v imeniku /hooks so shranjene izvršljive datoteke. To so lahko skripte v Bash, Python, Ruby itd. Takim izvršljivim datotekam pravimo kavlji (kljuke).

iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

Shell-operator se naroči na dogodke Kubernetes in zažene te kljuke kot odgovor na dogodke, ki jih potrebujemo.

iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

Kako lupinski operater ve, kateri kavelj zagnati in kdaj? Bistvo je, da ima vsak trnek dve stopnji. Med zagonom lupinski operater zažene vse kljuke z argumentom --config To je faza konfiguracije. In po njem se trnki sprožijo na običajen način - kot odgovor na dogodke, na katere so pripeti. V slednjem primeru kavelj prejme vezni kontekst (zavezujoč kontekst) - podatki v formatu JSON, o katerih bomo podrobneje govorili v nadaljevanju.

Izdelava operatorja v Bashu

Zdaj smo pripravljeni na izvedbo. Za to moramo napisati dve funkciji (mimogrede, priporočamo knjižnica shell_lib, ki močno poenostavi pisanje kavljev v Bashu):

  • prvi je potreben za konfiguracijsko stopnjo - prikaže vezni kontekst;
  • drugi vsebuje glavno logiko kljuke.

#!/bin/bash

source /shell_lib.sh

function __config__() {
  cat << EOF
    configVersion: v1
    # BINDING CONFIGURATION
EOF
}

function __main__() {
  # THE LOGIC
}

hook::run "$@"

Naslednji korak je odločitev, katere predmete potrebujemo. V našem primeru moramo slediti:

  • izvorna skrivnost za spremembe;
  • vse imenske prostore v gruči, da boste vedeli, katerim je pritrjena oznaka;
  • ciljne skrivnosti, da zagotovite, da so vse sinhronizirane z izvorno skrivnostjo.

Naročite se na tajni vir

Konfiguracija vezave je precej preprosta. Z imenom označimo, da nas zanima Secret mysecret v imenskem prostoru default:

iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

function __config__() {
  cat << EOF
    configVersion: v1
    kubernetes:
    - name: src_secret
      apiVersion: v1
      kind: Secret
      nameSelector:
        matchNames:
        - mysecret
      namespace:
        nameSelector:
          matchNames: ["default"]
      group: main
EOF

Posledično se bo kavelj sprožil, ko se spremeni izvorna skrivnost (src_secret) in prejmejo naslednji vezni kontekst:

iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

Kot lahko vidite, vsebuje ime in celoten predmet.

Spremljanje imenskih prostorov

Zdaj se morate naročiti na imenske prostore. Da bi to naredili, podamo naslednjo konfiguracijo vezave:

- name: namespaces
  group: main
  apiVersion: v1
  kind: Namespace
  jqFilter: |
    {
      namespace: .metadata.name,
      hasLabel: (
       .metadata.labels // {} |  
         contains({"secret": "yes"})
      )
    }
  group: main
  keepFullObjectsInMemory: false

Kot lahko vidite, se je v konfiguraciji pojavilo novo polje z imenom jqFilter. Kot že ime pove, jqFilter filtrira vse nepotrebne podatke in ustvari nov objekt JSON s polji, ki nas zanimajo. Kavelj s podobno konfiguracijo bo prejel naslednji vezni kontekst:

iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

Vsebuje niz filterResults za vsak imenski prostor v gruči. Logična spremenljivka hasLabel označuje, ali je oznaka pripeta danemu imenskemu prostoru. Selektor keepFullObjectsInMemory: false označuje, da v pomnilniku ni treba hraniti celotnih objektov.

Sledenje ciljnim skrivnostim

Naročeni smo na vse skrivnosti, ki imajo določeno opombo managed-secret: "yes" (to so naš cilj dst_secrets):

- name: dst_secrets
  apiVersion: v1
  kind: Secret
  labelSelector:
    matchLabels:
      managed-secret: "yes"
  jqFilter: |
    {
      "namespace":
        .metadata.namespace,
      "resourceVersion":
        .metadata.annotations.resourceVersion
    }
  group: main
  keepFullObjectsInMemory: false

V tem primeru jqFilter filtrira vse informacije razen imenskega prostora in parametra resourceVersion. Zadnji parameter je bil posredovan pripisu pri ustvarjanju skrivnosti: omogoča primerjavo različic skrivnosti in njihovo posodabljanje.

Kavelj, konfiguriran na ta način, bo ob izvedbi prejel tri zgoraj opisane vezne kontekste. Lahko si jih predstavljamo kot nekakšen posnetek (Posnetek) grozd.

iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

Na podlagi vseh teh informacij je mogoče razviti osnovni algoritem. Ponavlja vse imenske prostore in:

  • če hasLabel zadeve true za trenutni imenski prostor:
    • primerja globalno skrivnost z lokalno:
      • če sta enaka, ne naredi nič;
      • če se razlikujejo - izvrši kubectl replace ali create;
  • če hasLabel zadeve false za trenutni imenski prostor:
    • poskrbi, da Secret ni v danem imenskem prostoru:
      • če je lokalna skrivnost prisotna, jo izbrišite z uporabo kubectl delete;
      • če lokalna skrivnost ni zaznana, ne naredi ničesar.

iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

Implementacija algoritma v Bashu lahko prenesete v naši repozitorije s primeri.

Tako smo lahko ustvarili preprost krmilnik Kubernetes z uporabo 35 vrstic konfiguracije YAML in približno enake količine kode Bash! Naloga operaterja lupine je, da jih poveže.

Vendar pa kopiranje skrivnosti ni edino področje uporabe pripomočka. Tukaj je še nekaj primerov, ki kažejo, česa je sposoben.

Primer 1: Spreminjanje ConfigMap

Oglejmo si razporeditev, sestavljeno iz treh sklopov. Pods uporabljajo ConfigMap za shranjevanje nekaterih konfiguracij. Ko so bili podi zagnani, je bil ConfigMap v določenem stanju (recimo temu v.1). V skladu s tem vsi sklopi uporabljajo to posebno različico ConfigMap.

Zdaj pa predpostavimo, da se je ConfigMap spremenil (v.2). Vendar pa bodo sklopi uporabljali prejšnjo različico ConfigMap (v.1):

iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

Kako naj jih prepričam, da preklopijo na nov ConfigMap (v.2)? Odgovor je preprost: uporabite predlogo. V razdelek dodamo opombo kontrolne vsote template Konfiguracije uvajanja:

iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

Posledično bo ta kontrolna vsota registrirana v vseh sklopih in bo enaka kot pri razmestitvi. Zdaj morate samo posodobiti opombo, ko se spremeni ConfigMap. In lupinski operater pride v tem primeru prav. Vse kar morate storiti je, da programirate kavelj, ki se bo naročil na ConfigMap in posodobil kontrolno vsoto.

Če uporabnik naredi spremembe v ConfigMap, jih bo operater lupine opazil in znova izračunal kontrolno vsoto. Nato se bo začela uporabljati čarovnija Kubernetesa: orkestrator bo uničil pod, ustvaril novega in počakal, da postane Ready, in se premakne na naslednjega. Posledično se bo Deployment sinhroniziral in preklopil na novo različico ConfigMap.

iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

Primer 2: Delo z definicijami virov po meri

Kot veste, vam Kubernetes omogoča ustvarjanje vrst predmetov po meri. Na primer, lahko ustvarite vrsto MysqlDatabase. Recimo, da ima ta vrsta dva parametra metapodatkov: name и namespace.

apiVersion: example.com/v1alpha1
kind: MysqlDatabase
metadata:
  name: foo
  namespace: bar

Imamo gručo Kubernetes z različnimi imenskimi prostori, v katerih lahko ustvarjamo baze podatkov MySQL. V tem primeru lahko za sledenje virom uporabite lupinski operater MysqlDatabase, ki jih poveže s strežnikom MySQL in sinhronizira želena in opažena stanja gruče.

iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

Primer 3: Nadzor omrežja gruče

Kot veste, je uporaba pinga najpreprostejši način za nadzor omrežja. V tem primeru bomo pokazali, kako izvesti takšno spremljanje z uporabo lupinskega operaterja.

Najprej se boste morali naročiti na vozlišča. Operater lupine potrebuje ime in naslov IP vsakega vozlišča. Z njihovo pomočjo bo pingal ta vozlišča.

configVersion: v1
kubernetes:
- name: nodes
  apiVersion: v1
  kind: Node
  jqFilter: |
    {
      name: .metadata.name,
      ip: (
       .status.addresses[] |  
        select(.type == "InternalIP") |
        .address
      )
    }
  group: main
  keepFullObjectsInMemory: false
  executeHookOnEvent: []
schedule:
- name: every_minute
  group: main
  crontab: "* * * * *"

Parameter executeHookOnEvent: [] preprečuje, da bi se kljuka zagnala kot odziv na kateri koli dogodek (to je kot odgovor na spreminjanje, dodajanje, brisanje vozlišč). Vendar pa on bo tekel (in posodobite seznam vozlišč) Načrtovano - vsako minuto, kot predpisuje področje schedule.

Zdaj se postavlja vprašanje, kako natančno vemo za težave, kot je izguba paketov? Oglejmo si kodo:

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
}

Iteriramo po seznamu vozlišč, pridobimo njihova imena in naslove IP, jih pingamo in pošljemo rezultate Prometheusu. Operater lupine lahko izvozi meritve v Prometheusin jih shrani v datoteko, ki se nahaja v skladu s potjo, določeno v spremenljivki okolja $METRICS_PATH.

Tukaj tako lahko naredite operaterja za preprosto spremljanje omrežja v gruči.

Mehanizem čakalne vrste

Ta članek bi bil nepopoln brez opisa drugega pomembnega mehanizma, vgrajenega v lupinskega operaterja. Predstavljajte si, da izvede nekakšen kavelj kot odgovor na dogodek v gruči.

  • Kaj se zgodi, če se istočasno nekaj zgodi v gruči? še en dogodek?
  • Ali bo lupinski operater zagnal še en primerek kljuke?
  • Kaj pa, če se recimo v gruči zgodi pet dogodkov hkrati?
  • Ali jih bo lupinski operater obdeloval vzporedno?
  • Kaj pa porabljeni viri, kot sta pomnilnik in CPE?

Na srečo ima shell-operator vgrajen mehanizem čakalne vrste. Vsi dogodki so v čakalni vrsti in obdelani zaporedno.

Naj to ponazorimo s primeri. Recimo, da imamo dva trnka. Prvi dogodek gre na prvo kljuko. Ko je obdelava končana, se čakalna vrsta premakne naprej. Naslednji trije dogodki so preusmerjeni na drugi kavelj - odstranjeni so iz čakalne vrste in vanjo vneseni v "sveženj". To je kavelj prejme niz dogodkov — ali, natančneje, niz zavezujočih kontekstov.

Tudi te dogodke lahko združimo v eno veliko. Za to je odgovoren parameter group v vezni konfiguraciji.

iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

Ustvarite lahko poljubno število čakalnih vrst/kavljev in njihovih različnih kombinacij. Na primer, ena čakalna vrsta lahko deluje z dvema kljukama ali obratno.

iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

Vse kar morate storiti je, da ustrezno konfigurirate polje queue v vezni konfiguraciji. Če ime čakalne vrste ni podano, se kavelj izvaja v privzeti čakalni vrsti (default). Ta mehanizem čakalne vrste vam omogoča, da v celoti rešite vse težave z upravljanjem virov pri delu s kavlji.

Zaključek

Razložili smo, kaj je lupinski operater, pokazali, kako ga lahko uporabimo za hitro in enostavno ustvarjanje operaterjev Kubernetes, in podali več primerov njegove uporabe.

Podrobne informacije o lupinskem operaterju in hitra vadnica o njegovi uporabi so na voljo v ustreznem repozitorije na GitHubu. Ne oklevajte in se obrnite na nas z vprašanji: o njih lahko razpravljate v posebnem Telegram skupina (v ruščini) ali v tem forumu (v angleščini).

In če vam je bilo všeč, smo vedno veseli novih številk/PR/zvezd na GitHubu, kjer, mimogrede, najdete druge zanimivi projekti. Med njimi velja izpostaviti addon-operator, ki je starejši brat shell-operatorja. Ta pripomoček uporablja grafikone Helm za namestitev dodatkov, lahko dostavlja posodobitve in spremlja različne parametre/vrednosti grafikona, nadzoruje postopek namestitve grafikonov in jih lahko tudi spreminja kot odziv na dogodke v gruči.

iti? Bash! Spoznajte operaterja lupine (recenzija in video poročilo s KubeCon EU'2020)

Video posnetki in diapozitivi

Video z nastopa (~23 minut):


Predstavitev poročila:

PS

Preberite tudi na našem blogu:

Vir: www.habr.com

Dodaj komentar