Pagsusulat ng operator para sa Kubernetes sa Golang

Tandaan. transl.: Ang mga operator ay pantulong na software para sa Kubernetes, na idinisenyo upang i-automate ang pagsasagawa ng mga nakagawiang pagkilos sa mga cluster object kapag nangyari ang ilang partikular na kaganapan. Nagsulat na kami tungkol sa mga operator sa artikulong ito, kung saan pinag-usapan nila ang mga pangunahing ideya at prinsipyo ng kanilang gawain. Ngunit kung ang materyal na iyon ay higit na pananaw mula sa panig ng pagpapatakbo ng mga handa na bahagi para sa Kubernetes, kung gayon ang pagsasalin ng bagong artikulong iminungkahi ngayon ay ang pananaw na ng isang developer/DevOps engineer na nalilito sa pagpapatupad ng isang bagong operator.

Pagsusulat ng operator para sa Kubernetes sa Golang

Napagpasyahan kong isulat ang post na ito gamit ang isang totoong buhay na halimbawa pagkatapos ng aking mga pagtatangka na maghanap ng dokumentasyon sa paglikha ng isang operator para sa Kubernetes, na dumaan sa pag-aaral ng code.

Ang halimbawang ilalarawan ay ito: sa aming Kubernetes cluster, bawat isa Namespace kumakatawan sa kapaligiran ng sandbox ng isang koponan, at gusto naming limitahan ang pag-access sa kanila upang ang mga koponan ay makakapaglaro lamang sa sarili nilang mga sandbox.

Maaabot mo ang gusto mo sa pamamagitan ng pagtatalaga sa isang user ng pangkat na mayroon RoleBinding sa tiyak Namespace ΠΈ ClusterRole na may mga karapatan sa pag-edit. Ang representasyon ng YAML ay magiging ganito:

---
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.yamlsa hilaw)

Lumikha ng isa RoleBinding Maaari mong gawin ito nang manu-mano, ngunit pagkatapos tumawid sa daang marka ng mga namespace, ito ay nagiging isang nakakapagod na gawain. Dito nagagamit ang mga operator ng Kubernetesβ€”pinapayagan ka nilang i-automate ang paggawa ng mga mapagkukunan ng Kubernetes batay sa mga pagbabago sa mga mapagkukunan. Sa aming kaso gusto naming lumikha RoleBinding habang lumilikha Namespace.

Una sa lahat, tukuyin natin ang function mainna ginagawa ang kinakailangang pag-setup upang patakbuhin ang pahayag at pagkatapos ay tatawagin ang pagkilos ng pahayag:

(Tandaan. transl.: dito at sa ibaba ang mga komento sa code ay isinalin sa Russian. Bilang karagdagan, ang indentation ay naitama sa mga puwang sa halip na [inirerekomenda sa Go] na mga tab para lamang sa layunin ng mas mahusay na pagiging madaling mabasa sa loob ng layout ng Habr. Pagkatapos ng bawat listahan ay may mga link sa orihinal sa GitHub, kung saan naka-imbak ang mga komento at tab sa wikang English.)

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.gosa hilaw)

Ginagawa namin ang sumusunod:

  1. Nag-configure kami ng handler para sa mga partikular na signal ng operating system upang maging sanhi ng magandang pagwawakas ng operator.
  2. Gumamit WaitGroupupang maayos na ihinto ang lahat ng goroutine bago wakasan ang aplikasyon.
  3. Nagbibigay kami ng access sa cluster sa pamamagitan ng paggawa clientset.
  4. Ilunsad NamespaceController, kung saan matatagpuan ang lahat ng ating lohika.

Ngayon kailangan namin ng isang batayan para sa lohika, at sa aming kaso ito ang nabanggit 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.gosa hilaw)

Dito namin i-configure SharedIndexInformer, na epektibong maghihintay (gamit ang cache) para sa mga pagbabago sa mga namespace (magbasa nang higit pa tungkol sa mga impormante sa artikulong "Paano talaga gumagana ang scheduler ng Kubernetes?"- tinatayang pagsasalin). Pagkatapos nito kumonekta kami EventHandler sa tagapagbigay-alam, upang kapag nagdadagdag ng namespace (Namespace) function ay tinatawag createRoleBinding.

Ang susunod na hakbang ay upang tukuyin ang function na ito 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))
  }
}

(controller.gosa hilaw)

Nakukuha namin ang namespace bilang obj at i-convert ito sa isang bagay Namespace. Pagkatapos ay tukuyin namin RoleBinding, batay sa YAML file na binanggit sa simula, gamit ang ibinigay na bagay Namespace at paglikha RoleBinding. Sa wakas, nag-log kami kung matagumpay ang paglikha.

Ang huling function na tutukuyin ay 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.gosa hilaw)

Dito tayo nag-uusap WaitGroupna ilulunsad namin ang goroutine at pagkatapos ay tumawag namespaceInformer, na dati nang tinukoy. Kapag dumating ang stop signal, tatapusin nito ang function, ipaalam WaitGroup, na hindi na naisakatuparan, at lalabas ang function na ito.

Ang impormasyon tungkol sa pagbuo at pagpapatakbo ng pahayag na ito sa isang Kubernetes cluster ay matatagpuan sa mga repositoryo sa GitHub.

Iyon lang para sa operator na lumilikha RoleBinding kailan Namespace sa Kubernetes cluster, handa na.

Pinagmulan: www.habr.com

Magdagdag ng komento