Operatoriaus „Kubernetes“ rašymas Golange

Pastaba. vert.: operatoriai yra pagalbinė „Kubernetes“ programinė įranga, skirta automatizuoti įprastų veiksmų klasterio objektuose vykdymą, kai įvyksta tam tikri įvykiai. Apie operatorius jau rašėme Šis straipsnis, kur jie kalbėjo apie pagrindines savo darbo idėjas ir principus. Bet jei ši medžiaga buvo daugiau iš Kubernetes paruoštų komponentų veikimo pusės, tai dabar siūlomo naujo straipsnio vertimas jau yra kūrėjo / „DevOps“ inžinieriaus, suglumusio dėl naujo operatoriaus diegimo, vizija.

Operatoriaus „Kubernetes“ rašymas Golange

Nusprendžiau parašyti šį įrašą su realaus gyvenimo pavyzdžiu po to, kai bandžiau rasti Kubernetes operatoriaus kūrimo dokumentaciją, kuri buvo atlikta ištyrus kodą.

Bus aprašytas toks pavyzdys: mūsų „Kubernetes“ klasteryje kiekvienas Namespace atstovauja komandos smėlio dėžės aplinkai, todėl norėjome apriboti prieigą prie jų, kad komandos galėtų žaisti tik savo smėlio dėžėse.

Galite pasiekti tai, ko norite, priskirdami vartotojui grupę, kuri turi RoleBinding į konkrečius Namespace и ClusterRole su redagavimo teisėmis. YAML reprezentacija atrodys taip:

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

(vaidmenų įrišimas.yamlĮ žalias)

Sukurk viena RoleBinding Galite tai padaryti rankiniu būdu, tačiau peržengus šimto vardų erdvių ribą, tai tampa varginančia užduotimi. Čia praverčia „Kubernetes“ operatoriai – jie leidžia automatizuoti „Kubernetes“ išteklių kūrimą pagal išteklių pakeitimus. Mūsų atveju norime kurti RoleBinding kurdamas Namespace.

Visų pirma, apibrėžkime funkciją mainkuri atlieka reikalingą sąranką, kad paleistų sakinį, ir tada iškviečia teiginio veiksmą:

(Pastaba. vert.: čia ir žemiau kodo komentarai išversti į rusų kalbą. Be to, įtrauka buvo pakoreguota į tarpus, o ne [rekomenduojama eiti] skirtukus vien dėl geresnio skaitomumo Habr išdėstyme. Po kiekvieno įrašo yra nuorodos į originalą „GitHub“, kur saugomi komentarai ir skirtukai anglų kalba.)

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

(pagrindinis.goĮ žalias)

Mes atliekame šiuos veiksmus:

  1. Konfigūruojame konkrečių operacinės sistemos signalų tvarkyklę, kad būtų galima maloniai nutraukti operatoriaus darbą.
  2. Mes naudojame WaitGroupgrakščiai sustabdyti visas gorutinas prieš nutraukiant paraišką.
  3. Sukurdami suteikiame prieigą prie klasterio clientset.
  4. Paleisti NamespaceController, kuriame bus visa mūsų logika.

Dabar mums reikia logikos pagrindo, o mūsų atveju tai yra minėta 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
}

(valdiklis.goĮ žalias)

Čia mes sukonfigūruojame SharedIndexInformer, kuri efektyviai (naudodama talpyklą) lauks pakeitimų vardų erdvėse (daugiau apie informatorius skaitykite straipsnyje “Kaip iš tikrųjų veikia Kubernetes planavimo priemonė?"- apytiksliai vertimas). Po to mes jungiamės EventHandler informatoriui, kad pridedant vardų erdvę (Namespace) vadinama funkcija createRoleBinding.

Kitas žingsnis – apibrėžti šią funkciją 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))
  }
}

(valdiklis.goĮ žalias)

Vardų erdvę gauname kaip obj ir paversti jį objektu Namespace. Tada apibrėžiame RoleBinding, remiantis pradžioje minėtu YAML failu, naudojant pateiktą objektą Namespace ir kuriant RoleBinding. Galiausiai registruojame, ar sukūrimas pavyko.

Paskutinė apibrėžtina funkcija yra 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
}

(valdiklis.goĮ žalias)

Čia mes kalbame WaitGroupkad paleidžiame gorutiną ir tada skambiname namespaceInformer, kuris buvo apibrėžtas anksčiau. Atėjus stabdymo signalui, jis nutraukia funkciją, informuoja WaitGroup, kuri nebevykdoma, ir ši funkcija bus uždaryta.

Informacijos apie šio pareiškimo kūrimą ir vykdymą Kubernetes klasteryje rasite „GitHub“ saugyklos.

Tai operatoriui, kuris kuria RoleBinding kada Namespace „Kubernetes“ klasteryje, paruošta.

Šaltinis: www.habr.com

Добавить комментарий