Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan KubeCon EU'2020)

Dit jier wie de wichtichste Jeropeeske Kubernetes-konferinsje - KubeCon + CloudNativeCon Europe 2020 - firtueel. Sa'n wiziging yn opmaak hat ús lykwols net hindere om ús lang plande rapport "Gean? Bash! Moetsje de Shell-operator" wijd oan ús Open Source-projekt shell-operator.

Dit artikel, ynspirearre troch it petear, presintearret in oanpak foar it ferienfâldigjen fan it proses fan it meitsjen fan operators foar Kubernetes en lit sjen hoe't jo jo eigen kinne meitsje mei minimale ynspanning mei in shell-operator.

Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan KubeCon EU'2020)

Yntroduksje fideo fan it rapport (~ 23 minuten yn it Ingelsk, merkber ynformatyfr dan it artikel) en it haadúttreksel derút yn tekstfoarm. Go!

By Flant optimalisearje en automatisearje wy hieltyd alles. Hjoed sille wy prate oer in oar spannend konsept. Moetsje: cloud-native shell scripting!

Litte wy lykwols begjinne mei de kontekst wêryn dit alles bart: Kubernetes.

Kubernetes API en controllers

De API yn Kubernetes kin fertsjintwurdige wurde as in soarte fan triemserver mei mappen foar elk type objekt. Objekten (boarnen) op dizze tsjinner wurde fertsjintwurdige troch YAML-bestannen. Derneist hat de tsjinner in basis API wêrmei jo trije dingen kinne dwaan:

  • krije boarne troch syn soarte en namme;
  • wikselje boarne (yn dit gefal bewarret de tsjinner allinich "korrekte" objekten - alle ferkeard foarme of bedoeld foar oare mappen wurde ferwidere);
  • spoar foar de boarne (yn dit gefal krijt de brûker fuortendaliks syn aktuele/bywurke ferzje).

Sa fungearret Kubernetes as in soarte fan bestânstsjinner (foar YAML-manifesten) mei trije basismetoaden (ja, eins binne d'r oaren, mar wy sille se foar no weilitte).

Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan KubeCon EU'2020)

It probleem is dat de tsjinner allinich ynformaasje kin opslaan. Om it te wurkjen hawwe jo nedich kontrôler - it twadde wichtichste en fûnemintele konsept yn 'e wrâld fan Kubernetes.

D'r binne twa haadtypen fan controllers. De earste nimt ynformaasje fan Kubernetes, ferwurket it neffens nestede logika, en jout it werom nei K8s. De twadde nimt ynformaasje fan Kubernetes, mar, yn tsjinstelling ta it earste type, feroaret de steat fan guon eksterne boarnen.

Litte wy it proses fan it meitsjen fan in ynset yn Kubernetes in tichterby besjen:

  • Deployment Controller (ynklusyf yn kube-controller-manager) ûntfangt ynformaasje oer ynset en makket in ReplicaSet.
  • ReplicaSet makket twa replika's (twa pods) basearre op dizze ynformaasje, mar dizze pods binne noch net pland.
  • De planner plant pods en foeget node-ynformaasje ta oan har YAML's.
  • Kubelets meitsje feroarings oan in eksterne boarne (sizze Docker).

Dan wurdt dizze hiele folchoarder werhelle yn omkearde folchoarder: de kubelet kontrolearret de konteners, berekkent de status fan de pod en stjoert it werom. De ReplicaSet-controller ûntfangt de status en fernijt de steat fan 'e replika-set. Itselde bart mei de Deployment Controller en de brûker krijt op it lêst de bywurke (aktuele) status.

Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan KubeCon EU'2020)

Shell-operator

It docht bliken dat Kubernetes is basearre op it mienskiplike wurk fan ferskate controllers (Kubernetes operators binne ek controllers). De fraach ûntstiet, hoe meitsje jo eigen operator mei minimale ynspanning? En hjir komt dejinge dy't wy ûntwikkele hawwe ta de rêding shell-operator. It lit systeembehearders har eigen útspraken meitsje mei fertroude metoaden.

Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan KubeCon EU'2020)

Ienfâldich foarbyld: kopiearje geheimen

Litte wy nei in ienfâldich foarbyld sjen.

Litte wy sizze dat wy in Kubernetes-kluster hawwe. It hat in nammeromte default mei wat geheim mysecret. Dêrneist binne der oare nammeromten yn it kluster. Guon fan harren hawwe in spesifyk label oan har. Us doel is om Secret te kopiearjen yn nammeromten mei in label.

De taak wurdt komplisearre troch it feit dat nije nammeromten kinne ferskine yn it kluster, en guon fan harren kinne hawwe dit label. Oan 'e oare kant, as it label is wiske, moat Secret ek wiske wurde. Dêrnjonken kin it Geheim sels ek feroarje: yn dit gefal moat it nije Geheim nei alle nammeromten mei labels kopiearre wurde. As Secret per ûngelok wiske wurdt yn in nammeromte, moat ús operator it fuortendaliks weromsette.

No't de taak formulearre is, is it tiid om it te begjinnen mei it ymplementearjen mei de shell-operator. Mar earst is it wurdich in pear wurden te sizzen oer de shell-operator sels.

Hoe shell-operator wurket

Lykas oare workloads yn Kubernetes, rint shell-operator yn syn eigen pod. Yn dizze pod yn 'e map /hooks útfierbere triemmen wurde opslein. Dit kinne skripts wêze yn Bash, Python, Ruby, ensfh. Wy neame sokke útfierbere triemmen haken (haken).

Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan KubeCon EU'2020)

Shell-operator abonnearret op Kubernetes-eveneminten en rint dizze haken yn reaksje op dy eveneminten dy't wy nedich binne.

Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan KubeCon EU'2020)

Hoe wit de shell-operator hokker heak te rinnen en wannear? It punt is dat elke heak twa stadia hat. Tidens it opstarten rint de shell-operator alle haken mei in argumint --config Dit is de konfiguraasje poadium. En dêrnei wurde heakken op 'e normale manier lansearre - yn reaksje op' e eveneminten dêr't se oan hechte binne. Yn it lêste gefal krijt de heak de binende kontekst (binende kontekst) - gegevens yn JSON-formaat, dêr't wy hjirûnder yn mear detail oer prate.

It meitsjen fan in operator yn Bash

No binne wy ​​klear foar ymplemintaasje. Om dit te dwaan, moatte wy twa funksjes skriuwe (wy advisearje trouwens de bibleteek shell_lib, wat skriuwhaken yn Bash sterk ferienfâldiget):

  • de earste is nedich foar de konfiguraasje poadium - it toant de binende kontekst;
  • de twadde befettet de wichtichste logika fan 'e heak.

#!/bin/bash

source /shell_lib.sh

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

function __main__() {
  # THE LOGIC
}

hook::run "$@"

De folgjende stap is om te besluten hokker objekten wy nedich binne. Yn ús gefal moatte wy folgje:

  • boarne geheim foar feroarings;
  • alle nammeromten yn it kluster, sadat jo witte wa't in label oan har hawwe;
  • doelgeheimen om te soargjen dat se allegear syngronisearje mei it boarnegeheim.

Abonnearje op de geheime boarne

Binende konfiguraasje foar it is frij simpel. Wy jouwe oan dat wy ynteressearre binne yn Secret mei de namme mysecret yn nammeromte default:

Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan 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

As resultaat sil de heak ûntslein wurde as it boarnegeheim feroaret (src_secret) en ûntfange de folgjende binende kontekst:

Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan KubeCon EU'2020)

Sa't jo sjen kinne, befettet it de namme en it hiele objekt.

Byhâlden fan nammeromten

No moatte jo abonnearje op nammeromten. Om dit te dwaan, spesifisearje wy de folgjende binende konfiguraasje:

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

Sa't jo sjen kinne, is in nij fjild ferskynd yn 'e konfiguraasje mei de namme jqFilter. Lykas syn namme al fermoeden docht, jqFilter filtert alle ûnnedige ynformaasje en makket in nij JSON-objekt mei de fjilden dy't fan belang binne foar ús. In heak mei in ferlykbere konfiguraasje sil de folgjende binende kontekst ûntfange:

Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan KubeCon EU'2020)

It befettet in array filterResults foar elke nammeromte yn it kluster. Boolean fariabele hasLabel jout oan oft in label oan in opjûne nammeromte ferbûn is. Selektor keepFullObjectsInMemory: false jout oan dat it net nedich is om folsleine objekten yn it ûnthâld te hâlden.

Tracking doel geheimen

Wy abonnearje op alle geheimen dy't in oantsjutte annotaasje hawwe managed-secret: "yes" (dit binne ús doel 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

Yn dit gefal jqFilter filtert alle ynformaasje útsein de nammeromte en parameter resourceVersion. De lêste parameter waard trochjûn oan de annotaasje by it meitsjen fan it geheim: it makket it mooglik om ferzjes fan geheimen te fergelykjen en by de tiid te hâlden.

In op dizze manier konfigureare haak sil, as it wurdt útfierd, de hjirboppe beskreaune trije binende konteksten ûntfange. Se kinne beskôge wurde as in soarte fan momintopname (snapshot) kluster.

Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan KubeCon EU'2020)

Op grûn fan al dizze ynformaasje kin in basisalgoritme ûntwikkele wurde. It iterearret oer alle nammeromten en:

  • if hasLabel saken true foar de hjoeddeiske nammeromte:
    • fergeliket it globale geheim mei it lokale:
      • as se itselde binne, docht it neat;
      • as se ferskille - útfiert kubectl replace of create;
  • if hasLabel saken false foar de hjoeddeiske nammeromte:
    • soarget derfoar dat Secret net yn de opjûne nammeromte is:
      • as it lokale geheim oanwêzich is, wiskje it mei kubectl delete;
      • as it lokale geheim net ûntdutsen wurdt, docht it neat.

Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan KubeCon EU'2020)

Implementaasje fan it algoritme yn Bash kinne jo downloade yn ús repositories mei foarbylden.

Dat is hoe't wy in ienfâldige Kubernetes-controller kinne meitsje mei 35 rigels fan YAML-konfiguraasje en sawat itselde bedrach fan Bash-koade! De taak fan 'e shell-operator is om se te ferbinen.

Geheimen kopiearje is lykwols net it ienige gebiet fan tapassing fan it nut. Hjir binne noch in pear foarbylden om sjen te litten wat hy yn steat is.

Foarbyld 1: Feroarings oanmeitsje oan ConfigMap

Litte wy sjen nei in ynset besteande út trije pods. Pods brûke ConfigMap om wat konfiguraasje op te slaan. Doe't de pods waarden lansearre, wie ConfigMap yn in bepaalde steat (lite wy neame it v.1). Dêrtroch brûke alle pods dizze bepaalde ferzje fan ConfigMap.

No lit ús oannimme dat de ConfigMap is feroare (v.2). De pods sille lykwols de foarige ferzje fan ConfigMap (v.1) brûke:

Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan KubeCon EU'2020)

Hoe kin ik krije se te wikseljen nei de nije ConfigMap (v.2)? It antwurd is ienfâldich: brûk in sjabloan. Litte wy in kontrôlesum-annotaasje tafoegje oan 'e seksje template Ynsetkonfiguraasjes:

Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan KubeCon EU'2020)

As gefolch, dizze kontrôlesum wurdt registrearre yn alle pods, en it sil wêze itselde as dy fan Deployment. No moatte jo gewoan de annotaasje bywurkje as de ConfigMap feroaret. En de shell-operator komt yn dit gefal goed fan pas. Alles wat jo hoege te dwaan is programmearje in heak dy't sil abonnearje op de ConfigMap en bywurkje de kontrôlesum.

As de brûker feroarings oanmakket oan 'e ConfigMap, sil de shell-operator se opmerke en de kontrôlesum opnij berekkenje. Dêrnei sil de magy fan Kubernetes yn spiel komme: de orkestrator sil de pod deadzje, in nije meitsje, wachtsje oant it wurdt Ready, en giet troch nei de folgjende. As gefolch sil Deployment syngronisearje en wikselje nei de nije ferzje fan ConfigMap.

Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan KubeCon EU'2020)

Foarbyld 2: Wurkje mei Custom Resource Definitions

Lykas jo witte, kinne Kubernetes jo oanpaste soarten objekten meitsje. Jo kinne bygelyks soarte meitsje MysqlDatabase. Litte wy sizze dat dit type twa metadataparameters hat: name и namespace.

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

Wy hawwe in Kubernetes-kluster mei ferskate nammeromten wêryn wy MySQL-databases kinne oanmeitsje. Yn dit gefal kin shell-operator brûkt wurde om boarnen te folgjen MysqlDatabase, ferbine se mei de MySQL-tsjinner en syngronisearje de winske en waarnommen steaten fan it kluster.

Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan KubeCon EU'2020)

Foarbyld 3: Cluster Network Monitoring

Lykas jo witte, is it brûken fan ping de ienfâldichste manier om in netwurk te kontrolearjen. Yn dit foarbyld sille wy sjen litte hoe't sokke tafersjoch ymplementearje mei help fan shell-operator.

Earst moatte jo abonnearje op knopen. De shell-operator hat de namme en IP-adres fan elke knooppunt nedich. Mei har help sil hy dizze knopen pingelen.

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: [] foarkomt dat de heak rint as antwurd op elk barren (dat is, as antwurd op it feroarjen, tafoegjen, wiskjen fan knopen). Lykwols, hy sil rinne (en bywurkje de list mei knopen) Planne - elke minút, lykas foarskreaun troch it fjild schedule.

No komt de fraach op, hoe witte wy krekt oer problemen lykas pakketferlies? Litte wy nei de koade sjen:

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
}

Wy iterearje troch de list mei knooppunten, krije har nammen en IP-adressen, ping se en stjoere de resultaten nei Prometheus. Shell-operator kin metriken eksportearje nei Prometheus, bewarje se yn in bestân dat leit neffens it paad dat spesifisearre is yn 'e omjouwingsfariabele $METRICS_PATH.

Hjir sa jo kinne in operator meitsje foar ienfâldige netwurkmonitoring yn in kluster.

Wachtrige meganisme

Dit artikel soe ûnfolslein wêze sûnder in oar wichtich meganisme te beskriuwen ynboud yn 'e shell-operator. Stel jo foar dat it in soarte fan heak útfiert yn reaksje op in evenemint yn it kluster.

  • Wat bart der as der tagelyk wat bart yn it kluster? ien mear barren?
  • Sil shell-operator in oare eksimplaar fan 'e heak útfiere?
  • Wat as bygelyks fiif eveneminten tagelyk yn it kluster barre?
  • Sil de shell-operator se parallel ferwurkje?
  • Hoe sit it mei konsumeare boarnen lykas ûnthâld en CPU?

Gelokkich, shell-operator hat in ynboude wachtrige meganisme. Alle eveneminten wurde wachtrige en ferwurke sequentially.

Litte wy dit yllustrearje mei foarbylden. Litte wy sizze dat wy twa haken hawwe. It earste evenemint giet nei de earste heak. Sadree't de ferwurking foltôge is, giet de wachtrige foarút. De folgjende trije eveneminten wurde omlaat nei de twadde heak - se wurde fuorthelle út 'e wachtrige en ynfierd yn it yn in "bondel". Dat is hook ûntfangt in array fan eveneminten - of, krekter, in array fan binende konteksten.

Ek dizze eveneminten kinne wurde kombinearre yn ien grutte. De parameter is ferantwurdlik foar dit group yn de binende konfiguraasje.

Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan KubeCon EU'2020)

Jo kinne in oantal wachtrijen / heakjes oanmeitsje en har ferskate kombinaasjes. Bygelyks, ien wachtrige kin wurkje mei twa heakjes, of oarsom.

Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan KubeCon EU'2020)

Alles wat jo hoege te dwaan is it fjild dêrop konfigurearje queue yn de binende konfiguraasje. As in wachtrige namme net oantsjutte is, rint de heak op de standert wachtrige (default). Dit wachtrige meganisme kinne jo folslein oplosse alle boarne behear problemen by it wurkjen mei heakjes.

konklúzje

Wy hawwe útlein wat in shell-operator is, lieten sjen hoe't it kin wurde brûkt om fluch en maklik Kubernetes-operators te meitsjen, en joegen ferskate foarbylden fan it gebrûk.

Detaillearre ynformaasje oer de shell-operator, lykas in rappe tutorial oer hoe't jo it brûke, is beskikber yn 'e oerienkommende repositories op GitHub. Nim kontakt mei ús op mei fragen: jo kinne se beprate yn in spesjale Telegram groep (yn it Russysk) of yn dit foarum (yn it Ingelsk).

En as jo it leuk fine, binne wy ​​altyd bliid om nije problemen / PR / stjerren te sjen op GitHub, wêr't jo trouwens oaren kinne fine nijsgjirrige projekten. Under harren is it wurdich te markearjen addon operator, dat is de grutte broer fan shell-operator. Dit hulpprogramma brûkt Helm-diagrammen om tafoegings te ynstallearjen, kin updates leverje en ferskate diagramparameters / wearden kontrolearje, kontrolearret it ynstallaasjeproses fan charts, en kin se ek wizigje yn reaksje op eveneminten yn it kluster.

Gean? Bash! Moetsje de shell-operator (resinsje en fideoferslach fan KubeCon EU'2020)

Fideo's en dia's

Fideo fan 'e foarstelling (~ 23 minuten):


Presintaasje fan it rapport:

PS

Lês ek op ús blog:

Boarne: www.habr.com

Add a comment