Rakstām operatoru Kubernetes uz Golang

PiezÄ«me:: Operatori (operators) ir atbalsta programmatÅ«ra Kubernetes, kas paredzēta rutÄ«nu uzdevumu automatizēŔanai attiecÄ«bā uz klastera objektiem noteiktos notikumos. Mēs jau esam rakstÄ«juÅ”i par operatoriem Å”ajā rakstā, kur aplÅ«kojām to pamatidejas un darbÄ«bas principus. Taču, ja iepriekŔējais materiāls bija drÄ«zāk skatÄ«jums no puses uz gatavu komponentu izmantoÅ”anu Kubernetes, tad Å”is jaunais raksts ir izstrādātāja/DevOps inženiera skatÄ«jums, kas mocās ar jauna operatora izstrādi.

Rakstām operatoru Kubernetes uz Golang

Šo ierakstu ar reāla piemēra aprakstu es nolēmu uzrakstīt pēc tam, kad mēģināju atrast dokumentāciju par operatora izveidi Kubernetes, kas iekļāva koda izpēti.

Piemērs, kuru mēs aprakstÄ«sim, ir Ŕāds: mÅ«su Kubernetes klasterÄ« katrs Namespace pārstāv kādas komandas smilÅ”u kasti, un mēs vēlējāmies ierobežot piekļuvi tām tā, lai komandas varētu spēlēt tikai savās smilÅ”u kastēs.

Vēlamo var panākt, pieŔķirot lietotājam grupu, kurai ir RoleBinding attiecÄ«bā uz Namespace un ClusterRole ar rediģēŔanas tiesÄ«bām. YAML attēlojums izskatÄ«sies Ŕādi:

---
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, vienā no saviem ziņojumiem raw)

Šādus RoleBinding var izveidot arÄ« manuāli, taču, kad ir jāizveido vairāk par simtu nosaukumu telpām, tas kļūst nogurdinoÅ”s darbs. TieÅ”i Å”eit noder Kubernetes operatori — tie ļauj automatizēt Kubernetes resursu izveidi, pamatojoties uz resursu izmaiņām. MÅ«su gadÄ«jumā mēs vēlamies izveidot RoleBinding nosaukumu telpā izveidoÅ”anas laikā. Namespace.

Vispirms noskaidrosim funkciju main, kas veic nepiecieÅ”amos iestatÄ«jumus operatora palaiÅ”anai un pēc tam izsauc operatora darbÄ«bu:

(PiezÄ«me:: Å”eit un turpmāk kodā komentāri ir tulkoti uz krievu valodu. Turklāt, ieloces ir labotas tā, lai izmantotu atstarpes vietā [Go ieteiktos] tabulācijas simbolus, lai uzlabotu lasāmÄ«bu Å”ajā Hubra noformējumā. Pēc katra saraksta ir saites uz oriÄ£inālu GitHub, kurÅ« ir saglabāti angļu valodas komentāri un tabulācijas simboli.

func main() {
  // Iestatām žurnālu izvadi konsolē STDOUT
  log.SetOutput(os.Stdout)

  sigs := make(chan os.Signal, 1) // Izveidojam kanālu, lai saņemtu OS signālus
  stop := make(chan struct{})     // Izveidojam kanālu, lai saņemtu apstopēŔanas signālu

  // ReÄ£istrējam SIGTERM saņemÅ”anu kanālā sigs
  signal.Notify(sigs, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) 

  // Goroutines var paŔas pievienot sevi WaitGroup,
  // lai gaidītu to izpildes pabeigŔanu
  wg := &sync.WaitGroup{} 

  runOutsideCluster := flag.Bool("run-outside-cluster", false, "LÅ«dzu, iestatiet Å”o karoga vērtÄ«bu, kad darbojas ārpus klusteru.")
  flag.Parse()
  // Izveidojam clientset, lai mijiedarbotos ar Kubernetes klasteri
  clientset, err := newClientSet(*runOutsideCluster)

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

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

  <-sigs // Gaidām signālus (pirms signāla saņemÅ”anas nekas netiek darÄ«ts)
  log.Printf("Aizveram...")

  close(stop) // Sakām goroutines apstāties
  wg.Wait()   // Gaidām, līdz viss ir apstādināts
}

(main.go, vienā no saviem ziņojumiem raw)

Mēs veicam sekojoÅ”o:

  1. Iestādām specifisku operētājsistēmas signālu apstrādātāju, lai izsauktu pareizu (graceful) operatora aizvērÅ”anu.
  2. Izmantojam WaitGroup, lai pareizi apstādinātu visas goroutines pirms programmas izbeigŔanas.
  3. NodroŔinām piekļuvi klasterim, izveidojot clientset.
  4. Sāksim NamespaceController, kurā būs izvietota visa mūsu loģika.

Tagad nepiecieÅ”ama pamata loÄ£ika, un mÅ«su gadÄ«jumā tā ir minētais 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, vienā no saviem ziņojumiem raw)

Å eit mēs iestatām SharedIndexInformer, kas efektÄ«vi (izmantojot keÅ”atmiņu) gaidÄ«s izmaiņas vārdnÄ«cās (vairāk par informatoriem lasiet rakstā "Kā patiesÄ«bā darbojas Kubernetes plānotājs?Ā» — piez. tulk.). Pēc tam mēs pievienojam EventHandler informatoram, tādējādi, pievienojot vārdnÄ«cu (Namespace), tiek izsaukta funkcija createRoleBinding.

Nākamais solis — definēt Å”o funkciju 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("Neizdevās izveidot Role Binding: %s", err.Error()))
  } else {
    log.Println(fmt.Sprintf("Izveidots AD RoleBinding vārdnīcai: %s", roleBinding.Name))
  }
}

(controller.go, vienā no saviem ziņojumiem raw)

Mēs iegÅ«stam vārdnÄ«cu kā obj un pārvērÅ”am to par objektu Namespace. Pēc tam definējam RoleBinding, pamatojoties uz iepriekÅ” minēto YAML failā, izmantojot norādÄ«to objektu Namespace un izveidojot RoleBinding. Visbeidzot, reÄ£istrējam, vai izveide noritēja veiksmÄ«gi.

Pēdējā funkcija, kas jānosaka, ir 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, vienā no saviem ziņojumiem raw)

Å eit mēs sakām WaitGroup, ka palaidÄ«sim goroutine un pēc tam izsauksim namespaceInformer, kas tika iepriekÅ” definēts. Kad saņemsim apstāŔanās signālu, tas pabeigs funkciju un paziņos WaitGroup, ka vairs netiek izpildÄ«ts, un Ŕī funkcija pabeigs savu darbu.

Informāciju par Ŕī operators bÅ«vēŔanu un palaiÅ”anu Kubernetes klasterÄ« var atrast repozitorijā GitHub.

Šeit operators, kas izveido RoleBinding parādīŔanās brīdī Namespace Kubernetes klasterī ir gatavs.

Avots: habr.com

Iegādājieties uzticamu tÄ«mekļa hostingu ar DDoS aizsardzÄ«bu, VPS VDS serveriem šŸ”„ Iegādājieties uzticamu tÄ«mekļa hostingu ar DDoS aizsardzÄ«bu, VPS VDS serveriem | ProHoster