Introduktion av skal-operatör: att skapa operatörer för Kubernetes har precis blivit enklare

Det har redan funnits artiklar på vår blogg som pratar om operatörskapacitet i Kubernetes och hur skriv en enkel operatör själv. Den här gången vill vi presentera vår Open Source-lösning, som tar skapandet av operatörer till en superlätt nivå - kolla in skal-operatör!

Varför?

Idén med en skal-operatör är ganska enkel: prenumerera på händelser från Kubernetes-objekt, och när dessa händelser tas emot, starta ett externt program som ger det information om händelsen:

Introduktion av skal-operatör: att skapa operatörer för Kubernetes har precis blivit enklare

Behovet av det uppstod när det under driften av kluster började dyka upp små uppgifter som vi verkligen ville automatisera på rätt sätt. Alla dessa små uppgifter löstes med enkla bash-skript, även om det som ni vet är bättre att skriva operatorer i Golang. Uppenbarligen skulle det vara ineffektivt att investera i fullskalig utveckling av en operatör för varje sådan liten uppgift.

Operatör på 15 minuter

Låt oss titta på ett exempel på vad som kan automatiseras i ett Kubernetes-kluster och hur skal-operatören kan hjälpa till. Ett exempel skulle vara följande: replikera en hemlighet för att komma åt docker-registret.

Pods som använder bilder från ett privat register måste i sitt manifest innehålla en länk till en hemlighet med data för åtkomst till registret. Denna hemlighet måste skapas i varje namnområde innan du skapar poddar. Detta kan göras manuellt, men om vi sätter upp dynamiska miljöer kommer namnutrymmet för en applikation att bli mycket. Och om det inte heller finns 2-3 ansökningar... blir antalet hemligheter väldigt stort. Och en sak till om hemligheter: Jag skulle vilja ändra nyckeln för att komma åt registret då och då. Så småningom, manuella operationer som en lösning helt ineffektivt — vi måste automatisera skapandet och uppdateringen av hemligheter.

Enkel automatisering

Låt oss skriva ett skalskript som körs en gång var N:e sekund och kontrollerar namnutrymmen för närvaron av en hemlighet, och om det inte finns någon hemlighet skapas den. Fördelen med denna lösning är att den ser ut som ett skalskript i cron – ett klassiskt och begripligt förhållningssätt för alla. Nackdelen är att i intervallet mellan dess lanseringar kan ett nytt namnutrymme skapas och under en tid kommer det att förbli utan en hemlighet, vilket kommer att leda till fel vid lansering av pods.

Automatisering med skal-operatör

För att vårt skript ska fungera korrekt måste den klassiska cron-lanseringen ersättas med en lansering när ett namnområde läggs till: i det här fallet kan du skapa en hemlighet innan du använder den. Låt oss se hur man implementerar detta med hjälp av shell-operator.

Låt oss först titta på manuset. Skript i skal-operatortermer kallas hooks. Varje krok när den körs med en flagga --config informerar skaloperatören om sina bindningar, dvs. på vilka evenemang det ska lanseras. I vårt fall kommer vi att använda onKubernetesEvent:

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

Det beskrivs här att vi är intresserade av att lägga till evenemang (add) objekt av typen namespace.

Nu måste du lägga till koden som kommer att köras när händelsen inträffar:

#!/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

Bra! Resultatet blev ett litet vackert manus. För att "återuppliva" den finns det två steg kvar: förbered bilden och starta den i klustret.

Förbereder en bild med en krok

Om du tittar på skriptet kan du se att kommandona används kubectl и jq. Det betyder att bilden måste ha följande saker: vår krok, en skal-operator som övervakar händelser och kör kroken, och kommandona som används av kroken (kubectl och jq). Hub.docker.com har redan en färdig bild där shell-operator, kubectl och jq är förpackade. Allt som återstår är att lägga till en enkel krok 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

Springer i ett kluster

Låt oss titta på kroken igen och den här gången skriva ner vilka åtgärder och med vilka objekt den utför i klustret:

  1. prenumererar på händelser för att skapa namnutrymme;
  2. skapar en hemlighet i andra namnutrymmen än den där den lanseras.

Det visar sig att podden där vår bild kommer att lanseras måste ha behörighet att utföra dessa åtgärder. Detta kan göras genom att skapa ditt eget ServiceAccount. Tillståndet måste göras i form av ClusterRole och ClusterRoleBinding, eftersom vi är intresserade av föremål från hela klustret.

Den slutliga beskrivningen i YAML kommer att se ut ungefär så här:

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

Du kan starta den sammansatta bilden som en enkel distribution:

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

För enkelhetens skull skapas ett separat namnutrymme där skal-operatorn kommer att startas och de skapade manifesten kommer att tillämpas:

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

Det är allt: skaloperatorn startar, prenumererar på händelser för att skapa namnutrymme och kör kroken när det behövs.

Introduktion av skal-operatör: att skapa operatörer för Kubernetes har precis blivit enklare

Sålunda ett enkelt skalskript förvandlades till en riktig operatör för Kubernetes och fungerar som en del av ett kluster. Och allt detta utan den komplexa processen att utveckla operatörer i Golang:

Introduktion av skal-operatör: att skapa operatörer för Kubernetes har precis blivit enklare

Det finns en annan illustration i denna fråga...Introduktion av skal-operatör: att skapa operatörer för Kubernetes har precis blivit enklare

Vi kommer att avslöja dess innebörd mer i detalj i en av följande publikationer.

filtrering

Att spåra föremål är bra, men det finns ofta ett behov att reagera på ändra vissa objektegenskaper, till exempel för att ändra antalet repliker i Deployment eller för att ändra objektetiketter.

När en händelse anländer tar skaloperatören emot JSON-manifestet för objektet. Vi kan välja de egenskaper som intresserar oss i denna JSON och köra kroken endast när de ändras. Det finns ett fält för detta jqFilter, där du måste ange jq-uttrycket som ska tillämpas på JSON-manifestet.

Till exempel, för att svara på ändringar i etiketter för distributionsobjekt måste du filtrera fältet labels utanför fältet metadata. Konfigurationen blir så här:

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

Detta jqFilter-uttryck förvandlar Deployments långa JSON-manifest till kort JSON med etiketter:

Introduktion av skal-operatör: att skapa operatörer för Kubernetes har precis blivit enklare

shell-operator kommer bara att köra kroken när denna korta JSON ändras, och ändringar av andra egenskaper kommer att ignoreras.

Hook launch sammanhang

Hook-konfigurationen låter dig ange flera alternativ för evenemang - till exempel 2 alternativ för evenemang från Kubernetes och 2 scheman:

{"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"
]}

En liten avvikelse: ja, skal-operatör stöder kör crontab-stilskript. Mer information finns i dokumentation.

För att särskilja varför kroken lanserades skapar skaloperatorn en temporär fil och skickar sökvägen till den i en variabel till kroken BINDING_CONTEXT_TYPE. Filen innehåller en JSON-beskrivning av anledningen till att haken körs. Till exempel, var tionde minut kommer kroken att köras med följande innehåll:

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

... och på måndag börjar det med detta:

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

för onKubernetesEvent Det kommer att finnas fler JSON-utlösare, eftersom den innehåller en beskrivning av objektet:

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

Innehållet i fälten kan förstås från deras namn, och mer detaljer kan läsas i dokumentation. Ett exempel på att få ett resursnamn från ett fält resourceName att använda jq har redan visats i en krok som replikerar hemligheter:

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

Du kan få andra fält på liknande sätt.

Vad händer nu?

I projektförvaret, i /exempelkataloger, det finns exempel på krokar som är redo att köras på ett kluster. När du skriver dina egna krokar kan du använda dem som grund.

Det finns stöd för att samla in mätvärden med hjälp av Prometheus - tillgängliga mätvärden beskrivs i avsnittet METRIK.

Som du kanske gissar är skal-operatorn skriven i Go och distribuerad under en Open Source-licens (Apache 2.0). Vi kommer att vara tacksamma för allt utvecklingsbistånd projekt på GitHub: och stjärnor, och utfärdar, och pull-förfrågningar.

Vi lyfter på hemlighetsslöjan, vi kommer också att informera dig om att skal-operatören är små del av vårt system som kan hålla tillägg installerade i Kubernetes-klustret uppdaterade och utföra olika automatiska åtgärder. Läs mer om detta system berättade bokstavligen på måndag på HighLoad++ 2019 i St. Petersburg - vi kommer snart att publicera videon och transkriptionen av denna rapport.

Vi har en plan för att öppna upp resten av detta system: addon-operatören och vår samling av krokar och moduler. Förresten, addon-operator är det redan tillgänglig på github, men dokumentationen för det är fortfarande på väg. Releasen av samlingen av moduler är planerad till sommaren.

Håll dig igång!

PS

Läs även på vår blogg:

Källa: will.com

Lägg en kommentar