Kirjutame Kubernetes'e operaatori Golang'is

Märkus tõlke kohta.: Operaatorid (operators) on Kubernetes'e abimoodulid, mis on loodud automaatseks rutiinsete toimingute tegemiseks klastrite objektide üle teatud sündmuste korral. Oleme juba kirjutanud operaatori kohta selles artiklis, kus rääkisime nende töö põhialustest ja põhimõtetest. Kui eelmine materjal oli pigem pilk valmi komponentide kasutamisele Kubernetes'es, siis nüüd pakutud uus artikli tõlge on arendaja/DevOps-inseneri vaatenurk, kes on hädas uue operaatori rakendamisega.

Kirjutame Kubernetes'e operaatori Golang'is

Selle postituse reaalse elu näitena otsustasin kirjutada pärast oma katseid leida dokumentatsiooni Kubernetes'e operaatori loomise kohta, mis hõlmas koodi uurimist.

Näide, mida kirjeldame, on järgmine: meie Kubernetes'e klastris iga Namespace esindab mingisuguse meeskonna liivakasti, ja soovisime juurdepääsu piirata nii, et meeskonnad saaksid mängida ainult oma liivakastides.

Selle saavutamiseks on vajalik määrata kasutajale grupp, millel on RoleBinding konkreetsete Namespace ja ClusterRole redigeerimisõ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 toores)

Saab luua ka käsitsi, kuid pärast saja nimede ruumi ületamist muutub see tüütuks. Just siin aitavad Kubernetes'e operaatorid — nad võimaldavad automatiseerida Kubernetes'e ressursside loomist, tuginedes ressursside muudatustele. Meie puhul tahame luua RoleBinding loome loomisel RoleBinding Esimesena määratleme funktsiooni Namespace.

, mis teeb vajalikud seaded operaatori käivitamiseks ja seejärel kutsub operaatori tegevuse: main: siin ja edasi on koodis kommentaarid tõlgitud eesti keelde. Lisaks on sisestused parandatud ruumideks, mitte [Go's soovitatavaks] tabulatsioonideks ainult parema loetavuse huvides Habr'i disaini kontekstis. Iga loendi järel on lingid originaalile GitHub'is, kus on säilitatud ingliskeelsed kommentaarid ja tabulatsioonid.)

(Märkus tõlke kohta.: siin ja edaspidi on koodi kommentaarid tõlgitud vene keelde. Lisaks on sisestused parandatud ruutude asemel [Go soovitatud] tühikuteks, et saavutada paremat loetavust Habra vormindamise raames. Iga nimekirja järel on lingid originaalidele GitHubis, kus on säilitatud ingliskeelsed kommentaarid ja ruudud.)

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

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

  	// Registreerime SIGTERM signaalide saamiseks sigs kanalisse
  signal.Notify(sigs, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) 

  	// Goroutines saavad end ise lisada WaitGroup'i,
 	// et oodata nende lõpetamist
  wg := &sync.WaitGroup{} 

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

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

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

  <-sigs 	// Ootame signaale (enne signaali saamist ei juhtu rohkem midagi)
  log.Printf("Suleme...\n")

  close(stop) 	// Ütleme goroutines'ile, et peatuda
  wg.Wait()   	// Ootame, kuni kõik on peatatud
}

(main.go, in toores)

Teeme järgmist:

  1. Konfigureerime spetsiaalse operatsioonisüsteemi signaalide töötlejat, et kutsuda esile korrektne (graceful) operaatori lõpetamine.
  2. Kasutame WaitGroup, et peatada kõik goroutines enne rakenduse lõpetamist.
  3. Pakume juurdepääsu klastrile ülesande loomisega clientset.
  4. Käivitame NamespaceController, kuhu peame kõik oma loogika paigutama.

Nüüd on meil vajalik loogika alus, ja meie jaoks 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 toores)

Siin seadistame SharedIndexInformer, mis ootab tõhusalt (kasutades vahemälu) muudatusi nimedevahelistes ruumides (lisainformatsiooni informeerijate kohta leiate artiklist „Kuidas Kubernetes’i ajakava tegelikult töötab?» — märkus tõlke kohta.). Seejärel ühendame EventHandler informeerijaga, mis tähendab, et nimedevahelise ruumi lisamisel (Namespace) kutsutakse välja funktsioon createRoleBinding.

Järgmine samm — määrata 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("Rolli sidumise loomine ebaõnnestus: %s", err.Error()))
  } else {
    log.Println(fmt.Sprintf("Loodud AD Rolli sidumine nimedevahelise ruumi jaoks: %s", roleBinding.Name))
  }
}

(controller.go, in toores)

Saame nimedevahelise ruumi nagu obj ja teisendame selle objektiks Namespace. Seejärel määrame RoleBinding, lähtudes eelmainitud YAML-failist, kasutades antud objekti Namespace ja luues RoleBinding. Lõpuks logime, kas loomine õnnestus.

Viimane funktsioon, mille määratlemine on vajalik, on Käita:

// 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 toores)

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

Teavet selle operaatori kogumise ja käitamise kohta Kubernetes'i klastris leiate GitHubi hoidlast.

Sellega operaator, mis loob RoleBinding ilmudes Namespace Kubernetes'i klastris, on valmis.

Allikas: habr.com

Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster