Ipinapakilala ang shell-operator: naging mas madali ang paggawa ng mga operator para sa Kubernetes

Nagkaroon na ng mga artikulo sa aming blog na pinag-uusapan mga kakayahan ng operator sa Kubernetes at kung paano sumulat ng isang simpleng operator sa iyong sarili. Sa pagkakataong ito, nais naming ipakita sa iyong atensyon ang aming Open Source na solusyon, na nagdadala sa paglikha ng mga operator sa napakadaling antas - tingnan shell-operator!

Bakit?

Ang ideya ng isang shell-operator ay medyo simple: mag-subscribe sa mga kaganapan mula sa mga bagay ng Kubernetes, at kapag natanggap ang mga kaganapang ito, maglunsad ng isang panlabas na programa, na nagbibigay ng impormasyon tungkol sa kaganapan:

Ipinapakilala ang shell-operator: naging mas madali ang paggawa ng mga operator para sa Kubernetes

Ang pangangailangan para dito ay lumitaw nang, sa panahon ng pagpapatakbo ng mga kumpol, nagsimulang lumitaw ang maliliit na gawain na talagang gusto naming i-automate sa tamang paraan. Ang lahat ng maliliit na gawaing ito ay nalutas gamit ang mga simpleng bash script, bagaman, tulad ng alam mo, mas mahusay na magsulat ng mga operator sa Golang. Malinaw, ang pamumuhunan sa ganap na pagpapaunlad ng isang operator para sa bawat ganoong maliit na gawain ay hindi magiging epektibo.

Operator sa loob ng 15 minuto

Tingnan natin ang isang halimbawa ng kung ano ang maaaring i-automate sa isang Kubernetes cluster at kung paano makakatulong ang shell-operator. Ang isang halimbawa ay ang sumusunod: pagkopya ng isang lihim upang ma-access ang registry ng docker.

Ang mga pod na gumagamit ng mga larawan mula sa isang pribadong registry ay dapat maglaman sa kanilang manifest ng isang link sa isang lihim na may data para sa pag-access sa registry. Ang lihim na ito ay dapat gawin sa bawat namespace bago gumawa ng mga pod. Maaari itong gawin nang manu-mano, ngunit kung magse-set up tayo ng mga dynamic na kapaligiran, magiging marami ang namespace para sa isang application. At kung wala ring 2-3 application... ang bilang ng mga sikreto ay nagiging napakalaki. At isa pang bagay tungkol sa mga lihim: Gusto kong baguhin ang susi upang ma-access ang pagpapatala sa pana-panahon. Sa bandang huli, mga manu-manong operasyon bilang solusyon ganap na hindi epektibo — kailangan nating i-automate ang paglikha at pag-update ng mga lihim.

Simpleng automation

Sumulat tayo ng shell script na tumatakbo nang isang beses bawat N segundo at sinusuri ang mga namespace para sa pagkakaroon ng isang lihim, at kung walang sikreto, ito ay nilikha. Ang bentahe ng solusyon na ito ay mukhang isang shell script sa cron - isang klasiko at naiintindihan na diskarte sa lahat. Ang downside ay na sa pagitan sa pagitan ng mga paglulunsad nito ay maaaring malikha ang isang bagong namespace at sa loob ng ilang panahon ay mananatili itong walang lihim, na hahantong sa mga pagkakamali sa paglulunsad ng mga pod.

Automation gamit ang shell-operator

Para gumana nang tama ang aming script, kailangang mapalitan ng paglulunsad ang classic na cron launch kapag may naidagdag na namespace: sa kasong ito, maaari kang gumawa ng lihim bago ito gamitin. Tingnan natin kung paano ito ipatupad gamit ang shell-operator.

Una, tingnan natin ang script. Ang mga script sa mga termino ng shell-operator ay tinatawag na mga kawit. Bawat kawit kapag tumakbo na may bandila --config nagpapaalam sa shell-operator tungkol sa mga binding nito, i.e. sa kung anong mga kaganapan ang dapat itong ilunsad. Sa aming kaso gagamitin namin onKubernetesEvent:

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

Inilalarawan dito na kami ay interesado sa pagdaragdag ng mga kaganapan (add) mga bagay ng uri namespace.

Ngayon ay kailangan mong idagdag ang code na isasagawa kapag nangyari ang kaganapan:

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

Malaki! Ang resulta ay isang maliit, magandang script. Upang "muling buhayin" ito, may dalawang hakbang na natitira: ihanda ang imahe at ilunsad ito sa cluster.

Paghahanda ng isang imahe na may kawit

Kung titingnan mo ang script, makikita mo na ang mga utos ay ginagamit kubectl и jq. Nangangahulugan ito na ang imahe ay dapat magkaroon ng mga sumusunod na bagay: ang aming hook, isang shell-operator na makikinig sa mga kaganapan at magpapatakbo ng hook, at ang mga utos na ginagamit ng hook (kubectl at jq). Ang Hub.docker.com ay mayroon nang yari na imahe kung saan naka-package ang shell-operator, kubectl at jq. Ang natitira na lang ay magdagdag ng isang simpleng kawit 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

Tumatakbo sa isang kumpol

Tingnan natin muli ang hook at sa pagkakataong ito ay isulat kung anong mga aksyon at kung anong mga bagay ang ginagawa nito sa cluster:

  1. nag-subscribe sa mga kaganapan sa paglikha ng namespace;
  2. lumilikha ng isang lihim sa mga namespace maliban sa kung saan ito inilunsad.

Lumalabas na ang pod kung saan ilulunsad ang aming larawan ay dapat may mga pahintulot na gawin ang mga pagkilos na ito. Magagawa ito sa pamamagitan ng paglikha ng iyong sariling ServiceAccount. Ang pahintulot ay dapat gawin sa anyo ng ClusterRole at ClusterRoleBinding, dahil interesado kami sa mga bagay mula sa buong kumpol.

Ang huling paglalarawan sa YAML ay magiging ganito:

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

Maaari mong ilunsad ang naka-assemble na imahe bilang isang simpleng Deployment:

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

Para sa kaginhawahan, ang isang hiwalay na namespace ay nilikha kung saan ang shell-operator ay ilulunsad at ang mga nilikha na manifest ay ilalapat:

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

Iyon lang: magsisimula ang shell-operator, mag-subscribe sa mga kaganapan sa paglikha ng namespace at patakbuhin ang hook kapag kinakailangan.

Ipinapakilala ang shell-operator: naging mas madali ang paggawa ng mga operator para sa Kubernetes

Kaya, ang ang isang simpleng script ng shell ay naging isang tunay na operator para sa Kubernetes at gumagana bilang bahagi ng isang kumpol. At lahat ng ito nang walang kumplikadong proseso ng pagbuo ng mga operator sa Golang:

Ipinapakilala ang shell-operator: naging mas madali ang paggawa ng mga operator para sa Kubernetes

May isa pang paglalarawan sa bagay na ito...Ipinapakilala ang shell-operator: naging mas madali ang paggawa ng mga operator para sa Kubernetes

Ilalahad namin ang kahulugan nito nang mas detalyado sa isa sa mga sumusunod na publikasyon.

pagsasala

Mahusay ang pagsubaybay sa mga bagay, ngunit madalas na kailangang tumugon pagbabago ng ilang mga katangian ng bagay, halimbawa, upang baguhin ang bilang ng mga replika sa Deployment o upang baguhin ang mga label ng bagay.

Kapag dumating ang isang kaganapan, natatanggap ng shell-operator ang JSON manifest ng object. Maaari naming piliin ang mga katangian na interesado sa amin sa JSON na ito at patakbuhin ang hook lamang kapag nagbago sila. Mayroong isang larangan para dito jqFilter, kung saan kailangan mong tukuyin ang jq expression na ilalapat sa JSON manifest.

Halimbawa, upang tumugon sa mga pagbabago sa mga label para sa mga Deployment object, kailangan mong i-filter ang field labels palabas ng field metadata. Ang config ay magiging ganito:

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

Ginagawa ng expression na jqFilter na ito ang mahabang JSON manifest ng Deployment sa maikling JSON na may mga label:

Ipinapakilala ang shell-operator: naging mas madali ang paggawa ng mga operator para sa Kubernetes

Ang shell-operator ay tatakbo lamang sa hook kapag ang maikling JSON na ito ay nagbago, at ang mga pagbabago sa iba pang mga katangian ay hindi papansinin.

Konteksto ng paglulunsad ng Hook

Binibigyang-daan ka ng hook config na tumukoy ng ilang opsyon para sa mga kaganapan - halimbawa, 2 opsyon para sa mga kaganapan mula sa Kubernetes at 2 iskedyul:

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

Isang maliit na digression: oo, sinusuportahan ng shell-operator nagpapatakbo ng mga script ng istilong crontab. Higit pang mga detalye ay matatagpuan sa dokumentasyon.

Upang makilala kung bakit inilunsad ang hook, ang shell-operator ay gumagawa ng isang pansamantalang file at ipinapasa ang path dito sa isang variable sa hook BINDING_CONTEXT_TYPE. Ang file ay naglalaman ng isang paglalarawan ng JSON ng dahilan ng pagpapatakbo ng hook. Halimbawa, bawat 10 minuto tatakbo ang hook na may sumusunod na nilalaman:

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

... at sa Lunes magsisimula ito sa:

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

Para sa onKubernetesEvent Magkakaroon ng mas maraming JSON trigger, dahil naglalaman ito ng paglalarawan ng bagay:

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

Ang mga nilalaman ng mga patlang ay maaaring maunawaan mula sa kanilang mga pangalan, at higit pang mga detalye ay maaaring basahin sa dokumentasyon. Isang halimbawa ng pagkuha ng pangalan ng mapagkukunan mula sa isang field resourceName ang paggamit ng jq ay naipakita na sa isang hook na ginagaya ang mga lihim:

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

Maaari kang makakuha ng iba pang mga field sa katulad na paraan.

Ano ang susunod?

Sa imbakan ng proyekto, sa / mga halimbawa ng mga direktoryo, may mga halimbawa ng mga kawit na handang tumakbo sa isang kumpol. Kapag nagsusulat ng iyong sariling mga kawit, maaari mong gamitin ang mga ito bilang batayan.

Mayroong suporta para sa pagkolekta ng mga sukatan gamit ang Prometheus - ang mga available na sukatan ay inilalarawan sa seksyon METRICS.

Tulad ng maaari mong hulaan, ang shell-operator ay nakasulat sa Go at ipinamahagi sa ilalim ng lisensya ng Open Source (Apache 2.0). Kami ay magpapasalamat para sa anumang tulong sa pag-unlad proyekto sa GitHub: at mga bituin, at mga isyu, at mga kahilingan sa paghila.

Pag-alis ng belo ng lihim, ipapaalam din namin sa iyo na ang shell-operator ay maliit bahagi ng aming system na maaaring panatilihing napapanahon ang mga add-on sa cluster ng Kubernetes at magsagawa ng iba't ibang awtomatikong pagkilos. Magbasa nang higit pa tungkol sa sistemang ito sinabi literal sa Lunes sa HighLoad++ 2019 sa St. Petersburg - malapit na naming i-publish ang video at transcript ng ulat na ito.

May plano kaming buksan ang natitirang bahagi ng system na ito: ang addon-operator at ang aming koleksyon ng mga hook at module. By the way, addon-operator na pala available sa github, ngunit ang dokumentasyon para dito ay nasa daan pa rin. Ang paglabas ng koleksyon ng mga module ay pinlano para sa tag-init.

Manatiling nakatutok!

PS

Basahin din sa aming blog:

Pinagmulan: www.habr.com

Magdagdag ng komento