Kubernetes operátor írása Golang nyelven

Megjegyzés fordítás.: Az operátorok (operators) segédprogramok a Kubernetes számára, amelynek célja a rutinfeladatok automatizálása a klaszter objektumainak kezelése során bizonyos események hatására. Már írtunk az operátorokról a cikkben, ahol a működésük alapelveiről és koncepcióiról beszéltünk. De míg az előző anyag inkább a kész komponensek üzemeltetésére fókuszált, a mostani cikk fordítása már egy fejlesztő/DevOps mérnök nézőpontja, akit az új operátor megvalósítása foglalkoztat.

Kubernetes operátor írása Golang nyelven

Ezt a bejegyzést egy valós példával azért írtam, mert próbáltam dokumentációt találni a Kubernetes operátor létrehozásáról, ami a kód tanulmányozásával indult.

A példa, amit itt bemutatok, a következő: a Kubernetes klaszterünkben minden Namespace egy-egy csapat homokozó környezetét jelenti, és szerettük volna korlátozni a hozzáférést úgy, hogy a csapatok csak a saját homokozóikban tudjanak játszani.

A kívánt cél elérhető, ha a felhasználónak egy csoportot rendelünk, amelynek RoleBinding jogosultságai vannak Namespace és ClusterRole szerkesztési joggal. A YAML-ábrázolás így fog kinézni:

---
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.yaml, in raw)

Ilyen RoleBinding manuálisan is létrehozható, de miután átlépjük a száz namespace határt, ez unalmas tevékenységgé válik. Itt jönnek a Kubernetes operátorok segítségére, mivel lehetővé teszik a Kubernetes erőforrások automatikus létrehozását az erőforrások változásai alapján. Az esetünkben azt szeretnénk, hogy RoleBinding a létrehozás során Namespace.

Először határozzuk meg a mainfunkciót, amely végrehajtja a szükséges beállításokat az operátor indításához, majd meghívja az operátor műveletet:

(Megjegyzés fordítás.: itt és a továbbiakban a kódban található megjegyzések oroszra lettek fordítva. Ezen kívül a behúzások a [Go-ban ajánlott] tabok helyett szóközökre lettek cserélve kizárólag a Habra struktúráján belüli jobb olvashatóság érdekében. Minden listázás után hivatkozásokat adunk meg az eredetire a GitHubon, ahol megmaradtak az angol nyelvű megjegyzések és tabok.

func main() {
  // Beállítjuk a naplózás kimenetét a konzol STDOUT-ra
  log.SetOutput(os.Stdout)

  sigs := make(chan os.Signal, 1) // Létrehozunk egy csatornát az OS jelek fogadására
  stop := make(chan struct{})     // Létrehozunk egy csatornát a leállító jel fogadására

  // Regisztráljuk a SIGTERM jel fogadását a sigs csatornára
  signal.Notify(sigs, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) 

  // A Goroutine-ok önállóan hozzáadhatják magukat a WaitGroup-hoz,
  // hogy várakozzunk a végrehajtásuk befejezésére
  wg := &sync.WaitGroup{} 

  runOutsideCluster := flag.Bool("run-outside-cluster", false, "Ezt a zászlót állítsuk be, ha a klaszteren kívül futunk.")
  flag.Parse()
  // Létrehozzuk a clientset-et a Kubernetes klaszterrel való interakcióhoz
  clientset, err := newClientSet(*runOutsideCluster)

  if err != nil {
    panic(err.Error())
  }

  controller.NewNamespaceController(clientset).Run(stop, wg)

  <-sigs // Várjuk a jeleket (a jel megérkezéséig semmi más nem történik)
  log.Printf("Leállás...")

  close(stop) // Megmondjuk a goroutine-oknak, hogy álljanak meg
  wg.Wait()   // Várunk, hogy minden leálljon
}

(main.go, in raw)

A következőt tesszük:

  1. Beállítjuk a konkrét operációs rendszer jeleinek feldolgozóját, hogy megfelelő (graceful) leállítást hívjunk meg az operátor számára.
  2. Használjuk WaitGroup, hogy megfelelően leállítsuk az összes goroutine-t az alkalmazás leállítása előtt.
  3. Hozzáférést biztosítunk a klaszterhez az clientset.
  4. Indítás NamespaceController, ahol az összes logikánk elhelyezkedik.

Most szükség van a logika alapjára, és esetünkben ez a fent említett 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.go, in raw)

Itt beállítjuk a SharedIndexInformer, amely hatékonyan (cache használatával) várja a névtérbeli változásokat (további információ a informerekről olvasható a "Hogyan működik valójában a Kubernetes ütemező?» — megjegyzés). Ezután csatlakoztatjuk az EventHandler az informálóhoz, így névtér hozzáadásakor (Namespace) meghívódik a createRoleBinding.

Következő lépés — definiálni ezt a funkciót func (c *NamespaceController) createRoleBinding(obj interface{}) { namespaceObj := obj.(*v1.Namespace) namespaceName := namespaceObj.NameroleBinding := &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("A Role Binding létrehozása sikertelen: %s", err.Error())) } else { log.Println(fmt.Sprintf("AD RoleBinding létrehozva a névtérhez: %s", roleBinding.Name)) } } createRoleBinding:

A névteret úgy kapjuk meg, mint

(controller.go, in raw)

Névteret kapunk, mint obj és átalakítjuk objektummá Namespace. Ezután meghatározzuk RoleBinding, az elején említett YAML-fájl alapján, a megadott objektum használatával Namespace és létrehozva RoleBinding. Végül naplózzuk, hogy sikeres volt-e a létrehozás.

Az utolsó funkció, amelyet meg kell határozni, a 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.go, in raw)

Itt arról beszélünk WaitGroup, hogy elindítjuk a goroutine-t, majd meghívjuk namespaceInformer, amelyet előre definiáltunk. Amikor megérkezik a leállító jel, befejezi a funkciót, és jelzi WaitGroup, hogy már nem fut, és ez a funkció befejezi a működését.

Az operátor építéséről és futtatásáról a Kubernetes klaszterben az alábbiakban található tároló a GitHubon.

Ezzel az operátor, amely létrehozza RoleBinding a Kubernetes klaszterben való megjelenéskor, készen áll. Namespace Megjegyzés: Az operátorok olyan segédprogramok a Kuberneteshez, amelyek célja, hogy automatizálják a klaszter objektumain végzendő rutinfeladatok végrehajtását bizonyos események hatására.

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster