Skribante funkciigiston por Kubernetes en Golang

Notu. transl.: Operaciantoj estas helpa programaro por Kubernetes, dizajnita por aŭtomatigi la plenumon de rutinaj agoj sur aretobjektoj kiam certaj okazaĵoj okazas. Ni jam skribis pri operatoroj en ĉi tiu artikolo, kie ili parolis pri la fundamentaj ideoj kaj principoj de sia laboro. Sed se tiu materialo estis pli de vido de la flanko de funkciigado de pretaj komponantoj por Kubernetes, tiam la nun proponita traduko de la nova artikolo jam estas la vizio de programisto/DevOps-inĝeniero konfuzita de la efektivigo de nova funkciigisto.

Skribante funkciigiston por Kubernetes en Golang

Mi decidis skribi ĉi tiun afiŝon kun reala ekzemplo post miaj provoj trovi dokumentadon pri kreado de operatoro por Kubernetes, kiu trapasis studado de la kodo.

La ekzemplo, kiu estos priskribita, estas ĉi tiu: en nia Kubernetes-grupo, ĉiu Namespace reprezentas la sablokeston de teamo, kaj ni volis limigi aliron al ili tiel ke teamoj nur povu ludi en siaj propraj sablokestoj.

Vi povas atingi tion, kion vi volas asignante al uzanto grupon kiu havas RoleBinding al specifa Namespace и ClusterRole kun redaktaj rajtoj. La YAML-reprezento aspektos jene:

---
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.yamlen kruda)

Kreu unu RoleBinding Vi povas fari ĝin permane, sed post transpaso de la cent nomspacoj, ĝi fariĝas teda tasko. Ĉi tie utilas Kubernetes-funkciigistoj—ili permesas vin aŭtomatigi la kreadon de Kubernetes-resursoj surbaze de ŝanĝoj al la rimedoj. En nia kazo ni volas krei RoleBinding dum kreado Namespace.

Antaŭ ĉio, ni difinu la funkcion mainkiu faras la bezonatan aranĝon por ruli la deklaron kaj poste vokas la deklaron:

(Notu. transl.: ĉi tie kaj sube la komentoj en la kodo estas tradukitaj en la rusan. Krome, la indentaĵo estis korektita al spacoj anstataŭe de [rekomendite en Go] langetoj nur por pli bona legebleco ene de la Habr-aranĝo. Post ĉiu listo estas ligiloj al la originalo en GitHub, kie estas konservitaj anglalingvaj komentoj kaj langetoj.)

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

(ĉefa.goen kruda)

Ni faras la jenon:

  1. Ni agordas pritraktilon por specifaj operaciumaj signaloj por kaŭzi gracian finon de la funkciigisto.
  2. Ni uzas WaitGrouppor gracie haltigi ĉiujn gorutinojn antaŭ ol ĉesigi la aplikaĵon.
  3. Ni provizas aliron al la areto per kreado clientset.
  4. Lanĉo NamespaceController, en kiu troviĝos nia tuta logiko.

Nun ni bezonas bazon por logiko, kaj en nia kazo ĉi tiu estas la menciita 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
}

(regilo.iruen kruda)

Ĉi tie ni agordas SharedIndexInformer, kiu efike (uzante kaŝmemoron) atendos ŝanĝojn en nomspacoj (legu pli pri informantoj en la artikolo "Kiel efektive funkcias la planilo de Kubernetes?"- ĉ. traduko). Post ĉi tio ni konektiĝas EventHandler al la informanto, tiel ke aldonante nomspacon (Namespace) funkcio estas nomita createRoleBinding.

La sekva paŝo estas difini ĉi tiun funkcion 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))
  }
}

(regilo.iruen kruda)

Ni ricevas la nomspacon kiel obj kaj konverti ĝin al objekto Namespace. Tiam ni difinas RoleBindingsurbaze de la YAML-dosiero menciita komence, uzante la provizitan objekton Namespace kaj kreante RoleBinding. Fine ni registras ĉu la kreado sukcesis.

La lasta funkcio por esti difinita estas 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
}

(regilo.iruen kruda)

Jen ni parolas WaitGroupke ni lanĉu la gorutinon kaj poste voku namespaceInformer, kiu estis antaŭe difinita. Kiam la haltsignalo alvenos, ĝi finos la funkcion, informu WaitGroup, kiu ne plu estas ekzekutita, kaj ĉi tiu funkcio eliros.

Informoj pri konstruado kaj funkciado de ĉi tiu deklaro sur Kubernetes-areto troveblas en deponejoj sur GitHub.

Tio estas por la operatoro kiu kreas RoleBinding kiam Namespace en la Kubernetes-areo, preta.

fonto: www.habr.com

Aldoni komenton