ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z KubeCon EU'2020)

Tento rok bola hlavná európska konferencia Kubernetes – KubeCon + CloudNativeCon Europe 2020 – virtuálna. Takáto zmena formátu nám však nezabránila dodať dlho plánovanú správu „Go? Bash! Meet the Shell-operator“ venovaný nášmu projektu Open Source shell-operátor.

Tento článok, inšpirovaný prednáškou, predstavuje prístup k zjednodušeniu procesu vytvárania operátorov pre Kubernetes a ukazuje, ako si môžete vytvoriť svoj vlastný s minimálnym úsilím pomocou shell-operátora.

ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z KubeCon EU'2020)

Predstavujeme sa video zo správy (~23 minút v angličtine, výrazne informatívnejšie ako článok) a hlavný úryvok z neho v textovej podobe. Choď!

Vo Flante všetko neustále optimalizujeme a automatizujeme. Dnes budeme hovoriť o ďalšom vzrušujúcom koncepte. Zoznámte sa: cloud-native shell skriptovanie!

Začnime však kontextom, v ktorom sa to všetko deje: Kubernetes.

Kubernetes API a ovládače

API v Kubernetes môže byť reprezentované ako druh súborového servera s adresármi pre každý typ objektu. Objekty (zdroje) na tomto serveri sú reprezentované súbormi YAML. Okrem toho má server základné API, ktoré vám umožňuje robiť tri veci:

  • dostať zdroj podľa druhu a názvu;
  • zmeniť zdroj (v tomto prípade server ukladá iba „správne“ objekty - všetky nesprávne vytvorené alebo určené pre iné adresáre sú vyradené);
  • trať pre zdroj (v tomto prípade používateľ okamžite dostane jeho aktuálnu/aktualizovanú verziu).

Kubernetes teda funguje ako akýsi súborový server (pre YAML manifesty) s tromi základnými metódami (áno, v skutočnosti existujú aj iné, ale tie zatiaľ vynecháme).

ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z KubeCon EU'2020)

Problém je, že server môže ukladať iba informácie. Aby to fungovalo, potrebujete kontrolór - druhý najdôležitejší a základný koncept vo svete Kubernetes.

Existujú dva hlavné typy ovládačov. Prvý berie informácie z Kubernetes, spracováva ich podľa vnorenej logiky a vracia ich K8s. Druhý preberá informácie z Kubernetes, ale na rozdiel od prvého typu mení stav niektorých externých zdrojov.

Pozrime sa bližšie na proces vytvárania nasadenia v Kubernetes:

  • Ovládač nasadenia (súčasť kube-controller-manager) prijíma informácie o nasadení a vytvára ReplicaSet.
  • ReplicaSet vytvorí dve repliky (dve moduly) na základe týchto informácií, ale tieto moduly ešte nie sú naplánované.
  • Plánovač naplánuje moduly a pridá informácie o uzloch do ich YAML.
  • Kubelets vykoná zmeny v externom zdroji (povedzme Docker).

Potom sa celá táto sekvencia opakuje v opačnom poradí: kubelet skontroluje kontajnery, vypočíta stav modulu a odošle ho späť. Radič ReplicaSet prijíma stav a aktualizuje stav sady replík. To isté sa stane s ovládačom Deployment Controller a používateľ konečne získa aktualizovaný (aktuálny) stav.

ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z KubeCon EU'2020)

Shell-operátor

Ukazuje sa, že Kubernetes je založený na spoločnej práci rôznych kontrolórov (operátori Kubernetes sú tiež kontrolóri). Vynára sa otázka, ako si s minimálnym úsilím vytvoriť vlastného operátora? A tu prichádza na pomoc ten, ktorý sme vyvinuli shell-operátor. Umožňuje správcom systému vytvárať vlastné výpisy pomocou známych metód.

ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z KubeCon EU'2020)

Jednoduchý príklad: kopírovanie tajomstiev

Pozrime sa na jednoduchý príklad.

Povedzme, že máme klaster Kubernetes. Má menný priestor default s nejakým Tajomstvom mysecret. Okrem toho sú v klastri ďalšie menné priestory. Niektoré z nich majú na sebe pripevnený špecifický štítok. Naším cieľom je skopírovať Secret do menných priestorov so štítkom.

Úlohu komplikuje skutočnosť, že v klastri sa môžu objaviť nové menné priestory a niektoré z nich môžu mať toto označenie. Na druhej strane, keď sa štítok vymaže, malo by sa vymazať aj Tajné. Okrem toho sa môže zmeniť aj samotné tajomstvo: v tomto prípade musí byť nové tajomstvo skopírované do všetkých menných priestorov s menovkami. Ak dôjde k náhodnému vymazaniu tajomstva v akomkoľvek mennom priestore, náš operátor by ho mal okamžite obnoviť.

Teraz, keď je úloha sformulovaná, je čas začať ju implementovať pomocou shell-operátora. Najprv však stojí za to povedať pár slov o samotnom operátorovi shellu.

Ako funguje shell-operátor

Podobne ako iné pracovné zaťaženia v Kubernetes, aj shell-operátor beží vo svojom vlastnom pod. V tomto pod v adresári /hooks sú uložené spustiteľné súbory. Môžu to byť skripty v Bash, Python, Ruby atď. Takéto spustiteľné súbory nazývame háčiky (háky).

ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z KubeCon EU'2020)

Operátor Shell sa prihlasuje na odber udalostí Kubernetes a spúšťa tieto háky v reakcii na udalosti, ktoré potrebujeme.

ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z KubeCon EU'2020)

Ako vie operátor shellu, ktorý hák spustiť a kedy? Ide o to, že každý háčik má dve fázy. Počas spúšťania spustí operátor shell všetky háky s argumentom --config Toto je fáza konfigurácie. A po ňom sa háky spúšťajú normálnym spôsobom - v reakcii na udalosti, ku ktorým sú pripojené. V druhom prípade háčik dostane kontext väzby (záväzný kontext) - údaje vo formáte JSON, o ktorých si podrobnejšie povieme nižšie.

Vytvorenie operátora v Bash

Teraz sme pripravení na realizáciu. Aby sme to dosiahli, musíme napísať dve funkcie (mimochodom, odporúčame knižnica shell_lib, čo výrazne zjednodušuje písanie háčikov v Bash):

  • prvý je potrebný pre fázu konfigurácie – zobrazuje kontext väzby;
  • druhá obsahuje hlavnú logiku háku.

#!/bin/bash

source /shell_lib.sh

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

function __main__() {
  # THE LOGIC
}

hook::run "$@"

Ďalším krokom je rozhodnúť, aké predmety potrebujeme. V našom prípade musíme sledovať:

  • tajomstvo zdroja pre zmeny;
  • všetky menné priestory v klastri, aby ste vedeli, ku ktorým je priradený štítok;
  • cieľové tajné kľúče, aby ste sa uistili, že sú všetky synchronizované so zdrojovým tajným kľúčom.

Prihláste sa na odber tajného zdroja

Konfigurácia viazania je pomerne jednoduchá. Menom naznačujeme, že nás zaujíma Secret mysecret v mennom priestore default:

ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z 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

V dôsledku toho sa háčik spustí, keď sa zmení zdrojový tajný kód (src_secret) a získajte nasledujúci záväzný kontext:

ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z KubeCon EU'2020)

Ako vidíte, obsahuje názov a celý objekt.

Sledovanie menných priestorov

Teraz sa musíte prihlásiť na odber menných priestorov. Na tento účel zadáme nasledujúcu konfiguráciu väzby:

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

Ako vidíte, v konfigurácii sa objavilo nové pole s názvom jqFilter. Ako napovedá jej názov, jqFilter odfiltruje všetky nepotrebné informácie a vytvorí nový objekt JSON s poľami, ktoré nás zaujímajú. Hák s podobnou konfiguráciou získa nasledujúci väzbový kontext:

ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z KubeCon EU'2020)

Obsahuje pole filterResults pre každý menný priestor v klastri. Booleovská premenná hasLabel označuje, či je k danému mennému priestoru pripojený štítok. Selektor keepFullObjectsInMemory: false označuje, že nie je potrebné uchovávať v pamäti celé objekty.

Sledovanie tajomstiev cieľa

Odoberáme všetky tajomstvá, ktoré majú špecifikovanú anotáciu managed-secret: "yes" (toto sú náš cieľ 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 tomto prípade jqFilter odfiltruje všetky informácie okrem menného priestoru a parametra resourceVersion. Posledný parameter bol odovzdaný do anotácie pri vytváraní tajomstva: umožňuje porovnávať verzie tajomstiev a udržiavať ich aktuálne.

Hák nakonfigurovaný týmto spôsobom po spustení získa tri kontexty viazania opísané vyššie. Možno ich považovať za určitý druh snímky (momentka) zhluk.

ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z KubeCon EU'2020)

Na základe všetkých týchto informácií je možné vytvoriť základný algoritmus. Iteruje cez všetky menné priestory a:

  • ak hasLabel záležitosti true pre aktuálny menný priestor:
    • porovnáva globálne tajomstvo s lokálnym:
      • ak sú rovnaké, nerobí to nič;
      • ak sa líšia - vykoná kubectl replace alebo create;
  • ak hasLabel záležitosti false pre aktuálny menný priestor:
    • zaisťuje, že Secret nie je v danom mennom priestore:
      • ak je prítomný miestny tajný kód, odstráňte ho pomocou kubectl delete;
      • ak sa nezistí miestne tajomstvo, neurobí nič.

ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z KubeCon EU'2020)

Implementácia algoritmu v Bash si môžete stiahnuť v našom úložiská s príkladmi.

Takto sme dokázali vytvoriť jednoduchý ovládač Kubernetes pomocou 35 riadkov konfigurácie YAML a približne rovnakého množstva kódu Bash! Úlohou operátora shellu je spojiť ich dohromady.

Kopírovanie tajomstiev však nie je jedinou oblasťou použitia nástroja. Tu je niekoľko ďalších príkladov, ktoré ukážu, čoho je schopný.

Príklad 1: Vykonávanie zmien v ConfigMap

Pozrime sa na nasadenie pozostávajúce z troch modulov. Moduly používajú ConfigMap na uloženie určitej konfigurácie. Keď boli moduly spustené, ConfigMap bol v určitom stave (nazvime to v.1). Preto všetky moduly používajú túto konkrétnu verziu ConfigMap.

Teraz predpokladajme, že sa mapa ConfigMap zmenila (v.2). Moduly však budú používať predchádzajúcu verziu ConfigMap (v.1):

ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z KubeCon EU'2020)

Ako ich môžem prinútiť prejsť na novú mapu ConfigMap (v.2)? Odpoveď je jednoduchá: použite šablónu. Do sekcie pridáme anotáciu kontrolného súčtu template Konfigurácie nasadenia:

ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z KubeCon EU'2020)

Výsledkom bude, že tento kontrolný súčet bude zaregistrovaný vo všetkých moduloch a bude rovnaký ako pri nasadení. Teraz stačí aktualizovať anotáciu, keď sa zmení ConfigMap. A shell-operátor príde v tomto prípade vhod. Všetko, čo musíte urobiť, je naprogramovať háčik, ktorý sa prihlási na odber mapy ConfigMap a aktualizuje kontrolný súčet.

Ak používateľ vykoná zmeny v ConfigMap, operátor shellu si ich všimne a prepočíta kontrolný súčet. Potom vstúpi do hry kúzlo Kubernetes: orchestrátor zabije modul, vytvorí nový, počká, kým sa stane Readya prejde na ďalší. V dôsledku toho sa Deployment zosynchronizuje a prepne na novú verziu ConfigMap.

ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z KubeCon EU'2020)

Príklad 2: Práca s definíciami vlastných zdrojov

Ako viete, Kubernetes vám umožňuje vytvárať vlastné typy objektov. Môžete napríklad vytvoriť druh MysqlDatabase. Povedzme, že tento typ má dva parametre metadát: name и namespace.

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

Máme klaster Kubernetes s rôznymi mennými priestormi, v ktorých môžeme vytvárať databázy MySQL. V tomto prípade môže byť na sledovanie zdrojov použitý shell-operátor MysqlDatabase, ich pripojenie k serveru MySQL a synchronizácia požadovaných a pozorovaných stavov klastra.

ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z KubeCon EU'2020)

Príklad 3: Monitorovanie siete klastra

Ako viete, používanie príkazu ping je najjednoduchší spôsob monitorovania siete. V tomto príklade si ukážeme, ako implementovať takéto monitorovanie pomocou shell-operátora.

Najprv sa budete musieť prihlásiť na odber uzlov. Operátor shell potrebuje názov a IP adresu každého uzla. S ich pomocou bude pingovať tieto uzly.

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: [] bráni spusteniu háku v reakcii na akúkoľvek udalosť (teda v reakcii na zmenu, pridávanie, odstraňovanie uzlov). Avšak on pobeží (a aktualizovať zoznam uzlov) Naplánovaný - každú minútu, ako predpisuje pole schedule.

Teraz vyvstáva otázka, ako presne vieme o problémoch, ako je strata paketov? Poďme sa pozrieť na kód:

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
}

Iterujeme cez zoznam uzlov, získame ich mená a IP adresy, pingneme ich a pošleme výsledky do Prometheus. Shell-operátor môže exportovať metriky do Prometheus, uložte ich do súboru umiestneného podľa cesty uvedenej v premennej prostredia $METRICS_PATH.

Páči sa ti to môžete vytvoriť operátora pre jednoduché monitorovanie siete v klastri.

Mechanizmus radenia

Tento článok by bol neúplný bez opisu ďalšieho dôležitého mechanizmu zabudovaného do shell-operátora. Predstavte si, že vykoná nejaký hák ako odpoveď na udalosť v klastri.

  • Čo sa stane, ak sa zároveň v klastri niečo stane? ešte jeden udalosť?
  • Spustí shell-operátor ďalšiu inštanciu háku?
  • Čo ak sa v klastri stane povedzme päť udalostí naraz?
  • Bude ich shell-operátor spracovávať paralelne?
  • A čo spotrebované zdroje, ako je pamäť a CPU?

Našťastie má shell-operátor zabudovaný mechanizmus radenia. Všetky udalosti sú zaradené do frontu a spracované postupne.

Ilustrujme si to na príkladoch. Povedzme, že máme dva háčiky. Prvá udalosť ide do prvého háku. Po dokončení spracovania sa front posunie dopredu. Nasledujúce tri udalosti sú presmerované na druhý hák - sú odstránené z frontu a zaradené do „balíka“. Teda hák prijíma celý rad udalostí — alebo, presnejšie, súbor záväzných kontextov.

Aj tieto podujatia sa dajú spojiť do jedného veľkého. Za to je zodpovedný parameter group v konfigurácii väzby.

ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z KubeCon EU'2020)

Môžete vytvoriť ľubovoľný počet frontov/hákov a ich rôzne kombinácie. Napríklad jedna fronta môže pracovať s dvoma háčikmi alebo naopak.

ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z KubeCon EU'2020)

Všetko, čo musíte urobiť, je príslušne nakonfigurovať pole queue v konfigurácii väzby. Ak nie je zadaný názov frontu, hák sa spustí v predvolenom fronte (default). Tento mechanizmus radenia vám umožňuje úplne vyriešiť všetky problémy so správou zdrojov pri práci s háčikmi.

Záver

Vysvetlili sme si, čo je shell-operátor, ukázali, ako sa dá použiť na rýchle a jednoduché vytváranie operátorov Kubernetes a uviedli niekoľko príkladov jeho použitia.

Podrobné informácie o operátorovi shellu, ako aj rýchly návod, ako ho používať, sú k dispozícii v príslušnom úložiská na GitHub. Neváhajte nás kontaktovať s otázkami: môžete o nich diskutovať v špeciáli Telegramová skupina (v ruštine) alebo v toto fórum (v angličtine).

A ak sa vám to páčilo, vždy sa tešíme z nových vydaní/PR/hviezd na GitHub, kde, mimochodom, nájdete ďalšie zaujímavé projekty. Medzi nimi stojí za to zdôrazniť operátor doplnku, čo je veľký brat shell-operátora. Tento nástroj používa Helm diagramy na inštaláciu doplnkov, môže dodávať aktualizácie a monitorovať rôzne parametre/hodnoty grafov, riadi proces inštalácie diagramov a môže ich tiež upravovať v reakcii na udalosti v klastri.

ísť? Bash! Zoznámte sa so shell-operátorom (recenzia a videoreportáž z KubeCon EU'2020)

Videá a diapozitívy

Video z predstavenia (~23 minút):


Prezentácia správy:

PS

Prečítajte si aj na našom blogu:

Zdroj: hab.com

Pridať komentár