Γράφοντας έναν χειριστή για το Kubernetes στο Golang

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

Γράφοντας έναν χειριστή για το Kubernetes στο Golang

Αποφάσισα να γράψω αυτήν την ανάρτηση με ένα πραγματικό παράδειγμα μετά τις προσπάθειές μου να βρω τεκμηρίωση για τη δημιουργία ενός τελεστή για το Kubernetes, οι οποίες πέρασαν από τη μελέτη του κώδικα.

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

Μπορείτε να επιτύχετε αυτό που θέλετε αναθέτοντας σε έναν χρήστη μια ομάδα που έχει RoleBinding σε συγκεκριμένο Namespace и ClusterRole με δικαιώματα επεξεργασίας. Η αναπαράσταση YAML θα μοιάζει με αυτό:

---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: kubernetes-team-1
  namespace: team-1
subjects:
- kind: Group
  name: kubernetes-team-1
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: edit
apiGroup: rbac.authorization.k8s.io

(δεσμευτικό ρόλου.yamlΣε ακατέργαστος)

Δημιούργησε ένα RoleBinding Μπορείτε να το κάνετε χειροκίνητα, αλλά αφού περάσετε το σημάδι των εκατό ονομάτων, γίνεται μια κουραστική εργασία. Εδώ είναι χρήσιμοι οι χειριστές Kubernetes—σας επιτρέπουν να αυτοματοποιήσετε τη δημιουργία πόρων Kubernetes με βάση τις αλλαγές στους πόρους. Στην περίπτωσή μας θέλουμε να δημιουργήσουμε RoleBinding κατά τη δημιουργία Namespace.

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

(Σημείωση. μετάφρ.: εδώ και κάτω τα σχόλια στον κώδικα μεταφράζονται στα ρωσικά. Επιπλέον, η εσοχή έχει διορθωθεί σε κενά αντί για καρτέλες [προτείνεται στο Μετάβαση] αποκλειστικά για λόγους καλύτερης αναγνωσιμότητας εντός της διάταξης Habr. Μετά από κάθε καταχώριση υπάρχουν σύνδεσμοι προς το πρωτότυπο στο GitHub, όπου αποθηκεύονται τα σχόλια και οι καρτέλες στην αγγλική γλώσσα.)

func main() {
  // Устанавливаем вывод логов в консольный STDOUT
  log.SetOutput(os.Stdout)

  sigs := make(chan os.Signal, 1) // Создаем канал для получения сигналов ОС
  stop := make(chan struct{})     // Создаем канал для получения стоп-сигнала

  // Регистрируем получение SIGTERM в канале sigs
  signal.Notify(sigs, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) 

  // Goroutines могут сами добавлять себя в WaitGroup,
 // чтобы завершения их выполнения дожидались
  wg := &sync.WaitGroup{} 

  runOutsideCluster := flag.Bool("run-outside-cluster", false, "Set this flag when running outside of the cluster.")
  flag.Parse()
  // Создаем clientset для взаимодействия с кластером Kubernetes
  clientset, err := newClientSet(*runOutsideCluster)

  if err != nil {
    panic(err.Error())
  }

  controller.NewNamespaceController(clientset).Run(stop, wg)

  <-sigs // Ждем сигналов (до получения сигнала более ничего не происходит)
  log.Printf("Shutting down...")

  close(stop) // Говорим goroutines остановиться
  wg.Wait()   // Ожидаем, что все остановлено
}

(main.goΣε ακατέργαστος)

Κάνουμε τα εξής:

  1. Διαμορφώνουμε έναν χειριστή για συγκεκριμένα σήματα λειτουργικού συστήματος ώστε να προκαλεί χαριτωμένο τερματισμό του χειριστή.
  2. Χρησιμοποιούμε WaitGroupγια να σταματήσετε με χάρη όλες τις γορουτίνες πριν τερματίσετε την εφαρμογή.
  3. Παρέχουμε πρόσβαση στο σύμπλεγμα δημιουργώντας clientset.
  4. Εκκίνηση NamespaceController, στο οποίο θα βρίσκεται όλη η λογική μας.

Τώρα χρειαζόμαστε μια βάση για τη λογική, και στην περίπτωσή μας αυτή είναι αυτή που αναφέρθηκε NamespaceController:

// NamespaceController следит через Kubernetes API за изменениями
// в пространствах имен и создает RoleBinding для конкретного namespace.
type NamespaceController struct {
  namespaceInformer cache.SharedIndexInformer
  kclient           *kubernetes.Clientset
}

// NewNamespaceController создает новый NewNamespaceController
func NewNamespaceController(kclient *kubernetes.Clientset) *NamespaceController {
  namespaceWatcher := &NamespaceController{}

  // Создаем информер для слежения за Namespaces
  namespaceInformer := cache.NewSharedIndexInformer(
    &cache.ListWatch{
      ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
        return kclient.Core().Namespaces().List(options)
      },
      WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
        return kclient.Core().Namespaces().Watch(options)
      },
    },
    &v1.Namespace{},
    3*time.Minute,
    cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
  )

  namespaceInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
    AddFunc: namespaceWatcher.createRoleBinding,
  })

  namespaceWatcher.kclient = kclient
  namespaceWatcher.namespaceInformer = namespaceInformer

  return namespaceWatcher
}

(controller.goΣε ακατέργαστος)

Εδώ διαμορφώνουμε SharedIndexInformer, το οποίο θα περιμένει αποτελεσματικά (χρησιμοποιώντας προσωρινή μνήμη) για αλλαγές στους χώρους ονομάτων (διαβάστε περισσότερα για τους πληροφοριοδότες στο άρθρο "Πώς λειτουργεί πραγματικά ο προγραμματιστής Kubernetes;- περίπου. μετάφραση). Μετά από αυτό συνδεόμαστε EventHandler στον πληροφοριοδότη, έτσι ώστε κατά την προσθήκη ενός χώρου ονομάτων (Namespace) καλείται η συνάρτηση createRoleBinding.

Το επόμενο βήμα είναι να ορίσετε αυτή τη συνάρτηση createRoleBinding:

func (c *NamespaceController) createRoleBinding(obj interface{}) {
  namespaceObj := obj.(*v1.Namespace)
  namespaceName := namespaceObj.Name

  roleBinding := &v1beta1.RoleBinding{
    TypeMeta: metav1.TypeMeta{
      Kind:       "RoleBinding",
      APIVersion: "rbac.authorization.k8s.io/v1beta1",
    },
    ObjectMeta: metav1.ObjectMeta{
      Name:      fmt.Sprintf("ad-kubernetes-%s", namespaceName),
      Namespace: namespaceName,
    },
    Subjects: []v1beta1.Subject{
      v1beta1.Subject{
        Kind: "Group",
        Name: fmt.Sprintf("ad-kubernetes-%s", namespaceName),
      },
    },
    RoleRef: v1beta1.RoleRef{
      APIGroup: "rbac.authorization.k8s.io",
        Kind:     "ClusterRole",
        Name:     "edit",
    },
  }

  _, err := c.kclient.Rbac().RoleBindings(namespaceName).Create(roleBinding)

  if err != nil {
    log.Println(fmt.Sprintf("Failed to create Role Binding: %s", err.Error()))
  } else {
    log.Println(fmt.Sprintf("Created AD RoleBinding for Namespace: %s", roleBinding.Name))
  }
}

(controller.goΣε ακατέργαστος)

Παίρνουμε τον χώρο ονομάτων ως obj και να το μετατρέψετε σε αντικείμενο Namespace. Μετά ορίζουμε RoleBinding, με βάση το αρχείο YAML που αναφέρθηκε στην αρχή, χρησιμοποιώντας το παρεχόμενο αντικείμενο Namespace και δημιουργία RoleBinding. Τέλος, καταγράφουμε αν η δημιουργία ήταν επιτυχής.

Η τελευταία συνάρτηση που πρέπει να οριστεί είναι Run:

// Run запускает процесс ожидания изменений в пространствах имён
// и действия в соответствии с этими изменениями.
func (c *NamespaceController) Run(stopCh <-chan struct{}, wg *sync.WaitGroup) {
  // Когда эта функция завершена, пометим как выполненную
  defer wg.Done()

  // Инкрементируем wait group, т.к. собираемся вызвать goroutine
  wg.Add(1)

  // Вызываем goroutine
  go c.namespaceInformer.Run(stopCh)

  // Ожидаем получения стоп-сигнала
  <-stopCh
}

(controller.goΣε ακατέργαστος)

Εδώ μιλάμε WaitGroupότι εκκινούμε την γορουτίνα και μετά καλούμε namespaceInformer, που έχει οριστεί προηγουμένως. Όταν φτάσει το σήμα διακοπής, θα τερματίσει τη λειτουργία, ενημερώστε WaitGroup, το οποίο δεν εκτελείται πλέον και αυτή η συνάρτηση θα βγει.

Πληροφορίες σχετικά με τη δημιουργία και την εκτέλεση αυτής της δήλωσης σε ένα σύμπλεγμα Kubernetes μπορείτε να βρείτε στο αποθετήρια στο GitHub.

Αυτό είναι για τον χειριστή που δημιουργεί RoleBinding πότε Namespace στο σύμπλεγμα Kubernetes, έτοιμο.

Πηγή: www.habr.com

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