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. .
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.

Tutvustame (~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).

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.

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 . See vĂ”imaldab sĂŒsteemiadministraatoritel luua oma operaatorid, kasutades tuttavaid meetodeid.
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).

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

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 , 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:

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:

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:

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.

Kogu selle teabe pÔhjal saab vÀlja töötada pÔhialgoritmi. See lÀbib kÔik nimede ruumid ja:
- kui
hasLabelvÀÀrtustruepraeguse nimede ruumi jaoks:- vÔrdleb globaalset saladust kohaliku:
- kui nad on samad â ei tee midagi;
- kui nad erinevad â tĂ€idab
kubectl asendadavÔicreate;
- vÔrdleb globaalset saladust kohaliku:
- kui
hasLabelvÀÀrtusfalsepraeguse 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.
- kui kohalik Secret on olemas â kustutab selle
- veendub, et Secret ei ole selles nimespetsiifis:

saate alla laadida meie .
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):

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

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.

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.

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.
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.

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.

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 . Ăra kĂ”hkle meiega kĂŒsimuste osas ĂŒhendust vĂ”tta: neid saab arutada eraldi (vene keeles) vĂ”i (inglise keeles).
Ja kui see meeldis - oleme alati rÔÔmsad uutest issues/PR/tĂ€htedest GitHubis, kus muide, leiad ka teisi . Nendest tasub esile tĂ”sta , 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.

Video ja slaidid
Esituse video (~23 minutit):

Ettekande esitlus:
P.S.
Lugege ka meie blogist:
- «»;
- «»;
- «»;
- «.
Allikas: habr.com
