Presentació de l'operador de shell: crear operadors per a Kubernetes és més fàcil

Al nostre blog ja hi ha articles que parlen capacitats de l'operador a Kubernetes i com escriu tu mateix un operador senzill. Aquesta vegada ens agradaria presentar-vos a la vostra atenció la nostra solució de codi obert, que porta la creació d'operadors a un nivell súper fàcil: consulteu operador de shell!

Per què?

La idea d'un operador d'intèrpret d'ordres és bastant senzilla: subscriu-te als esdeveniments dels objectes Kubernetes i, quan es rebin aquests esdeveniments, inicieu un programa extern que li proporcioni informació sobre l'esdeveniment:

Presentació de l'operador de shell: crear operadors per a Kubernetes és més fàcil

La necessitat va sorgir quan, durant el funcionament dels clústers, van començar a aparèixer petites tasques que realment volíem automatitzar de la manera correcta. Totes aquestes petites tasques es van resoldre mitjançant scripts bash senzills, encara que, com ja sabeu, és millor escriure operadors a Golang. Òbviament, invertir en el desenvolupament a gran escala d'un operador per a cada petita tasca seria ineficaç.

Operador en 15 minuts

Vegem un exemple del que es pot automatitzar en un clúster de Kubernetes i de com pot ajudar l'operador d'intèrpret d'ordres. Un exemple seria el següent: replicar un secret per accedir al registre docker.

Els pods que utilitzen imatges d'un registre privat han de contenir al seu manifest un enllaç a un secret amb dades per accedir al registre. Aquest secret s'ha de crear a cada espai de noms abans de crear pods. Això es pot fer manualment, però si configurem entorns dinàmics, l'espai de noms d'una aplicació es convertirà en molt. I si tampoc hi ha 2-3 aplicacions... el nombre de secrets es fa molt gran. I una cosa més sobre els secrets: m'agradaria canviar la clau per accedir al registre de tant en tant. Finalment, operacions manuals com a solució completament ineficaç — Hem d'automatitzar la creació i actualització de secrets.

Automatització senzilla

Escrivim un script d'intèrpret d'ordres que s'executi un cop cada N segons i comprovi la presència d'un secret en els espais de noms i, si no hi ha cap secret, es crea. L'avantatge d'aquesta solució és que sembla un script d'intèrpret d'ordres a cron, un enfocament clàssic i entenedor per a tothom. L'inconvenient és que en l'interval entre els seus llançaments es pot crear un nou espai de noms i durant algun temps romandrà sense secret, la qual cosa comportarà errors en el llançament de pods.

Automatització amb operador shell

Perquè el nostre script funcioni correctament, el llançament de cron clàssic s'ha de substituir per un llançament quan s'afegeix un espai de noms: en aquest cas, podeu crear un secret abans d'utilitzar-lo. Vegem com implementar-ho mitjançant l'operador shell.

Primer, mirem el guió. Els scripts en termes d'operador de shell s'anomenen ganxos. Cada ganxo quan es corre amb una bandera --config informa l'operador de l'intèrpret d'ordres sobre els seus enllaços, és a dir. sobre quins esdeveniments s'hauria de posar en marxa. En el nostre cas utilitzarem onKubernetesEvent:

#!/bin/bash
if [[ $1 == "--config" ]] ; then
cat <<EOF
{
"onKubernetesEvent": [
  { "kind": "namespace",
    "event":["add"]
  }
]}
EOF
fi

Aquí es descriu que ens interessa afegir esdeveniments (add) objectes de tipus namespace.

Ara cal afegir el codi que s'executarà quan es produeixi l'esdeveniment:

#!/bin/bash
if [[ $1 == "--config" ]] ; then
  # конфигурация
cat <<EOF
{
"onKubernetesEvent": [
{ "kind": "namespace",
  "event":["add"]
}
]}
EOF
else
  # реакция:
  # узнать, какой namespace появился
  createdNamespace=$(jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH)
  # создать в нём нужный секрет
  kubectl create -n ${createdNamespace} -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  ...
data:
  ...
EOF
fi

Genial! El resultat va ser un guió petit i bonic. Per "reviure"-la, queden dos passos: preparar la imatge i llançar-la al clúster.

Preparant una imatge amb un ganxo

Si mireu l'script, podeu veure que s'utilitzen les ordres kubectl и jq. Això vol dir que la imatge ha de tenir les coses següents: el nostre ganxo, un operador d'intèrpret d'ordres que supervisarà els esdeveniments i executarà el ganxo, i les ordres utilitzades pel ganxo (kubectl i jq). Hub.docker.com ja té una imatge preparada en la qual s'empaqueta l'operador de shell, kubectl i jq. Només queda afegir-hi un simple ganxo Dockerfile:

$ cat Dockerfile
FROM flant/shell-operator:v1.0.0-beta.1-alpine3.9
ADD namespace-hook.sh /hooks

$ docker build -t registry.example.com/my-operator:v1 . 
$ docker push registry.example.com/my-operator:v1

Córrer en un clúster

Tornem a mirar el ganxo i aquesta vegada anotem quines accions i amb quins objectes realitza al clúster:

  1. es subscriu als esdeveniments de creació d'espais de noms;
  2. crea un secret en espais de noms diferents d'aquell on es llança.

Resulta que el pod en el qual es llançarà la nostra imatge ha de tenir permisos per fer aquestes accions. Això es pot fer creant el vostre compte de servei. El permís s'ha de fer en forma de ClusterRole i ClusterRoleBinding, perquè ens interessen els objectes de tot el clúster.

La descripció final de YAML tindrà un aspecte semblant a això:

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: monitor-namespaces-acc

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: monitor-namespaces
rules:
- apiGroups: [""]
  resources: ["namespaces"]
  verbs: ["get", "watch", "list"]
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "list", "create", "patch"]

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: monitor-namespaces
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: monitor-namespaces
subjects:
  - kind: ServiceAccount
    name: monitor-namespaces-acc
    namespace: example-monitor-namespaces

Podeu llançar la imatge muntada com un desplegament senzill:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-operator
spec:
  template:
    spec:
      containers:
      - name: my-operator
        image: registry.example.com/my-operator:v1
      serviceAccountName: monitor-namespaces-acc

Per comoditat, es crea un espai de noms separat on s'iniciarà l'operador de shell i s'aplicaran els manifests creats:

$ kubectl create ns example-monitor-namespaces
$ kubectl -n example-monitor-namespaces apply -f rbac.yaml
$ kubectl -n example-monitor-namespaces apply -f deployment.yaml

Això és tot: l'operador de l'intèrpret d'ordres s'iniciarà, es subscriurà als esdeveniments de creació d'espais de noms i executarà el ganxo quan sigui necessari.

Presentació de l'operador de shell: crear operadors per a Kubernetes és més fàcil

Per tant, la un senzill script de shell convertit en un operador real per a Kubernetes i funciona com a part d'un clúster. I tot això sense el complex procés de desenvolupament d'operadors a Golang:

Presentació de l'operador de shell: crear operadors per a Kubernetes és més fàcil

Hi ha una altra il·lustració sobre aquest tema...Presentació de l'operador de shell: crear operadors per a Kubernetes és més fàcil

Desvetllarem el seu significat amb més detall en una de les publicacions següents.

filtració

El seguiment dels objectes és bo, però sovint cal reaccionar canviant algunes propietats de l'objecte, per exemple, per canviar el nombre de rèpliques a Deployment o per canviar les etiquetes d'objectes.

Quan arriba un esdeveniment, l'operador de shell rep el manifest JSON de l'objecte. Podem seleccionar les propietats que ens interessen en aquest JSON i executar el ganxo només quan canvien. Hi ha un camp per a això jqFilter, on heu d'especificar l'expressió jq que s'aplicarà al manifest JSON.

Per exemple, per respondre als canvis a les etiquetes dels objectes de desplegament, heu de filtrar el camp labels fora del camp metadata. La configuració serà així:

cat <<EOF
{
"onKubernetesEvent": [
{ "kind": "deployment",
  "event":["update"],
  "jqFilter": ".metadata.labels"
}
]}
EOF

Aquesta expressió jqFilter converteix el manifest JSON llarg de Deployment en JSON curt amb etiquetes:

Presentació de l'operador de shell: crear operadors per a Kubernetes és més fàcil

shell-operator només executarà el ganxo quan aquest JSON curt canvia, i els canvis a altres propietats s'ignoraran.

Context de llançament del ganxo

La configuració del ganxo us permet especificar diverses opcions per a esdeveniments, per exemple, 2 opcions per a esdeveniments de Kubernetes i 2 horaris:

{"onKubernetesEvent":[
  {"name":"OnCreatePod",
  "kind": "pod",
  "event":["add"]
  },
  {"name":"OnModifiedNamespace",
  "kind": "namespace",
  "event":["update"],
  "jqFilter": ".metadata.labels"
  }
],
"schedule": [
{ "name":"every 10 min",
  "crontab":"* */10 * * * *"
}, {"name":"on Mondays at 12:10",
"crontab": "* 10 12 * * 1"
]}

Una petita digressió: sí, admet l'operador de shell executant scripts d'estil crontab. Podeu trobar més detalls a documentació.

Per distingir per què es va llançar el ganxo, l'operador d'intèrpret d'ordres crea un fitxer temporal i li passa el camí en una variable al ganxo BINDING_CONTEXT_TYPE. El fitxer conté una descripció JSON del motiu de l'execució del ganxo. Per exemple, cada 10 minuts el ganxo s'executarà amb el contingut següent:

[{ "binding": "every 10 min"}]

... i dilluns començarà amb això:

[{ "binding": "every 10 min"}, { "binding": "on Mondays at 12:10"}]

Per onKubernetesEvent Hi haurà més activadors JSON, perquè conté una descripció de l'objecte:

[
 {
 "binding": "onCreatePod",
 "resourceEvent": "add",
 "resourceKind": "pod",
 "resourceName": "foo",
 "resourceNamespace": "bar"
 }
]

El contingut dels camps es pot entendre a partir dels seus noms i es poden llegir més detalls documentació. Un exemple d'obtenir un nom de recurs d'un camp resourceName l'ús de jq ja s'ha mostrat en un ganxo que replica secrets:

jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH

Podeu obtenir altres camps de la mateixa manera.

Què serà el següent?

Al repositori del projecte, a directoris /exemples, hi ha exemples de ganxos que estan preparats per executar-se en un clúster. Quan escriu els teus propis ganxos, pots utilitzar-los com a base.

Hi ha suport per recopilar mètriques amb Prometheus; les mètriques disponibles es descriuen a la secció MÈTRICA.

Com podeu endevinar, l'operador d'intèrpret d'ordres està escrit a Go i es distribueix sota una llicència de codi obert (Apache 2.0). Agrairem qualsevol ajuda per al desenvolupament projecte a GitHub: i estrelles, i problemes i sol·licituds d'extracció.

Aixecant el vel del secret, també us informarem que ho és l'operador d'intèrpret petit part del nostre sistema que pot mantenir actualitzats els complements instal·lats al clúster Kubernetes i realitzar diverses accions automàtiques. Llegeix més sobre aquest sistema va dir literalment dilluns a HighLoad++ 2019 a Sant Petersburg; aviat publicarem el vídeo i la transcripció d'aquest informe.

Tenim un pla per obrir la resta d'aquest sistema: l'operador de complements i la nostra col·lecció de ganxos i mòduls. Per cert, l'operador de complements ja ho és disponible a github, però la documentació per això encara està en camí. El llançament de la col·lecció de mòduls està previst per a l'estiu.

Estiguin atents!

PS

Llegeix també al nostre blog:

Font: www.habr.com

Afegeix comentari