Shkrimi i një operatori për Kubernetes në Golang

Shënim. përkth.: Operatorët janë softuer ndihmës për Kubernetes, i krijuar për të automatizuar ekzekutimin e veprimeve rutinë në objektet e grupimit kur ndodhin ngjarje të caktuara. Ne kemi shkruar tashmë për operatorët në Ky artikull, ku folën për idetë dhe parimet themelore të punës së tyre. Por nëse ky material ishte më shumë një pamje nga ana e funksionimit të komponentëve të gatshëm për Kubernetes, atëherë përkthimi i propozuar tani i artikullit të ri është tashmë vizioni i një zhvilluesi/inxhinieri DevOps të hutuar nga zbatimi i një operatori të ri.

Shkrimi i një operatori për Kubernetes në Golang

Vendosa ta shkruaj këtë postim me një shembull të jetës reale pas përpjekjeve të mia për të gjetur dokumentacion për krijimin e një operatori për Kubernetes, i cili kaloi përmes studimit të kodit.

Shembulli që do të përshkruhet është ky: në grupin tonë Kubernetes, secili Namespace përfaqëson mjedisin e sandbox të një ekipi dhe ne donim të kufizonim aksesin në to në mënyrë që ekipet të mund të luanin vetëm në sandboxet e tyre.

Ju mund të arrini atë që dëshironi duke i caktuar një përdoruesi një grup që ka RoleBinding të veçanta Namespace и ClusterRole me të drejta redaktimi. Përfaqësimi YAML do të duket kështu:

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

(lidhje me role.yaml, në i gjallë)

Krijo nje RoleBinding Mund ta bëni me dorë, por pasi të kaloni shenjën e njëqind hapësirave të emrave, bëhet një detyrë e lodhshme. Kjo është ajo ku operatorët Kubernetes vijnë në ndihmë - ata ju lejojnë të automatizoni krijimin e burimeve të Kubernetes bazuar në ndryshimet në burime. Në rastin tonë ne duam të krijojmë RoleBinding gjatë krijimit Namespace.

Para së gjithash, le të përcaktojmë funksionin maini cili bën konfigurimin e kërkuar për të ekzekutuar deklaratën dhe më pas e quan veprimin e deklaratës:

(Shënim. përkth.: këtu dhe më poshtë komentet në kod janë përkthyer në Rusisht. Përveç kësaj, dhëmbëzimi është korrigjuar në hapësira në vend të skedave [rekomandohet në Shko] vetëm për qëllimin e lexueshmërisë më të mirë brenda paraqitjes Habr. Pas çdo listimi ka lidhje me origjinalin në GitHub, ku ruhen komentet dhe skedat në gjuhën angleze.)

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()   // Ожидаем, что все остановлено
}

(kryesore.shko, në i gjallë)

Ne bëjmë sa vijon:

  1. Ne konfigurojmë një mbajtës për sinjale specifike të sistemit operativ për të shkaktuar ndërprerje të këndshme të operatorit.
  2. Ne përdorim WaitGrouppër të ndaluar me hijeshi të gjitha gorutinat përpara se të përfundoni aplikacionin.
  3. Ne ofrojmë qasje në grup duke krijuar clientset.
  4. Nisja NamespaceController, në të cilën do të vendoset e gjithë logjika jonë.

Tani na duhet një bazë për logjikën, dhe në rastin tonë kjo është ajo e përmendur 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
}

(kontrollues.shko, në i gjallë)

Këtu ne konfigurojmë SharedIndexInformer, i cili në mënyrë efektive (duke përdorur një cache) do të presë për ndryshime në hapësirat e emrave (lexoni më shumë rreth informatorëve në artikullin "Si funksionon realisht planifikuesi Kubernetes?"- përafërsisht. përkthimi). Pas kësaj ne lidhemi EventHandler te informatori, në mënyrë që kur të shtoni një hapësirë ​​emri (Namespace) thirret funksioni createRoleBinding.

Hapi tjetër është përcaktimi i këtij funksioni 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))
  }
}

(kontrollues.shko, në i gjallë)

Ne e marrim hapësirën e emrave si obj dhe e kthejnë atë në një objekt Namespace. Pastaj ne përcaktojmë RoleBinding, bazuar në skedarin YAML të përmendur në fillim, duke përdorur objektin e dhënë Namespace dhe duke krijuar RoleBinding. Së fundi, ne regjistrojmë nëse krijimi ishte i suksesshëm.

Funksioni i fundit që duhet përcaktuar është 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
}

(kontrollues.shko, në i gjallë)

Këtu po flasim WaitGroupqë ne lëshojmë gorutinën dhe më pas telefonojmë namespaceInformer, e cila është përcaktuar më parë. Kur të arrijë sinjali i ndalimit, ai do të përfundojë funksionin, informoni WaitGroup, i cili nuk ekzekutohet më dhe ky funksion do të dalë.

Informacion rreth ndërtimit dhe ekzekutimit të kësaj deklarate në një grupim Kubernetes mund të gjenden në depo në GitHub.

Kjo është ajo për operatorin që krijon RoleBinding kur Namespace në grupin Kubernetes, gati.

Burimi: www.habr.com

Shto një koment