Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo de KubeCon EU'2020)

Aquest any, la principal conferència europea de Kubernetes - KubeCon + CloudNativeCon Europe 2020 - va ser virtual. No obstant això, aquest canvi de format no ens va impedir lliurar el nostre informe llargament planificat “Va? Xoc! Meet the Shell-operator” dedicat al nostre projecte de codi obert operador de shell.

Aquest article, inspirat en la xerrada, presenta un enfocament per simplificar el procés de creació d'operadors per a Kubernetes i mostra com podeu fer el vostre amb el mínim esforç mitjançant un operador de shell.

Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo de KubeCon EU'2020)

Presentació vídeo del reportatge (~23 minuts en anglès, notablement més informatiu que l'article) i l'extracte principal en forma de text. Va!

A Flant ho optimitzem i ho automatitzem tot constantment. Avui parlarem d'un altre concepte apassionant. Trobar-se: script d'intèrpret d'ordres natiu del núvol!

Tanmateix, comencem pel context en què passa tot això: Kubernetes.

API i controladors de Kubernetes

L'API de Kubernetes es pot representar com una mena de servidor de fitxers amb directoris per a cada tipus d'objecte. Els objectes (recursos) d'aquest servidor estan representats per fitxers YAML. A més, el servidor té una API bàsica que us permet fer tres coses:

  • rebre recurs pel seu tipus i nom;
  • canvi recurs (en aquest cas, el servidor només emmagatzema objectes "correctes": es descarten tots els formats incorrectament o destinats a altres directoris);
  • pista per al recurs (en aquest cas, l'usuari rep immediatament la seva versió actual/actualitzada).

Així, Kubernetes actua com una mena de servidor de fitxers (per a manifests YAML) amb tres mètodes bàsics (sí, en realitat n'hi ha d'altres, però de moment els ometrem).

Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo de KubeCon EU'2020)

El problema és que el servidor només pot emmagatzemar informació. Per fer-ho funcionar cal controlador - el segon concepte més important i fonamental del món de Kubernetes.

Hi ha dos tipus principals de controladors. El primer pren informació de Kubernetes, la processa segons la lògica imbricada i la torna a K8s. El segon pren informació de Kubernetes, però, a diferència del primer tipus, canvia l'estat d'alguns recursos externs.

Fem una ullada més de prop al procés de creació d'un desplegament a Kubernetes:

  • Controlador de desplegament (inclòs a kube-controller-manager) rep informació sobre el desplegament i crea un ReplicaSet.
  • ReplicaSet crea dues rèpliques (dos pods) a partir d'aquesta informació, però aquests pods encara no estan programats.
  • El planificador programa pods i afegeix informació de nodes als seus YAML.
  • Kubelets fan canvis en un recurs extern (per exemple, Docker).

A continuació, tota aquesta seqüència es repeteix en ordre invers: el kubelet comprova els contenidors, calcula l'estat de la beina i l'envia de tornada. El controlador ReplicaSet rep l'estat i actualitza l'estat del conjunt de rèpliques. El mateix passa amb el controlador de desplegament i l'usuari finalment obté l'estat actualitzat (actual).

Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo de KubeCon EU'2020)

Operador de Shell

Resulta que Kubernetes es basa en el treball conjunt de diversos controladors (els operadors de Kubernetes també són controladors). Sorgeix la pregunta, com crear el vostre propi operador amb el mínim esforç? I aquí el que hem desenvolupat ve al rescat operador de shell. Permet als administradors del sistema crear les seves pròpies declaracions utilitzant mètodes coneguts.

Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo de KubeCon EU'2020)

Exemple senzill: copiar secrets

Vegem un exemple senzill.

Suposem que tenim un clúster de Kubernetes. Té un espai de noms default amb algun secret mysecret. A més, hi ha altres espais de noms al clúster. Alguns d'ells tenen una etiqueta específica adjunta. El nostre objectiu és copiar Secret als espais de noms amb una etiqueta.

La tasca es complica pel fet que poden aparèixer nous espais de noms al clúster, i alguns d'ells poden tenir aquesta etiqueta. D'altra banda, quan s'elimina l'etiqueta, també s'ha d'eliminar Secret. A més d'això, el propi Secret també pot canviar: en aquest cas, el nou Secret s'ha de copiar a tots els espais de noms amb etiquetes. Si Secret s'elimina accidentalment en qualsevol espai de noms, el nostre operador hauria de restaurar-lo immediatament.

Ara que la tasca s'ha formulat, és hora de començar a implementar-la mitjançant l'operador de shell. Però primer val la pena dir unes quantes paraules sobre l'operador d'intèrpret d'ordres en si.

Com funciona l'operador shell

Igual que altres càrregues de treball a Kubernetes, l'operador shell s'executa al seu propi pod. En aquest pod del directori /hooks s'emmagatzemen els fitxers executables. Aquests poden ser scripts en Bash, Python, Ruby, etc. A aquests fitxers executables els anomenem ganxos (ganxos).

Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo de KubeCon EU'2020)

L'operador de Shell es subscriu als esdeveniments de Kubernetes i executa aquests ganxos en resposta als esdeveniments que necessitem.

Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo de KubeCon EU'2020)

Com sap l'operador de l'intèrpret d'ordres quin ganxo ha d'executar i quan? La qüestió és que cada ganxo té dues etapes. Durant l'inici, l'operador de shell executa tots els ganxos amb un argument --config Aquesta és l'etapa de configuració. I després d'això, els ganxos es llancen de la manera normal, en resposta als esdeveniments als quals estan vinculats. En aquest últim cas, el ganxo rep el context d'enllaç (context vinculant) - dades en format JSON, de les quals parlarem amb més detall a continuació.

Fer un operador a Bash

Ara estem preparats per a la implementació. Per fer-ho, hem d'escriure dues funcions (per cert, us recomanem la biblioteca shell_lib, que simplifica enormement els ganxos d'escriptura a Bash):

  • el primer és necessari per a l'etapa de configuració: mostra el context d'enllaç;
  • el segon conté la lògica principal del ganxo.

#!/bin/bash

source /shell_lib.sh

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

function __main__() {
  # THE LOGIC
}

hook::run "$@"

El següent pas és decidir quins objectes necessitem. En el nostre cas, hem de fer un seguiment:

  • font secreta per als canvis;
  • tots els espais de noms del clúster, de manera que sàpigues quins tenen una etiqueta adjunta;
  • secrets de destinació per assegurar-se que tots estan sincronitzats amb el secret d'origen.

Subscriu-te a la font secreta

La configuració d'enllaç és bastant senzilla. Indiquem que ens interessa Secret amb el nom mysecret a l'espai de noms default:

Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo 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

Com a resultat, el ganxo s'activarà quan canviï el secret d'origen (src_secret) i rebre el context vinculant següent:

Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo de KubeCon EU'2020)

Com podeu veure, conté el nom i l'objecte sencer.

Fer un seguiment dels espais de noms

Ara us heu de subscriure als espais de noms. Per fer-ho, especifiquem la següent configuració d'enllaç:

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

Com podeu veure, ha aparegut un camp nou a la configuració amb el nom jqFilter. Com el seu nom indica, jqFilter filtra tota la informació innecessària i crea un nou objecte JSON amb els camps que ens interessen. Un ganxo amb una configuració similar rebrà el context d'enllaç següent:

Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo de KubeCon EU'2020)

Conté una matriu filterResults per a cada espai de noms del clúster. Variable booleana hasLabel indica si s'adjunta una etiqueta a un espai de noms determinat. Selector keepFullObjectsInMemory: false indica que no cal mantenir objectes complets a la memòria.

Seguiment de secrets objectiu

Ens subscrivim a tots els Secrets que tinguin una anotació especificada managed-secret: "yes" (aquests són el nostre objectiu 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 aquest cas, jqFilter filtra tota la informació excepte l'espai de noms i el paràmetre resourceVersion. L'últim paràmetre es va passar a l'anotació en crear el secret: permet comparar versions de secrets i mantenir-los actualitzats.

Un ganxo configurat d'aquesta manera rebrà, quan s'executa, els tres contextos d'enllaç descrits anteriorment. Es poden considerar com una mena de instantània (instantània) clúster.

Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo de KubeCon EU'2020)

A partir de tota aquesta informació, es pot desenvolupar un algorisme bàsic. Itera per tots els espais de noms i:

  • si hasLabel assumptes true per a l'espai de noms actual:
    • compara el secret global amb el local:
      • si són iguals, no fa res;
      • si difereixen - executa kubectl replace o create;
  • si hasLabel assumptes false per a l'espai de noms actual:
    • s'assegura que Secret no estigui a l'espai de noms donat:
      • si el secret local està present, suprimiu-lo utilitzant kubectl delete;
      • si no es detecta el Secret local, no fa res.

Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo de KubeCon EU'2020)

Implementació de l'algorisme a Bash us podeu descarregar al nostre repositoris amb exemples.

Així és com vam poder crear un controlador Kubernetes senzill utilitzant 35 línies de configuració YAML i aproximadament la mateixa quantitat de codi Bash! La feina de l'operador d'intèrpret d'ordres és enllaçar-los.

Tanmateix, copiar secrets no és l'únic àmbit d'aplicació de la utilitat. Aquí teniu uns quants exemples més per demostrar de què és capaç.

Exemple 1: fer canvis a ConfigMap

Vegem un desplegament format per tres pods. Els pods utilitzen ConfigMap per emmagatzemar alguna configuració. Quan es van llançar els pods, ConfigMap estava en un estat determinat (anomenarem-lo v.1). En conseqüència, tots els pods utilitzen aquesta versió particular de ConfigMap.

Ara suposem que el ConfigMap ha canviat (v.2). Tanmateix, els pods utilitzaran la versió anterior de ConfigMap (v.1):

Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo de KubeCon EU'2020)

Com puc fer que canviïn al nou ConfigMap (v.2)? La resposta és senzilla: utilitzeu una plantilla. Afegim una anotació de suma de comprovació a la secció template Configuracions de desplegament:

Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo de KubeCon EU'2020)

Com a resultat, aquesta suma de comprovació es registrarà a tots els pods i serà la mateixa que la del desplegament. Ara només cal actualitzar l'anotació quan canviï el ConfigMap. I l'operador de shell és útil en aquest cas. Tot el que has de fer és programar un ganxo que se subscriurà al ConfigMap i actualitzarà la suma de verificació.

Si l'usuari fa canvis al ConfigMap, l'operador de l'intèrpret d'ordres els notarà i tornarà a calcular la suma de verificació. Després d'això, la màgia de Kubernetes entrarà en joc: l'orquestrador matarà la beina, en crearà una de nova, esperarà que esdevingui. Ready, i passa a la següent. Com a resultat, Deployment sincronitzarà i canviarà a la nova versió de ConfigMap.

Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo de KubeCon EU'2020)

Exemple 2: treballant amb definicions de recursos personalitzades

Com ja sabeu, Kubernetes us permet crear tipus d'objectes personalitzats. Per exemple, podeu crear amable MysqlDatabase. Suposem que aquest tipus té dos paràmetres de metadades: name и namespace.

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

Tenim un clúster Kubernetes amb diferents espais de noms en el qual podem crear bases de dades MySQL. En aquest cas, l'operador de shell es pot utilitzar per fer un seguiment dels recursos MysqlDatabase, connectant-los al servidor MySQL i sincronitzant els estats desitjats i observats del clúster.

Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo de KubeCon EU'2020)

Exemple 3: Monitorització de la xarxa de clúster

Com ja sabeu, utilitzar ping és la manera més senzilla de controlar una xarxa. En aquest exemple mostrarem com implementar aquest monitoratge mitjançant l'operador shell.

En primer lloc, us haureu de subscriure als nodes. L'operador de shell necessita el nom i l'adreça IP de cada node. Amb la seva ajuda, farà ping a aquests nodes.

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

Paràmetre executeHookOnEvent: [] impedeix que el ganxo s'executi en resposta a qualsevol esdeveniment (és a dir, en resposta a canviar, afegir, suprimir nodes). Tanmateix, ell correrà (i actualitzar la llista de nodes) Programat - cada minut, segons prescriu el camp schedule.

Ara sorgeix la pregunta, com sabem exactament sobre problemes com la pèrdua de paquets? Fem una ullada al codi:

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
}

Recorrem la llista de nodes, obtenim els seus noms i adreces IP, els fem ping i enviem els resultats a Prometheus. L'operador de Shell pot exportar mètriques a Prometheus, desant-los en un fitxer situat segons el camí especificat a la variable d'entorn $METRICS_PATH.

Aquí sí podeu crear un operador per a un seguiment senzill de la xarxa en un clúster.

Mecanisme de cua

Aquest article estaria incomplet sense descriure un altre mecanisme important integrat a l'operador de shell. Imagineu que executa algun tipus de ganxo en resposta a un esdeveniment del clúster.

  • Què passa si, al mateix temps, passa alguna cosa al clúster? un més esdeveniment?
  • L'operador de shell executarà una altra instància del ganxo?
  • Què passa si, per exemple, passen cinc esdeveniments al clúster alhora?
  • Els processarà l'operador de l'intèrpret d'ordres en paral·lel?
  • Què passa amb els recursos consumits com la memòria i la CPU?

Afortunadament, l'operador de shell té un mecanisme de cua integrat. Tots els esdeveniments es posen a la cua i es processen seqüencialment.

Il·lustrem-ho amb exemples. Diguem que tenim dos ganxos. El primer esdeveniment va al primer ganxo. Un cop finalitzat el processament, la cua avança. Els tres esdeveniments següents es redirigien al segon ganxo: s'eliminen de la cua i s'hi introdueixen en un "paquet". Això és hook rep una sèrie d'esdeveniments — o, més precisament, una sèrie de contextos vinculants.

També aquests els esdeveniments es poden combinar en un sol gran. El paràmetre és responsable d'això group en la configuració d'enllaç.

Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo de KubeCon EU'2020)

Podeu crear qualsevol nombre de cues/ganxos i les seves diverses combinacions. Per exemple, una cua pot funcionar amb dos ganxos, o viceversa.

Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo de KubeCon EU'2020)

Tot el que heu de fer és configurar el camp en conseqüència queue en la configuració d'enllaç. Si no s'especifica un nom de cua, el ganxo s'executa a la cua predeterminada (default). Aquest mecanisme de cua us permet resoldre completament tots els problemes de gestió de recursos quan es treballa amb ganxos.

Conclusió

Vam explicar què és un operador d'intèrpret d'ordres, vam mostrar com es pot utilitzar per crear operadors Kubernetes de manera ràpida i sense esforç i vam donar diversos exemples del seu ús.

La informació detallada sobre l'operador de l'intèrpret d'ordres, així com un tutorial ràpid sobre com utilitzar-lo, està disponible a la pàgina corresponent. repositoris a GitHub. No dubteu a posar-vos en contacte amb nosaltres si teniu preguntes: podeu discutir-les en un especial Grup de Telegram (en rus) o en aquest fòrum (en anglès).

I si t'ha agradat, sempre estem encantats de veure nous números/PR/estrelles a GitHub, on, per cert, en pots trobar d'altres. projectes interessants. Entre ells cal destacar operador-complement, que és el germà gran de shell-operator. Aquesta utilitat utilitza gràfics Helm per instal·lar complements, pot oferir actualitzacions i supervisar diversos paràmetres/valors de gràfics, controla el procés d'instal·lació dels gràfics i també els pot modificar en resposta als esdeveniments del clúster.

Anar? Xoc! Coneix l'operador d'intèrpret d'ordres (revisió i informe de vídeo de KubeCon EU'2020)

Vídeos i diapositives

Vídeo de l'actuació (~23 minuts):


Presentació de l'informe:

PS

Llegeix també al nostre blog:

Font: www.habr.com

Afegeix comentari