Kubernetes'e operaatori kirjutamine Golangis

Märk. tõlge.: Operatsioonid (operators) on Kubernetes'e abijuhend, mille eesmärk on automatiseerida rutiinseid toiminguid klastrielementidega teatud sündmuste korral. Oleme juba rääkinud operaatoritest selles artiklis, kus käsitlesime nende töö põhialuseid ja printsiipe. Kui eelmine materjal andis rohkem praktilise ülevaate valmis komponentidest, siis käesolev tõlge uue artikli kohta on arendaja/DevOps-inseneri vaatenurk, kes on mures uue operaatori rakendamise üle.

Kubernetes'e operaatori kirjutamine Golangis

Otsustasin kirjutada selle postituse elust illustreeriva näite põhjal pärast oma katseid leida dokumentatsiooni Kubernetes'e operaatori loomise kohta, mis hõlmas ka koodi uurimist.

Näide, mida kirjeldan, on järgmine: meie Kubernetes'e klastris esindab iga Namespaces mingi tiimi liivakasti keskkonda, ning soovisime piirata ligipääsu nii, et tiimid saaksid mängida vaid oma liivakastides.

Soovitud tulemuse saavutamiseks võib kasutajale määrata rühma, millel on Rolli seondus konkreetsete Namespaces ja ClusterRole redigeerimise õigustega. YAML-esitus näeb välja järgmine:

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

(rolebinding.yaml, in raw)

Seda luua Rolli seondus on võimalik ka käsitsi, kuid kui üle 100 nimetuse märk ületatakse, muutub see tüütuks. Just siin tulevad appi Kubernetes operatsioonid — need võimaldavad automatiseerida Kubernetes ressursside loomist, tuginedes muudatustele ressurssides. Meie puhul soovime luua Rolli seondus loomisel Namespaces.

Esimese asjana määratleme funktsiooni main, mis rakendab vajalikud seadistused operaatori käivitamiseks ja seejärel kutsub operaatori tegevuse:

(Märk. tõlge.: siit alates on koodis kommentaarid tõlgitud eesti keelde. Samuti on taanded parandatud tükkideks, mitte [Go-s soovitatud] tabideks, et muuta lugemine lihtsamaks Habr arvustuste vormindamise kontekstis. Iga loendi järel on lingid originaalidele GitHubis, kus ingliskeelsed kommentaarid ja tabid on säilinud.

func main() {
  // Seame logide väljund konsooli STDOUT
  log.SetOutput(os.Stdout)

  sigs := make(chan os.Signal, 1) // Loome kanali operatsioonisüsteemi signaalide vastuvõtmiseks
  stop := make(chan struct{})     // Loome kanali stopp-signaali vastuvõtmiseks

  // Registreerime SIGTERM signaali kanalis sigs
  signal.Notify(sigs, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) 

  // Goroutines võivad end ise WaitGroup'i lisada,
  // et nende täitmise lõpetamist oodata
  wg := &sync.WaitGroup{} 

  runOutsideCluster := flag.Bool("run-outside-cluster", false, "Käivitage see lipp, kui töötate väljaspool klastrit.")
  flag.Parse()
  // Loome clientset'i Kubernetes klastri suhtlemiseks
  clientset, err := newClientSet(*runOutsideCluster)

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

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

  < -sigs // Ootame signaale (kuni signaali saamiseni ei toimu midagi muud)
  log.Printf("Lõpetame töö...")

  close(stop) // Ütleme goroutines'e peatuda
  wg.Wait()   // Ootame, et kõik on lõpetatud
}

(main.go, in raw)

Me teeme järgmist:

  1. Seame operatsioonisüsteemi konkreetsete signaalide töötleja, et käivitada korrektne (graceful) lõppemine operaatorile.
  2. Kasutame WaitGroup, et korrektselt peatada kõik goroutines enne rakenduse sulgemist.
  3. Anname ligipääsu klastrile loomisega clientset.
  4. Käivitage NamespaceController, kus asub kogu meie loogika.

Nüüd on vajalik loogika alus, ja meie puhul on see mainitud 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, in raw)

Siin seadistame SharedIndexInformer, mis tõhusalt (kasutades vahemälu) ootab muutusi nimedepartemangudes (lisainfot informeri kohta lugege artiklist „Kuidas Kubernetes'i planeerija tegelikult töötab?» — kommentaar tõlkijalt.). Pärast seda ühendame EventHandler informeri külge, mis võimaldab nimede lisamisel (Namespaces) kutsuda välja funktsiooni createRoleBinding.

Järgmine samm on määratleda see funktsioon 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, in raw)

Saame nimede osana obj ja teisendame selle objektiks Namespaces. Seejärel määratleme Rolli seondus, põhinedes eelnevalt mainitud YAML-failil, kasutades antud objekti Namespaces ja luues Rolli seondus. Lõpuks logime, kas loomine läks korda.

Viimane funktsioon, mille määratlemine on vajalik, on 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, in raw)

Siin räägime WaitGroup, et käivitame goroutine'i ja seejärel kutsume namespaceInformer, mis on eelnevalt määratletud. Kui tuleb peatamissignaal, lõpetab see funktsiooni, teatab WaitGroup, et see enam ei tööta, ja see funktsioon lõpetab oma tegevuse.

Teavet selle operaatori kokkupaneku ja käivitamise kohta Kubernetes'i klastri sees leiate GitHubi repostaariumist.

Sellega operaator, mis loob Rolli seondus Kubernetes'i klastri tekkides, on valmis. Namespaces Märkus: Operaatorid (operators) on Kubernetes'i abirakendused, mille eesmärk on automatiseerida rutiinseid tegevusi klastrite objektide üle teatud sündmuste korral. Oleme juba kirjutanud operaatoritest selles artiklis, kus rääkisime nende peamistest ideedest ja tööprintsiipidest. Kui eelmine materjal keskendus pigem valmis komponentide kasutamisele Kubernetes'is, siis nüüd pakutav

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster