Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de KubeCon EU'2020)

Ĉi-jare, la ĉefa eŭropa Kubernetes-konferenco - KubeCon + CloudNativeCon Europe 2020 - estis virtuala. Tamen tia ŝanĝo en formato ne malhelpis nin liveri nian longe planitan raporton “Ĉu? Bato! Meet the Shell-operator” dediĉita al nia Malfermfonta projekto ŝelo-funkciigisto.

Ĉi tiu artikolo, inspirita de la parolado, prezentas aliron por simpligi la procezon de kreado de funkciigistoj por Kubernetes kaj montras kiel vi povas fari vian propran per minimuma peno uzante ŝel-funkciigiston.

Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de KubeCon EU'2020)

Enkondukante video de la raporto (~23 minutoj en la angla, videble pli informa ol la artikolo) kaj la ĉefa eltiraĵo el ĝi en tekstformo. Iru!

Ĉe Flant ni konstante optimumigas kaj aŭtomatigas ĉion. Hodiaŭ ni parolos pri alia ekscita koncepto. Renkontu: nubo-denaska ŝelo-skripto!

Tamen, ni komencu per la kunteksto en kiu ĉio ĉi okazas: Kubernetes.

Kubernetes API kaj regiloj

La API en Kubernetes povas esti reprezentita kiel speco de dosierservilo kun dosierujoj por ĉiu speco de objekto. Objektoj (rimedoj) sur ĉi tiu servilo estas reprezentitaj per YAML-dosieroj. Krome, la servilo havas bazan API, kiu permesas vin fari tri aferojn:

  • akiri rimedo laŭ sia speco kaj nomo;
  • ŝanĝi rimedo (en ĉi tiu kazo, la servilo stokas nur "ĝustajn" objektojn - ĉiuj malĝuste formitaj aŭ destinitaj por aliaj dosierujoj estas forĵetitaj);
  • trako por la rimedo (en ĉi tiu kazo, la uzanto tuj ricevas ĝian aktualan/ĝisdatigitan version).

Tiel, Kubernetes funkcias kiel speco de dosierservilo (por YAML-manifestoj) kun tri bazaj metodoj (jes, efektive ekzistas aliaj, sed ni preterlasos ilin nuntempe).

Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de KubeCon EU'2020)

La problemo estas, ke la servilo nur povas stoki informojn. Por ke ĝi funkciu, vi bezonas regilo - la dua plej grava kaj fundamenta koncepto en la mondo de Kubernetes.

Estas du ĉefaj specoj de regiloj. La unua prenas informojn de Kubernetes, prilaboras ĝin laŭ nestita logiko, kaj resendas ĝin al K8s. La dua prenas informojn de Kubernetes, sed, male al la unua, ŝanĝas la staton de iuj eksteraj rimedoj.

Ni rigardu pli detale la procezon de kreado de Deplojo en Kubernetes:

  • Deploja regilo (inkluzivita en kube-controller-manager) ricevas informojn pri Deployment kaj kreas ReplicaSet.
  • ReplicaSet kreas du kopiojn (du podojn) surbaze de ĉi tiu informo, sed ĉi tiuj balgoj ankoraŭ ne estas planitaj.
  • La planisto planas podojn kaj aldonas nodajn informojn al siaj YAMLoj.
  • Kubelets faras ŝanĝojn al ekstera rimedo (diru Docker).

Tiam ĉi tiu tuta sinsekvo estas ripetita en inversa sinsekvo: la kubelet kontrolas la ujojn, kalkulas la staton de la balgo kaj resendas ĝin. La ReplicaSet-regilo ricevas la statuson kaj ĝisdatigas la staton de la kopiaro. La sama afero okazas kun la Deploja Regilo kaj la uzanto finfine ricevas la ĝisdatigitan (nuntempan) statuson.

Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de KubeCon EU'2020)

Ŝelo-funkciigisto

Rezultas, ke Kubernetes baziĝas sur la komuna laboro de diversaj regiloj (Kubernetes-funkciigistoj ankaŭ estas regiloj). La demando ŝprucas, kiel krei vian propran funkciigiston kun minimuma peno? Kaj ĉi tie tiu, kiun ni evoluigis, venas al la savo ŝelo-funkciigisto. Ĝi permesas al sistemadministrantoj krei siajn proprajn deklarojn uzante konatajn metodojn.

Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de KubeCon EU'2020)

Simpla ekzemplo: kopii sekretojn

Ni rigardu simplan ekzemplon.

Ni diru, ke ni havas Kubernetes-grupon. Ĝi havas nomspacon default kun iu Sekreto mysecret. Krome, estas aliaj nomspacoj en la areto. Kelkaj el ili havas specifan etikedon alfiksita al ili. Nia celo estas kopii Sekreton en nomspacojn kun etikedo.

La tasko estas malfaciligita pro tio, ke novaj nomspacoj povas aperi en la areto, kaj kelkaj el ili povas havi ĉi tiun etikedon. Aliflanke, kiam la etikedo estas forigita, Sekreto ankaŭ devus esti forigita. Aldone al tio, la Sekreto mem ankaŭ povas ŝanĝiĝi: en ĉi tiu kazo, la nova Sekreto devas esti kopiita al ĉiuj nomspacoj kun etikedoj. Se Sekreto estas hazarde forigita en iu nomspaco, nia operatoro devus restarigi ĝin tuj.

Nun kiam la tasko estis formulita, estas tempo komenci efektivigi ĝin uzante la ŝelon-funkciigiston. Sed unue indas diri kelkajn vortojn pri la ŝel-funkciigisto mem.

Kiel shell-operator funkcias

Kiel aliaj laborŝarĝoj en Kubernetes, shell-operator funkcias en sia propra pod. En ĉi tiu pod en la dosierujo /hooks ruleblaj dosieroj estas konservitaj. Ĉi tiuj povas esti skriptoj en Bash, Python, Ruby, ktp. Ni nomas tiajn ruleblajn dosierojn hokoj (hokoj).

Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de KubeCon EU'2020)

Ŝelo-funkciigisto abonas Kubernetes-eventojn kaj kuras ĉi tiujn hokojn en respondo al tiuj eventoj, kiujn ni bezonas.

Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de KubeCon EU'2020)

Kiel la ŝelo-funkciigisto scias kiun hokon ruli kaj kiam? La punkto estas, ke ĉiu hoko havas du stadiojn. Dum ekfunkciigo, la ŝelo-funkciigisto kuras ĉiujn hokojn kun argumento --config Ĉi tiu estas la agorda etapo. Kaj post ĝi, hokoj estas lanĉitaj laŭ la normala maniero - en respondo al la eventoj al kiuj ili estas ligitaj. En ĉi-lasta kazo, la hoko ricevas la devigan kuntekston (liga kunteksto) - datumoj en formato JSON, pri kiuj ni parolos pli detale sube.

Farante funkciigiston en Bash

Nun ni estas pretaj por efektivigo. Por fari tion, ni devas skribi du funkciojn (cetere, ni rekomendas biblioteko shell_lib, kiu multe simpligas skribhokojn en Bash):

  • the first is needed for the configuration stage - ĝi montras la devigan kuntekston;
  • la dua enhavas la ĉefan logikon de la hoko.

#!/bin/bash

source /shell_lib.sh

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

function __main__() {
  # THE LOGIC
}

hook::run "$@"

La sekva paŝo estas decidi kiajn objektojn ni bezonas. En nia kazo, ni devas spuri:

  • fontsekreto por ŝanĝoj;
  • ĉiuj nomspacoj en la areto, por ke vi sciu kiuj havas etikedon alfiksita al ili;
  • celsekretoj por certigi, ke ili ĉiuj sinkronigas kun la fontsekreto.

Abonu la sekretan fonton

Biliga agordo por ĝi estas sufiĉe simpla. Ni indikas, ke ni interesiĝas pri Sekreto kun la nomo mysecret en nomspaco default:

Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de 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

Kiel rezulto, la hoko estos ekigita kiam la fontsekreto ŝanĝiĝas (src_secret) kaj ricevu la sekvan devigan kuntekston:

Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de KubeCon EU'2020)

Kiel vi povas vidi, ĝi enhavas la nomon kaj la tutan objekton.

Konservado de nomspacoj

Nun vi devas aboni nomspacojn. Por fari tion, ni specifas la sekvan ligan agordon:

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

Kiel vi povas vidi, nova kampo aperis en la agordo kun la nomo jqFiltrilo. Kiel ĝia nomo sugestas, jqFilter filtras ĉiujn nenecesajn informojn kaj kreas novan JSON-objekton kun la kampoj, kiuj interesas nin. Hoko kun simila agordo ricevos la sekvan ligan kuntekston:

Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de KubeCon EU'2020)

Ĝi enhavas tabelon filterResults por ĉiu nomspaco en la areto. Bulea variablo hasLabel indikas ĉu etikedo estas alfiksita al donita nomspaco. Elektilo keepFullObjectsInMemory: false indikas ke ne necesas konservi kompletajn objektojn en memoro.

Spurado de celsekretoj

Ni abonas ĉiujn Sekretojn, kiuj havas komentarion specifitan managed-secret: "yes" (ĉi tiuj estas nia celo 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

En ĉi tiu kazo jqFilter filtras ĉiujn informojn krom la nomspaco kaj parametro resourceVersion. La lasta parametro estis transdonita al la komentario dum kreado de la sekreto: ĝi permesas vin kompari versiojn de sekretoj kaj konservi ilin ĝisdatigitaj.

Hoko agordita tiamaniere ricevos, kiam ekzekutita, la tri devigajn kuntekstojn priskribitajn supre. Ili povas esti konsiderataj kiel speco de momentfoto (ekrano) areto.

Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de KubeCon EU'2020)

Surbaze de ĉiuj ĉi tiuj informoj, baza algoritmo povas esti evoluigita. Ĝi ripetas super ĉiuj nomspacoj kaj:

  • se hasLabel aferoj true por la nuna nomspaco:
    • komparas la tutmondan sekreton kun la loka:
      • se ili estas samaj, ĝi faras nenion;
      • se ili malsamas - ekzekutas kubectl replacecreate;
  • se hasLabel aferoj false por la nuna nomspaco:
    • certigas, ke Sekreto ne estas en la donita nomspaco:
      • se la loka Sekreto ĉeestas, forigu ĝin uzante kubectl delete;
      • se la loka Sekreto ne estas detektita, ĝi faras nenion.

Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de KubeCon EU'2020)

Efektivigo de la algoritmo en Bash vi povas elŝuti en nia deponejoj kun ekzemploj.

Tiel ni povis krei simplan Kubernetes-regilon uzante 35 liniojn de YAML-agordo kaj proksimume la saman kvanton da Bash-kodo! La tasko de la ŝelo-funkciigisto estas ligi ilin kune.

Tamen, kopii sekretojn ne estas la sola areo de apliko de la utileco. Jen kelkaj pliaj ekzemploj por montri, kion li kapablas.

Ekzemplo 1: Farante ŝanĝojn al ConfigMap

Ni rigardu Deplojon konsistantan el tri balgoj. Pods uzas ConfigMap por stoki iun agordon. Kiam la podoj estis lanĉitaj, ConfigMap estis en certa stato (ni nomu ĝin v.1). Sekve, ĉiuj podoj uzas ĉi tiun apartan version de ConfigMap.

Nun ni supozu, ke la ConfigMap ŝanĝiĝis (v.2). Tamen, la podoj uzos la antaŭan version de ConfigMap (v.1):

Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de KubeCon EU'2020)

Kiel mi povas igi ilin ŝanĝi al la nova ConfigMap (v.2)? La respondo estas simpla: uzu ŝablonon. Ni aldonu kontrolsuman komenton al la sekcio template Deplojaj agordoj:

Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de KubeCon EU'2020)

Kiel rezulto, ĉi tiu kontrolsumo estos registrita en ĉiuj podoj, kaj ĝi estos la sama kiel tiu de Deplojo. Nun vi nur bezonas ĝisdatigi la komentarion kiam la ConfigMap ŝanĝiĝas. Kaj la ŝelo-funkciigisto utilas en ĉi tiu kazo. Ĉio, kion vi devas fari, estas programi hoko kiu abonos la ConfigMap kaj ĝisdatigos la kontrolsumon.

Se la uzanto faras ŝanĝojn al la ConfigMap, la ŝel-funkciigisto rimarkos ilin kaj rekalkulos la ĉeksumon. Post tio la magio de Kubernetes ekludos: la orkestro mortigos la balgon, kreos novan, atendos, ke ĝi fariĝos. Ready, kaj pluiras al la sekva. Kiel rezulto, Deployment sinkronigos kaj ŝanĝos al la nova versio de ConfigMap.

Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de KubeCon EU'2020)

Ekzemplo 2: Laborante kun Propraj Rimedaj Difinoj

Kiel vi scias, Kubernetes permesas krei kutimajn specojn de objektoj. Ekzemple, vi povas krei afablan MysqlDatabase. Ni diru, ke ĉi tiu tipo havas du metadatumajn parametrojn: name и namespace.

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

Ni havas Kubernetes-grupon kun malsamaj nomspacoj en kiuj ni povas krei MySQL-datumbazon. En ĉi tiu kazo shell-operator povas esti uzata por spuri rimedojn MysqlDatabase, ligante ilin al la MySQL-servilo kaj sinkronigante la deziratajn kaj observitajn statojn de la areto.

Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de KubeCon EU'2020)

Ekzemplo 3: Cluster Network Monitoring

Kiel vi scias, uzi ping estas la plej simpla maniero por kontroli reton. En ĉi tiu ekzemplo ni montros kiel efektivigi tian monitoradon per shell-operator.

Antaŭ ĉio, vi devos aboni nodojn. La ŝelfunkciigisto bezonas la nomon kaj IP-adreson de ĉiu nodo. Kun ilia helpo, li pingos ĉi tiujn nodojn.

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

Parametro executeHookOnEvent: [] malhelpas la hokon funkcii responde al iu ajn evento (tio estas, responde al ŝanĝado, aldono, forigo de nodoj). Tamen, li kuros (kaj ĝisdatigi la liston de nodoj) Planita - ĉiuminute, kiel preskribite de la kampo schedule.

Nun aperas la demando, kiel precize ni scias pri problemoj kiel paka perdo? Ni rigardu la kodon:

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
}

Ni ripetas la liston de nodoj, ricevas iliajn nomojn kaj IP-adresojn, ping ilin kaj sendas la rezultojn al Prometheus. Ŝelo-funkciigisto povas eksporti metrikojn al Prometheus, konservante ilin al dosiero situanta laŭ la vojo specifita en la mediovariablo $METRICS_PATH.

Ĉi tie vi povas fari funkciigiston por simpla reta monitorado en areto.

Mekanismo de vico

Ĉi tiu artikolo estus nekompleta sen priskribado de alia grava mekanismo konstruita en la ŝel-funkciigisto. Imagu, ke ĝi efektivigas ian hokon responde al evento en la areto.

  • Kio okazas se, samtempe, io okazas en la areto? unu pli evento?
  • Ĉu shell-operator ruligos alian okazon de la hoko?
  • Kio se, ekzemple, kvin eventoj okazas en la areto samtempe?
  • Ĉu la ŝelo-funkciigisto prilaboros ilin paralele?
  • Kio pri konsumitaj rimedoj kiel memoro kaj CPU?

Feliĉe, ŝelo-funkciigisto havas enkonstruitan vicmekanismon. Ĉiuj eventoj estas vicigitaj kaj procesitaj sinsekve.

Ni ilustru ĉi tion per ekzemploj. Ni diru, ke ni havas du hokojn. La unua evento iras al la unua hoko. Post kiam ĝia pretigo estas kompleta, la atendovico antaŭeniras. La sekvaj tri eventoj estas redirektitaj al la dua hoko - ili estas forigitaj el la atendovico kaj enigitaj en ĝin en "fasko". Tio estas hoko ricevas aron da eventoj — aŭ, pli precize, tabelo da liga kuntekstoj.

Ankaŭ ĉi tiuj eventoj povas esti kombinitaj en unu grandan. La parametro respondecas pri tio group en la deviga agordo.

Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de KubeCon EU'2020)

Vi povas krei ajnan nombron da vostoj/hokoj kaj iliaj diversaj kombinaĵoj. Ekzemple, unu vosto povas funkcii per du hokoj, aŭ inverse.

Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de KubeCon EU'2020)

Ĉio, kion vi devas fari, estas agordi la kampon laŭe queue en la deviga agordo. Se vostonomo ne estas specifita, la hoko funkcias sur la defaŭlta atendovico (default). Ĉi tiu vicmekanismo permesas vin tute solvi ĉiujn problemojn pri administrado de rimedoj kiam vi laboras kun hokoj.

konkludo

Ni klarigis kio estas ŝel-funkciigisto, montris kiel ĝi povas esti uzata por rapide kaj senpene krei Kubernetes-funkciigistojn, kaj donis plurajn ekzemplojn de ĝia uzo.

Detalaj informoj pri la ŝelo-funkciigisto, same kiel rapida lernilo pri kiel uzi ĝin, estas haveblaj en la responda deponejoj sur GitHub. Ne hezitu kontakti nin kun demandoj: vi povas diskuti ilin en speciala Telegrama grupo (en la rusa) aŭ en ĉi tiu forumo (en la angla).

Kaj se vi ŝatis ĝin, ni ĉiam ĝojas vidi novajn temojn/PR/stelojn en GitHub, kie, cetere, vi povas trovi aliajn. interesaj projektoj. Inter ili indas reliefigi aldon-funkciigisto, kiu estas la granda frato de shell-operator. Ĉi tiu utileco uzas Helm-diagramojn por instali aldonaĵojn, povas liveri ĝisdatigojn kaj monitori diversajn diagramajn parametrojn/valorojn, kontrolas la instalan procezon de leteroj, kaj ankaŭ povas modifi ilin responde al eventoj en la areto.

Ĉu iri? Bato! Renkontu la ŝel-funkciigiston (recenzo kaj videoraporto de KubeCon EU'2020)

Filmetoj kaj diapozitivoj

Video de la prezentado (~23 minutoj):


Prezento de la raporto:

PS

Legu ankaŭ en nia blogo:

fonto: www.habr.com

Aldoni komenton