Go? Bash! Tutvuge shell-operatoriga (ülevaade ja video KubeCon EU'2020 ettekandest)

Sel aastal toimus Euroopa peamine Kubernetes'i konverents KubeCon + CloudNativeCon Europe 2020 virtuaalselt. Siiski ei takistanud see formaadi muutus meid esitlemast juba ammu plaanitud ettekannet „Go? Bash! Tutvuge Shell-operatoriga”, mis on pühendatud meie Open Source projektile. shell-operator.

Selles artiklis, mis põhineb ettekandel, esitatakse lähenemine Kubernetes'i operaatorite loomise protsessi lihtsustamiseks ja näidatakse, kuidas shell-operator'i abil saab minimaalse vaevaga luua omaenda operaatori.

Go? Bash! Tere tulemast shell-operatori (ülevaade ja ettekande video KubeCon EU'2020-lt)

Tutvustame ettekande video vaatamiseks (~23 minutit inglise keeles, selgelt informatiivsem kui artikkel) ja põhisisu sellest tekstivormis. Alustame!

Me „Flante'is” optimeerime ja automatiseerime pidevalt. Täna räägime veel ühest põnevast kontseptsioonist. Tere tulemast: cloud-native shell-skriptimine!

Kuid alustame kontekstist, milles see kõik aset leiab — Kubernetes’est.

Kubernetes API ja kontrollijad

Kubernetes'es võib API-d kujutada kui mingisugust failiserverit, kus igat tüüpi objektide jaoks on katalogid. Selle serveri objektid (ressursid) on esitatud YAML-failidena. Lisaks on serveril baas-API, mis võimaldab teha kolme asja:

  • saama ressursi järgi selle kind'i ja nime;
  • muutma ressurssi (samal ajal server salvestab ainult "õigeid" objekte — kõik valesti vormistatud või teiste kaustade jaoks määratud objektid jäetakse kõrvale);
  • jälgida ressursi (sel juhul saab kasutaja kohe selle praeguse/uuendatud versiooni).

Seega toimib Kubernetes nagu failiserver (YAML-manifestide jaoks) kolme põhimeetodiga (jah, tegelikult on ka teisi, aga neid me praegu ei käsitle).

Go? Bash! Tere tulemast shell-operatori (ülevaade ja ettekande video KubeCon EU'2020-lt)

Probleem on selles, et server oskab ainult teavet salvestada. Selle töötamiseks on vajalik kontroller — teine olulisus ja fundamentaalsus Kubernetes'e maailmas.

Erinevad kaks peamist kontrollerite tüüpi. Esimene võtab teavet Kubernetes'est, töötleb seda vastavalt sisemise loogika järgi ja tagastab K8s-ile. Teine — võtab teavet Kubernetes'est, kuid erinevalt esimesest tüübist muudab välistes ressurssides olekut.

Vaatame lähemalt Kubernetes'st Deployment’i loomise protsessi:

  • Deployment Controller (mis kuulub kube-controller-manager) saab teavet Deployment’i kohta ja loob ReplicaSet’i.
  • ReplicaSet loob selle teabe põhjal kaks koopiat (kahte pod'i), kuid need pod'id veel ei planeeritud.
  • Planeerija planeerib pod'e ja lisab nende YAML-idesse teavet sõlmede kohta.
  • Kubelet'id teevad muudatusi välistes ressurssides (näiteks Dockeris).

Seejärel kordub kogu see järjestus vastupidises järjekorras: kubelet kontrollib konteinerite olekut, arvutab pod'i staatuse ja saadab selle tagasi. ReplicaSeti kontrollija saab staatuse ja värskendab replikate kogumi olekut. Sama toimub Deployment Controlleriga, ja kasutaja saab lõpuks värskendatud (praeguse) staatuse.

Go? Bash! Tere tulemast shell-operatori (ülevaade ja ettekande video KubeCon EU'2020-lt)

Shell-operator

Kubernetes'e aluseks on erinevate kontrollijate koostöö (Kubernetes'e operaatorid on samuti kontrollijad). Kuidas luua oma operaator minimaalse vaevaga? Siin tuleb appi meie välja töötatud shell-operator. See võimaldab süsteemiadministraatoritel luua oma operaatorid, kasutades tuttavaid meetodeid.

Go? Bash! Tere tulemast shell-operatori (ülevaade ja ettekande video KubeCon EU'2020-lt)

Lihtne näide: saladuste kopeerimine

Vaatame lihtsat näidet.

Oletame, et meil on Kubernetes'e klaster. Seal on nimesüsteem default mingi Secret'iga mysecret. Lisaks on klastris ka teisi nimiruume. Mõnedele neist on kinnitatud teatud silt. Meie eesmärk on kopeerida Secret nimiruumidesse, kus on see silt.

Ülesanne muutub keerulisemaks, kuna klastris võivad tekkida uued nimiruumid, ja mõnel neist võib olla see silt. Teiselt poolt, sildi eemaldamisel peab Secret ka eemaldama. Lisaks võib ka ise Secret muutuda: sel juhul peab uus Secret olema kopeeritud kõigisse nimiruumidesse, kus on sildid. Kui Secret juhuslikult mõnes nimiruumis eemaldatakse, peab meie operaator selle kohe taastama.

Nüüd, kui ülesanne on sõnastatud, on aeg alustada selle elluviimist shell-operatori abil. Kuid esmalt tuleks öelda paar sõna shell-operatori kohta.

Shell-operatori tööpõhimõtted

Nagu teised töökoormused Kuberneteses, töötab shell-operator oma pod’is. Selles pod’is asub kataloog /hooks , kus hoitakse täidetavaid faile. Need võivad olla Bash, Python, Ruby jne skripte. Selliseid täidetavaid faile nimetame hook'ideks (hooks).

Go? Bash! Tere tulemast shell-operatori (ülevaade ja ettekande video KubeCon EU'2020-lt)

). Shell-operator subscribe'ib Kuberneteses toimuvatele sündmustele ja käivitab neid hook'e vastusena neile sündmustele, mis meid huvitavad.

Go? Bash! Tere tulemast shell-operatori (ülevaade ja ettekande video KubeCon EU'2020-lt)

Kuidas shell-operator teab, millal ja millist hook'i käivitada? Iga hook'il on kaks etappi. Shell-operator käivitab hook'id koos argumendiga alustatud etapis --config — see on seadistamise etapp. Pärast seda käivituvad hook'id tavaliselt — vastuseks sündmustele, millega nad on seotud. Viimases juhul saab hook sidumise konteksti (binding context) — andmed JSON-formaadis, millest räägime lähemalt allpool.

Teeme Bash'is operaatorit

Nüüd oleme valmis teostuseks. Selleks peame kirjutama kaks funktsiooni (märkusena soovitame raamatukogu shell_lib, mis lihtsustab hook'ide kirjutamist Bash'is):

  • esimene on vajalik seadistamise etapis — see kuvab sidumise konteksti;
  • teine sisaldab hook'i põhiloogikat.

#!/bin/bash

source /shell_lib.sh

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

function __main__() {
  # THE LOGIC
}

hook::run "$@"

J siguiente etapas — kindlaks teha, milliseid objekte vajame. Meie puhul on vajalik jälgida:

  • salajase allika muudatused;
  • kõiki namespace'e klastris, et teada, millistele neist on kleebis kinnitatud;
  • salajased sihid, et veenduda, et kõik need on sünkroonitud salajase allikaga.

Liitume salajase allikaga

Binding configuration on sellele on piisavalt lihtne. Me näitame, et meid huvitab saladus nimega mysecret nimeserveris default:

Go? Bash! Tere tulemast shell-operatori (ülevaade ja ettekande video KubeCon EU'2020-lt)

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

Kokkuvõttes käivitatakse hook, kui saladuse allikas muutub (src_secret) ja saab järgmise binding context'i:

Go? Bash! Tere tulemast shell-operatori (ülevaade ja ettekande video KubeCon EU'2020-lt)

Nagu näete, sisaldab see nime ja objekti tervikuna.

Jälgime nimesid

Nüüd peame registreeruma nimedele. Selleks näitame järgmist binding configuration'i:

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

Nagu näete, on konfiguratsioonis uus väli nimega jqFilter. Kuidas tema nimi ütleb, jqFilter filtreerib kogu üleliigse teabe ja loob uue JSON objekti, mille väljad on meie jaoks huvitavad. Hook sellise konfiguratsiooniga saab järgmise binding context'i:

Go? Bash! Tere tulemast shell-operatori (ülevaade ja ettekande video KubeCon EU'2020-lt)

See sisaldab massiivi filterResults iga klastris asuva nime jaoks. Boolean muutuja hasLabel näitab, kas silt on antud nimede ruumiga seotud. Valija keepFullObjectsInMemory: false ütleb, et pole vaja hoida täisobjekte mälus.

Jälgime sihtsaladusi

Tellime kõiki salajasi, millel on määratud annotatsioon managed-secret: "yes" (need on meie sihttou 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

Sel juhul jqFilter filtreerib kogu teabe välja, välja arvatud nimede ruum ja parameeter resourceVersion. Viimane parameeter anti annotatsioonis, kui saladus loodi: see võimaldab võrrelda saladuste versioone ja hoida neid ajakohasena.

Hook, mis on selliselt seadistatud, saab tõhusalt kolme konteksti sidumist, nagu eespool kirjeldatud. Neid võib esitada kui teatud tüüpi hetkeseis (snapshot) klastri.

Go? Bash! Tere tulemast shell-operatori (ülevaade ja ettekande video KubeCon EU'2020-lt)

Kogu selle teabe põhjal saab välja töötada põhialgoritmi. See läbib kõik nimede ruumid ja:

  • kui hasLabel väärtus true praeguse nimede ruumi jaoks:
    • võrdleb globaalset saladust kohaliku:
      • kui nad on samad — ei tee midagi;
      • kui nad erinevad — täidab kubectl asendada või create;
  • kui hasLabel väärtus false praeguse nimede ruumi jaoks:
    • veendub, et Secret ei ole selles nimespetsiifis:
      • kui kohalik Secret on olemas — kustutab selle kubectl delete;
      • kui kohalik Secret ei leidu — ei tee midagi.

Go? Bash! Tere tulemast shell-operatori (ülevaade ja ettekande video KubeCon EU'2020-lt)

Bash'i algoritmi rakendamine saate alla laadida meie näidiste hoidlast.

Nii suudame luua lihtsa Kubernetes'i juhtkontrolleri, kasutades 35 rida YAML-konfide ja umbes sama palju Bash'i koodi! Shell-operaatori ülesanne on need kokku siduda.

Kuid saladuste kopeerimine ei ole ainus rakendus, millega utiliit tegeleb. Siin on veel paar näidet, mis näitavad, milleks see võimeline on.

Näide 1: muudatuste tegemine ConfigMap'is

Vaadakem Deployment'i, mis koosneb kolmest pod'ist. Pod'id kasutavad ConfigMap'i teatud konfigureerimise talletamiseks. Pod'ide käivitamise ajal oli ConfigMap mingis seisundis (nimetame seda v.1). Seega kasutavad kõik pod'id just seda ConfigMap'i versiooni.

Nüüd eeldame, et ConfigMap on muutunud (v.2). Kuid pod'id jätkuvalt kasutavad vana ConfigMap'i versiooni (v.1):

Go? Bash! Tere tulemast shell-operatori (ülevaade ja ettekande video KubeCon EU'2020-lt)

Kuidas saavutada, et nad üleminek uuele ConfigMap'ile (v.2)? Vastus on lihtne: kasutada templati. Lisame annotatsiooni koos kontrollsummaga sektsiooni template Deployment'i konfiguratsioon:

Go? Bash! Tere tulemast shell-operatori (ülevaade ja ettekande video KubeCon EU'2020-lt)

Tulemusena on kõigis pod'ides selline kontrollsumma, nagu Deployment'il. Nüüd tuleb lihtsalt annotatsiooni uuendada, kui ConfigMap muutub. Ja shell-operator on selle puhul suurepärane lahendus. Kõik, mis on vajalik — programmeerida hook, mis registreerib end ConfigMap'i ning uuendab kontrollsummat.

Kui kasutaja teeb ConfigMap'is muudatusi, märkab shell-operator neid ning arvutab kontrollsummat uuesti. Seejärel astub mängu Kubernetes'i maagia: orkestreerija tapab pod'i, loob uue, ootab, kuni see muutub Valmis, ja liigub edasi. Tulemusena sünkroniseeritakse Deployment ja liigub uuele ConfigMap'i versioonile.

Go? Bash! Tere tulemast shell-operatori (ülevaade ja ettekande video KubeCon EU'2020-lt)

Näide 2: töö Custom Resource Definitions'iga

Nagu teada, võimaldab Kubernetes luua kohandatud tüüpe (kinds) objekte. Näiteks, võib luua tüübi MysqlDatabase.Oletame, et sellel tüübil on kaks metadata-parameetrit: name ja namespace.

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

Meil on Kubernetes klastri, millel on erinevad nimeruumi, kus saame luua MySQL andmebaase. Sel juhul saab shell-operatorit kasutada ressursside jälgimiseks MysqlDatabase., nende ühenduste jälgimiseks MySQL serveriga ja soovitud ning jälgitud klastriseisundite sünkroniseerimiseks.

Go? Bash! Tere tulemast shell-operatori (ülevaade ja ettekande video KubeCon EU'2020-lt)

Näide 3: klastrivõrgu jälgimine

Nagu teada, on ping'i kasutamine kõige lihtsam viis võrgu jälgimiseks. Selles näites näitame, kuidas sellist jälgimist rakendada shell-operatori abil.

Esiteks on vaja abonente registreerida sõlmedele. Shell-operator vajab iga sõlme nime ja IP-aadressi. Nende abil saavad nad neid sõlmi pingida.

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: "* * * * *"

Parameeter executeHookOnEvent: [] takistab hüüde käivitamist vastusena igasugustele sündmustele (st sõlmede muutmise, lisamise või kustutamise vastusena). Siiski käivitub ta (ja uuendab sõlmede loetelu) ajakava järgi — iga minut, nagu on ette nähtud väljas schedule.

Nüüd tekib küsimus, kuidas me saame teada probleemidest, nagu pakettide kaotus? Vaatame koodile:

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
}

Me käime läbi sõlmede loendi, saame nende nimed ja IP-aadressid, pingime ja edastame tulemused Prometheusse. Shell-operator oskab eksportida mõõdikud Prometheusesse, salvestades need faili, mis asub keskkonnamuutuja $METRICS_PATH.

Nii see abil saab teha operaatorit lihtsaks võrgu jälgimiseks klastris.

Queue mehhanism

See artikkel oleks puudulik, kui ei mainiks veel üht olulist mehhanismi, mis on integreeritud shell-operatorisse. Kujutage ette, et see käivitab mingi huki vastusena klastris toimunud sündmusele.

  • Mis juhtub, kui samal ajal klastris toimub veel üks sündmus?
  • Kas shell-operator käivitab veel ühe huki eksemplari?
  • Ja mis siis, kui klastris toimub korraga, ütleme, viis sündmust?
  • Kas shell-operator töötleb neid paralleelselt?
  • Ent kuidas on lood ressursside, nagu mälu ja CPU, tarbimisega?

Õnneks on shell-operatoris olemas sisseehitatud järjekordade mehhanism. Kõik sündmused paigutatakse järjekorda ja töödeldakse järjestikku.

Illustreerime seda näidete abil. Oletame, et meil on kaks hooki. Esimene sündmus tõmmatakse esimeselt hookilt. Pärast töötlemise lõpetamist edeneb järjekord edasi. Järgmised kolm sündmust suunatakse teisele hookile — need tõmmatakse järjekorrast ja edastatakse sellele 'kogusena'. Seega hook saab sündmuste massiivi — või täpsemalt öeldes, sidumise konteksti massiivi.

Samuti saab neid sündmusi kombineerida ühte suurde. Selle eest vastutab parameeter grupp sidumise konfiguratsioonis.

Go? Bash! Tere tulemast shell-operatori (ülevaade ja ettekande video KubeCon EU'2020-lt)

Saate luua suvalise arvu järjekordi/hooke ja nende erinevaid kombinatsioone. Näiteks võib üks järjekord töötada kahe hookiga või vastupidi.

Go? Bash! Tere tulemast shell-operatori (ülevaade ja ettekande video KubeCon EU'2020-lt)

Kõik, mida tuleb teha, on vastavalt seadistada queue sidumise konfiguratsiooni väli. Kui järjekorra nime ei ole määratud, käivitatakse hook vaikimisi järjekorras (default). Selline järjekordade mehhanism lahendab täielikult kõik ressursihalduse probleemid hookidega töötamisel.

Kokkuvõte

Rääkisime, mis on shell-operator, näitasime, kuidas selle abil saab kiiresti ja vaevatasu luua Kubernetes'e operaattoreid, ning tõime esile mitmeid näiteid selle kasutamisest.

Üksikasjalik teave shell-operatori kohta ning lühike juhend selle kasutamiseks on saadaval vastavas GitHubi repostaariumist. Ära kõhkle meiega küsimuste osas ühendust võtta: neid saab arutada eraldi Telegrami grupis (vene keeles) või selles foorumis (inglise keeles).

Ja kui see meeldis - oleme alati rõõmsad uutest issues/PR/tähtedest GitHubis, kus muide, leiad ka teisi huvitavaid projekte. Nendest tasub esile tõsta addon-operator, mis on shell-operatori vanem vend. See utiliit kasutab Helm'i chart'e laienduste installimiseks, suudab edastada uuendusi ja jälgida erinevaid parameetreid/väärtuseid chart'ides, kontrollib chart'ide installimise protsessi ja suudab neid modifitseerida vastusena klastris toimuvatele sündmustele.

Go? Bash! Tere tulemast shell-operatori (ülevaade ja ettekande video KubeCon EU'2020-lt)

Video ja slaidid

Esituse video (~23 minutit):

Vaata videot

Ettekande esitlus:

P.S.

Lugege ka meie blogist:

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster