Παρουσίαση του shell-operator: η δημιουργία τελεστών για το Kubernetes μόλις έγινε ευκολότερη

Υπάρχουν ήδη άρθρα στο ιστολόγιό μας που μιλούν για δυνατότητες χειριστή στο Kubernetes και πως γράψτε μόνοι σας έναν απλό χειριστή. Αυτή τη φορά θα θέλαμε να παρουσιάσουμε στην προσοχή σας τη λύση Ανοικτού Κώδικα, η οποία οδηγεί τη δημιουργία τελεστών σε ένα εξαιρετικά εύκολο επίπεδο - δείτε κέλυφος-χειριστής!

Γιατί;

Η ιδέα ενός χειριστή κελύφους είναι αρκετά απλή: εγγραφείτε σε συμβάντα από αντικείμενα Kubernetes και όταν ληφθούν αυτά τα συμβάντα, ξεκινήστε ένα εξωτερικό πρόγραμμα, παρέχοντάς του πληροφορίες σχετικά με το συμβάν:

Παρουσίαση του shell-operator: η δημιουργία τελεστών για το Kubernetes μόλις έγινε ευκολότερη

Η ανάγκη για αυτό προέκυψε όταν κατά τη λειτουργία των clusters άρχισαν να εμφανίζονται μικρές εργασίες που θέλαμε πολύ να αυτοματοποιήσουμε με τον σωστό τρόπο. Όλες αυτές οι μικρές εργασίες επιλύθηκαν χρησιμοποιώντας απλά σενάρια bash, αν και, όπως γνωρίζετε, είναι καλύτερο να γράφετε τελεστές στο Golang. Προφανώς, η επένδυση στην ανάπτυξη πλήρους κλίμακας ενός χειριστή για κάθε τόσο μικρή εργασία θα ήταν αναποτελεσματική.

Χειριστής σε 15 λεπτά

Ας δούμε ένα παράδειγμα του τι μπορεί να αυτοματοποιηθεί σε ένα σύμπλεγμα Kubernetes και πώς μπορεί να βοηθήσει ο χειριστής του κελύφους. Ένα παράδειγμα θα ήταν το εξής: αναπαραγωγή ενός μυστικού για πρόσβαση στο μητρώο του docker.

Οι ομάδες που χρησιμοποιούν εικόνες από ένα ιδιωτικό μητρώο πρέπει να περιέχουν στο μανιφέστο τους έναν σύνδεσμο προς ένα μυστικό με δεδομένα για πρόσβαση στο μητρώο. Αυτό το μυστικό πρέπει να δημιουργηθεί σε κάθε χώρο ονομάτων πριν από τη δημιουργία ομάδων. Αυτό μπορεί να γίνει χειροκίνητα, αλλά αν ρυθμίσουμε δυναμικά περιβάλλοντα, τότε ο χώρος ονομάτων για μία εφαρμογή θα γίνει πολύς. Κι αν επίσης δεν υπάρχουν 2-3 εφαρμογές... ο αριθμός των μυστικών γίνεται πολύ μεγάλος. Και κάτι ακόμα για τα μυστικά: Θα ήθελα να αλλάζω το κλειδί για πρόσβαση στο μητρώο από καιρό σε καιρό. Τελικά, χειρωνακτικές λειτουργίες ως λύση εντελώς αναποτελεσματική — πρέπει να αυτοματοποιήσουμε τη δημιουργία και την ενημέρωση των μυστικών.

Απλός αυτοματισμός

Ας γράψουμε ένα σενάριο φλοιού που εκτελείται μία φορά κάθε N δευτερόλεπτα και ελέγχει τους χώρους ονομάτων για την παρουσία ενός μυστικού, και αν δεν υπάρχει μυστικό, τότε δημιουργείται. Το πλεονέκτημα αυτής της λύσης είναι ότι μοιάζει με script shell in cron - μια κλασική και κατανοητή προσέγγιση για όλους. Το μειονέκτημα είναι ότι στο μεσοδιάστημα μεταξύ των εκκινήσεών του μπορεί να δημιουργηθεί ένας νέος χώρος ονομάτων και για κάποιο χρονικό διάστημα θα παραμείνει χωρίς μυστικό, γεγονός που θα οδηγήσει σε σφάλματα κατά την εκκίνηση pods.

Αυτοματισμός με χειριστή κελύφους

Για να λειτουργήσει σωστά το σενάριό μας, η κλασική εκκίνηση του cron πρέπει να αντικατασταθεί με μια εκκίνηση όταν προστίθεται ένας χώρος ονομάτων: σε αυτήν την περίπτωση, μπορείτε να δημιουργήσετε ένα μυστικό πριν το χρησιμοποιήσετε. Ας δούμε πώς μπορείτε να το εφαρμόσετε χρησιμοποιώντας το shell-operator.

Αρχικά, ας δούμε το σενάριο. Τα σενάρια με όρους κελύφους χειριστή ονομάζονται hook. Κάθε γάντζος όταν τρέχει με μια σημαία --config ενημερώνει τον χειριστή του κελύφους για τις δεσμεύσεις του, δηλ. σχετικά με τις εκδηλώσεις που θα πρέπει να ξεκινήσει. Στην περίπτωσή μας θα χρησιμοποιήσουμε onKubernetesEvent:

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

Περιγράφεται εδώ ότι μας ενδιαφέρει να προσθέσουμε συμβάντα (add) αντικείμενα του τύπου namespace.

Τώρα πρέπει να προσθέσετε τον κώδικα που θα εκτελεστεί όταν συμβεί το συμβάν:

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

Εξαιρετική! Το αποτέλεσμα ήταν ένα μικρό, όμορφο σενάριο. Για να την «αναβιώσετε», απομένουν δύο βήματα: προετοιμάστε την εικόνα και εκκινήστε την στο σύμπλεγμα.

Προετοιμασία εικόνας με γάντζο

Αν κοιτάξετε το σενάριο, μπορείτε να δείτε ότι χρησιμοποιούνται οι εντολές kubectl и jq. Αυτό σημαίνει ότι η εικόνα πρέπει να έχει τα ακόλουθα στοιχεία: το άγκιστρο μας, έναν χειριστή φλοιού που θα ακούει συμβάντα και θα εκτελεί το άγκιστρο και τις εντολές που χρησιμοποιεί το άγκιστρο (kubectl και jq). Το Hub.docker.com έχει ήδη μια έτοιμη εικόνα στην οποία είναι συσκευασμένα shell-operator, kubectl και jq. Το μόνο που μένει είναι να προσθέσετε ένα απλό γάντζο 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

Τρέξιμο σε ένα σύμπλεγμα

Ας δούμε ξανά το άγκιστρο και αυτή τη φορά γράψτε ποιες ενέργειες και με ποια αντικείμενα εκτελεί στο σύμπλεγμα:

  1. εγγράφεται σε συμβάντα δημιουργίας χώρου ονομάτων.
  2. δημιουργεί ένα μυστικό σε χώρους ονομάτων διαφορετικούς από αυτόν στον οποίο εκκινείται.

Αποδεικνύεται ότι το pod στο οποίο θα εκκινηθεί η εικόνα μας πρέπει να έχει δικαιώματα για να κάνει αυτές τις ενέργειες. Αυτό μπορεί να γίνει δημιουργώντας τον δικό σας Λογαριασμό Υπηρεσίας. Η άδεια πρέπει να γίνει με τη μορφή ClusterRole και ClusterRoleBinding, επειδή μας ενδιαφέρουν αντικείμενα από ολόκληρο το σύμπλεγμα.

Η τελική περιγραφή στο YAML θα μοιάζει κάπως έτσι:

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

Μπορείτε να εκκινήσετε τη συναρμολογημένη εικόνα ως απλή Ανάπτυξη:

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

Για ευκολία, δημιουργείται ένας ξεχωριστός χώρος ονομάτων όπου θα εκκινηθεί ο τελεστής φλοιού και θα εφαρμοστούν οι δημιουργημένες δηλώσεις:

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

Αυτό είναι όλο: ο χειριστής του κελύφους θα ξεκινήσει, θα εγγραφεί σε συμβάντα δημιουργίας χώρου ονομάτων και θα εκτελέσει το άγκιστρο όταν χρειάζεται.

Παρουσίαση του shell-operator: η δημιουργία τελεστών για το Kubernetes μόλις έγινε ευκολότερη

Έτσι, η ένα απλό σενάριο φλοιού μετατράπηκε σε πραγματικό χειριστή για το Kubernetes και λειτουργεί ως μέρος ενός συμπλέγματος. Και όλα αυτά χωρίς την περίπλοκη διαδικασία ανάπτυξης χειριστών στο Golang:

Παρουσίαση του shell-operator: η δημιουργία τελεστών για το Kubernetes μόλις έγινε ευκολότερη

Υπάρχει ένα άλλο παράδειγμα για αυτό το θέμα...Παρουσίαση του shell-operator: η δημιουργία τελεστών για το Kubernetes μόλις έγινε ευκολότερη

Θα αποκαλύψουμε το νόημά του με περισσότερες λεπτομέρειες σε μία από τις ακόλουθες δημοσιεύσεις.

φιλτράρισμα

Η παρακολούθηση αντικειμένων είναι καλή, αλλά συχνά υπάρχει ανάγκη αντίδρασης αλλαγή ορισμένων ιδιοτήτων αντικειμένων, για παράδειγμα, για να αλλάξετε τον αριθμό των αντιγράφων στο Deployment ή για να αλλάξετε τις ετικέτες αντικειμένων.

Όταν φθάνει ένα συμβάν, ο χειριστής φλοιού λαμβάνει το μανιφέστο JSON του αντικειμένου. Μπορούμε να επιλέξουμε τις ιδιότητες που μας ενδιαφέρουν σε αυτό το JSON και να εκτελέσουμε το άγκιστρο μόνο όταν αλλάζουν. Υπάρχει πεδίο για αυτό jqFilter, όπου πρέπει να καθορίσετε την έκφραση jq που θα εφαρμοστεί στο μανιφέστο JSON.

Για παράδειγμα, για να απαντήσετε σε αλλαγές σε ετικέτες για αντικείμενα ανάπτυξης, πρέπει να φιλτράρετε το πεδίο labels έξω από το γήπεδο metadata. Η διαμόρφωση θα είναι ως εξής:

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

Αυτή η έκφραση jqFilter μετατρέπει το μακρύ μανιφέστο JSON του Deployment σε σύντομο JSON με ετικέτες:

Παρουσίαση του shell-operator: η δημιουργία τελεστών για το Kubernetes μόλις έγινε ευκολότερη

Ο χειριστής φλοιού θα εκτελέσει το άγκιστρο μόνο όταν αλλάξει αυτό το σύντομο JSON και οι αλλαγές σε άλλες ιδιότητες θα αγνοηθούν.

Πλαίσιο εκκίνησης γάντζου

Το hook config σάς επιτρέπει να καθορίσετε πολλές επιλογές για συμβάντα - για παράδειγμα, 2 επιλογές για συμβάντα από το Kubernetes και 2 χρονοδιαγράμματα:

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

Μια μικρή παρέκκλιση: ναι, υποστηρίζει το shell-operator εκτέλεση σεναρίων σε στυλ crontab. Περισσότερες λεπτομέρειες μπορείτε να βρείτε στο τεκμηρίωση.

Για να διακρίνει γιατί ξεκίνησε το άγκιστρο, ο χειριστής του κελύφους δημιουργεί ένα προσωρινό αρχείο και μεταβιβάζει τη διαδρομή προς αυτό σε μια μεταβλητή στο άγκιστρο BINDING_CONTEXT_TYPE. Το αρχείο περιέχει μια περιγραφή JSON για τον λόγο εκτέλεσης του άγκιστρου. Για παράδειγμα, κάθε 10 λεπτά το άγκιστρο θα λειτουργεί με το ακόλουθο περιεχόμενο:

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

... και τη Δευτέρα θα ξεκινήσει με αυτό:

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

Για onKubernetesEvent Θα υπάρξουν περισσότεροι κανόνες ενεργοποίησης JSON, επειδή περιέχει μια περιγραφή του αντικειμένου:

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

Τα περιεχόμενα των πεδίων μπορούν να γίνουν κατανοητά από τα ονόματά τους και περισσότερες λεπτομέρειες μπορείτε να διαβάσετε τεκμηρίωση. Ένα παράδειγμα λήψης ονόματος πόρου από ένα πεδίο resourceName Η χρήση jq έχει ήδη παρουσιαστεί σε ένα άγκιστρο που αναπαράγει μυστικά:

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

Μπορείτε να αποκτήσετε άλλα πεδία με παρόμοιο τρόπο.

Ποιο είναι το επόμενο;

Στο αποθετήριο του έργου, στο /examples καταλόγους, υπάρχουν παραδείγματα αγκίστρων που είναι έτοιμα να τρέξουν σε ένα σύμπλεγμα. Όταν γράφετε τα δικά σας άγκιστρα, μπορείτε να τα χρησιμοποιήσετε ως βάση.

Υπάρχει υποστήριξη για τη συλλογή μετρήσεων χρησιμοποιώντας το Prometheus - οι διαθέσιμες μετρήσεις περιγράφονται στην ενότητα ΜΕΤΡΕΣ.

Όπως μπορείτε να μαντέψετε, ο τελεστής κελύφους είναι γραμμένος στο Go και διανέμεται με άδεια ανοιχτού κώδικα (Apache 2.0). Θα είμαστε ευγνώμονες για οποιαδήποτε αναπτυξιακή βοήθεια έργο στο GitHub: και αστέρια, και ζητήματα, και αιτήματα έλξης.

Αίροντας το πέπλο της μυστικότητας, θα σας ενημερώσουμε επίσης ότι η shell-operator είναι μικρό μέρος του συστήματός μας που μπορεί να διατηρεί ενημερωμένα τα πρόσθετα εγκατεστημένα στο σύμπλεγμα Kubernetes και να εκτελεί διάφορες αυτόματες ενέργειες. Διαβάστε περισσότερα για αυτό το σύστημα είπα κυριολεκτικά τη Δευτέρα στο HighLoad++ 2019 στην Αγία Πετρούπολη - σύντομα θα δημοσιεύσουμε το βίντεο και τη μεταγραφή αυτής της αναφοράς.

Έχουμε ένα σχέδιο για να ανοίξουμε το υπόλοιπο σύστημα: τον χειριστή του πρόσθετου και τη συλλογή μας από άγκιστρα και μονάδες. Παρεμπιπτόντως, ο τελεστής πρόσθετου είναι ήδη διαθέσιμο στο github, αλλά η τεκμηρίωση για αυτό είναι ακόμα στο δρόμο. Η κυκλοφορία της συλλογής των ενοτήτων έχει προγραμματιστεί για το καλοκαίρι.

Μείνετε συντονισμένοι!

PS

Διαβάστε επίσης στο blog μας:

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο