Ons skryf 'n operateur vir Kubernetes in Golang

Let wel. vertaal.: Operateurs is hulpprogrammatuur vir Kubernetes wat ontwerp is om die uitvoering van roetine-aksies op groepvoorwerpe tydens sekere gebeurtenisse te outomatiseer. Ons het reeds geskryf oor operateurs in Hierdie artikelwaar hulle oor die fundamentele idees en beginsels van hul werk gepraat het. Maar as daardie materiaal eerder 'n siening was van die kant van die werking van klaargemaakte komponente vir Kubernetes, dan is die vertaling van die nuwe artikel wat nou voorgestel word, reeds die visie van 'n ontwikkelaar / DevOps-ingenieur, verbaas oor die implementering van 'n nuwe operateur .

Ons skryf 'n operateur vir Kubernetes in Golang

Ek het besluit om hierdie pos met 'n werklike voorbeeld te skryf nadat my pogings om dokumentasie te vind oor die skep van 'n operateur vir Kubernetes, deur die studie van die kode gegaan het.

Die voorbeeld wat beskryf sal word, is die volgende: in ons Kubernetes-kluster, elk Namespace verteenwoordig 'n span se sandbox-omgewing, en ons wou toegang daartoe beperk sodat spanne net in hul eie sandboxes kan speel.

Jy kan bereik wat jy wil deur 'n gebruiker toe te wys aan 'n groep wat het RoleBinding spesifiek Namespace ΠΈ ClusterRole met redigeerregte. Die YAML-voorstelling sal soos volg lyk:

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

(rolbinding.yamlIn rou)

Skep so RoleBinding jy kan dit met die hand doen, maar sodra jy meer as honderd naamruimtes kry, word dit 'n vervelige taak. Dit is waar Kubernetes-operateurs handig te pas kom - hulle laat jou toe om die skepping van Kubernetes-hulpbronne te outomatiseer op grond van veranderinge in hulpbronne. In ons geval wil ons skep RoleBinding terwyl jy skep Namespace.

Eerstens, laat ons die funksie definieer main, wat die vereiste opstelling doen om die stelling uit te voer en dan die stellingsaksie aanroep:

(Let wel. vertaal.: hierna word die opmerkings in die kode in Russies vertaal. Daarbenewens word die inkeping gekorrigeer vir spasies in plaas van [aanbeveel in Gaan] oortjies uitsluitlik vir die doel van beter leesbaarheid binne die raamwerk van Habr se uitleg. Na elke aanbieding is daar skakels na die oorspronklike op GitHub, waar Engelstalige opmerkings en oortjies gestoor word.)

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()   // ОТидаСм, Ρ‡Ρ‚ΠΎ всС остановлСно
}

(hoof.gaanIn rou)

Ons doen die volgende:

  1. Ons het 'n hanteerder vir spesifieke bedryfstelselseine opgestel om die operateur grasieus te laat verlaat.
  2. Ons gebruik WaitGroupom alle goroutines grasieus te stop voordat die toepassing beΓ«indig word.
  3. Ons bied toegang tot die groepering deur te skep clientset.
  4. Begin NamespaceController, waarin al ons logika geleΓ« sal wees.

Nou het ons 'n basis vir logika nodig, en in ons geval is dit die genoemde 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
}

(kontroleerder.gaanIn rou)

Hier sit ons SharedIndexInformer, wat doeltreffend (met behulp van die kas) sal wag vir naamruimteveranderings (lees meer oor informante in die artikel "Hoe werk die Kubernetes-skeduleerder eintlik?"- ongeveer. vertaal.). Daarna verbind ons EventHandler aan die informant, waardeur, wanneer 'n naamruimte bygevoeg word (Namespace) word die funksie genoem createRoleBinding.

Die volgende stap is om hierdie funksie te definieer 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))
  }
}

(kontroleerder.gaanIn rou)

Ons kry die naamruimte as obj en omskep dit na 'n voorwerp Namespace. Dan definieer ons RoleBinding, gebaseer op die YAML-lΓͺer wat aan die begin genoem is, deur die verskafde voorwerp te gebruik Namespace en skep RoleBinding. Ten slotte teken ons aan of die skepping suksesvol was.

Die laaste funksie wat gedefinieer moet word is βˆ’ 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
}

(kontroleerder.gaanIn rou)

Hier praat ons WaitGroupdat ons die goroutine begin en dan bel namespaceInformerwat voorheen gedefinieer is. Wanneer 'n stopsein inkom, sal dit die funksie beΓ«indig, laat weet WaitGroup, wat nie meer uitgevoer word nie, en hierdie funksie sal verlaat.

Inligting oor die bou en uitvoer van hierdie stelling op 'n Kubernetes-kluster kan gevind word by bewaarplekke op GitHub.

Op hierdie stelling, wat skep RoleBinding wanneer Namespace in 'n Kubernetes-kluster, gereed.

Bron: will.com

Voeg 'n opmerking