Að skrifa rekstraraðila fyrir Kubernetes í Golang

Athugið. þýð.: Rekstraraðilar eru hjálparhugbúnaður fyrir Kubernetes, hannaður til að gera sjálfvirkan framkvæmd venjubundinna aðgerða á klasahlutum þegar ákveðnir atburðir eiga sér stað. Við höfum þegar skrifað um rekstraraðila í Þessi grein, þar sem þeir ræddu um grundvallarhugmyndir og meginreglur í starfi sínu. En ef þetta efni var meira sjónarhorn frá hlið rekstri tilbúinna íhluta fyrir Kubernetes, þá er þýðing nýju greinarinnar sem nú er lögð til þegar sýn þróunaraðila/DevOps verkfræðings sem er undrandi á innleiðingu nýs rekstraraðila.

Að skrifa rekstraraðila fyrir Kubernetes í Golang

Ég ákvað að skrifa þessa færslu með raunverulegu dæmi eftir tilraunir mínar til að finna skjöl um að búa til rekstraraðila fyrir Kubernetes, sem fór í gegnum nám í kóðanum.

Dæmið sem verður lýst er þetta: í Kubernetes þyrpingunni okkar, hver Namespace táknar sandkassaumhverfi liðs og við vildum takmarka aðgang að þeim þannig að lið gætu aðeins spilað í sínum eigin sandkassa.

Þú getur náð því sem þú vilt með því að úthluta notanda hópi sem hefur RoleBinding til sérstakrar Namespace и ClusterRole með ritstjórnarrétti. YAML framsetningin mun líta svona út:

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

(hlutverkabinding.yamlÍ hrár)

Búðu til einn RoleBinding Þú getur gert það handvirkt, en eftir að hafa farið yfir hundrað nafnamörk verður þetta leiðinlegt verkefni. Þetta er þar sem Kubernetes rekstraraðilar koma sér vel - þeir leyfa þér að gera sjálfvirkan stofnun Kubernetes auðlinda byggt á breytingum á auðlindum. Í okkar tilviki viljum við skapa RoleBinding meðan á sköpun stendur Namespace.

Fyrst af öllu skulum við skilgreina aðgerðina mainsem gerir nauðsynlega uppsetningu til að keyra yfirlýsinguna og kallar síðan yfirlýsinguaðgerðina:

(Athugið. þýð.: hér og fyrir neðan eru athugasemdirnar í kóðanum þýddar á rússnesku. Að auki hefur inndrátturinn verið leiðréttur í bil í stað [ráðlagt í Go] flipa eingöngu í þeim tilgangi að bæta læsileika innan Habr skipulagsins. Eftir hverja skráningu eru tenglar á frumritið á GitHub, þar sem athugasemdir og flipar á ensku eru geymdar.)

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

(main.goÍ hrár)

Við gerum eftirfarandi:

  1. Við stillum meðhöndlun fyrir ákveðin stýrikerfismerki til að valda þokkafullri uppsögn á símafyrirtækinu.
  2. Við notum WaitGroupað stöðva allar goroutínar af þokkabót áður en umsókninni er hætt.
  3. Við veitum aðgang að klasanum með því að búa til clientset.
  4. Sjósetja NamespaceController, þar sem öll rökfræði okkar verður staðsett.

Nú þurfum við grundvöll fyrir rökfræði og í okkar tilviki er þetta sá sem nefndur er 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
}

(stjórnandi.faraÍ hrár)

Hér stillum við SharedIndexInformer, sem mun í raun (með því að nota skyndiminni) bíða eftir breytingum á nafnasvæðum (lesið meira um uppljóstrara í greininni “Hvernig virkar Kubernetes tímaáætlun í raun og veru?"- ca. þýðing). Eftir þetta tengjumst við EventHandler til uppljóstrara, þannig að þegar nafnrými er bætt við (Namespace) fall er kallað createRoleBinding.

Næsta skref er að skilgreina þessa aðgerð 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))
  }
}

(stjórnandi.faraÍ hrár)

Við fáum nafnrýmið sem obj og breyta því í hlut Namespace. Þá skilgreinum við RoleBinding, byggt á YAML skránni sem nefnd var í upphafi, með því að nota tilgreindan hlut Namespace og skapa RoleBinding. Að lokum skráum við hvort stofnunin hafi tekist.

Síðasta aðgerðin sem á að skilgreina er 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
}

(stjórnandi.faraÍ hrár)

Hér erum við að tala WaitGroupað við ræsum goroutine og köllum svo namespaceInformer, sem áður hefur verið skilgreint. Þegar stöðvunarmerkið kemur mun það enda aðgerðina, upplýstu WaitGroup, sem er ekki lengur keyrt, og þessi aðgerð mun hætta.

Upplýsingar um að byggja og keyra þessa yfirlýsingu á Kubernetes klasa er að finna í geymslur á GitHub.

Það er það fyrir rekstraraðilann sem skapar RoleBinding hvenær Namespace í Kubernetes klasanum, tilbúinn.

Heimild: www.habr.com

Bæta við athugasemd