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 mga nakagawiang pagkilos sa mga cluster object kapag nangyari ang ilang partikular na kaganapan. Nagsulat na kami tungkol sa mga operator sa artikulong ito, na tinalakay ang mga pangunahing ideya at prinsipyo ng pagpapatakbo. Ngunit habang ang materyal na iyon ay higit pa sa isang pananaw mula sa pananaw ng pagpapatakbo ng mga handa na bahagi ng Kubernetes, ang iminungkahing pagsasalin ng bagong artikulong ito ay ang pananaw ng isang developer/DevOps engineer, na nalilito sa pagpapatupad ng bagong operator.

Pagsusulat ng operator para sa Kubernetes sa Golang

Nagpasya akong isulat ang post na ito gamit ang isang real-world na halimbawa pagkatapos ng aking mga pagtatangka na maghanap ng dokumentasyon sa paglikha ng isang operator para sa Kubernetes, na humantong sa aking 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 paghigpitan ang pag-access sa kanila upang ang mga koponan ay makapaglaro lamang sa sarili nilang mga sandbox.

Ang ninanais na resulta ay maaaring makamit sa pamamagitan ng pagtatalaga ng user sa isang 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)

Upang lumikha ng ganoon RoleBinding Magagawa mo ito nang manu-mano, ngunit pagkatapos mong tumawid sa marka ng 100 namespace, ito ay nagiging nakakapagod. Dito nagagamit ang mga operator ng Kubernetesβ€”pinapayagan ka nilang i-automate ang paggawa ng mga mapagkukunan ng Kubernetes batay sa mga pagbabago sa mapagkukunan. Sa aming kaso, gusto naming lumikha RoleBinding kapag lumilikha Namespace.

Una sa lahat, tukuyin natin ang function main, na nagsasagawa ng kinakailangang pag-setup upang patakbuhin ang operator at pagkatapos ay hinihimok ang pagkilos ng operator:

(Tandaan. transl.: Mula dito, ang mga komento sa code ay isinalin na sa Russian. Bilang karagdagan, ang mga indentasyon ay ginawang mga puwang sa halip na mga tab [inirerekomenda sa Go] para lamang sa layunin ng pagpapabuti ng pagiging madaling mabasa sa loob ng layout ng Habr. Ang pagsunod sa bawat listahan ay mga link sa orihinal na GitHub code, kung saan pinapanatili ang mga komento at tab sa 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 matapos 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 kami nag-set up SharedIndexInformer, na mahusay na 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 ay kumonekta kami EventHandler sa tagapagbigay-alam, upang kapag nagdadagdag ng namespace (Namespace) ang 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 ibahin ito sa isang bagay Namespace. Pagkatapos ay tinutukoy namin RoleBinding, batay sa YAML file na binanggit sa simula, gamit ang ibinigay na bagay Namespace at paglikha RoleBindingSa wakas, nag-log kami kung matagumpay ang paglikha.

Ang huling function na kailangang tukuyin 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 nagsasalita WaitGroup, na simulan namin ang goroutine at pagkatapos ay tumawag namespaceInformer, na dating tinukoy. Kapag ang isang stop signal ay natanggap, ito ay wakasan ang function, ulat WaitGroup, na hindi na tumatakbo, at ang function na ito ay magwawakas.

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

Sa operator na ito, na lumilikha RoleBinding sa hitsura Namespace sa Kubernetes cluster, handa na.

Pinagmulan: www.habr.com

Bumili ng maaasahang pagho-host para sa mga site na may proteksyon ng DDoS, mga server ng VPS VDS πŸ”₯ Bumili ng maaasahang website hosting na may proteksyon ng DDoS, VPS VDS servers | ProHoster