שרייבן אַן אָפּעראַטאָר פֿאַר Kubernetes אין Golang

נאטיץ. טראַנסל.: אָפּערייטערז זענען אַגזיליערי ווייכווארג פֿאַר Kubernetes, דיזיינד צו אָטאַמייט די דורכפירונג פון רוטין אַקשאַנז אויף קנויל אַבדזשעקץ ווען זיכער געשעענישן פּאַסירן. מיר האָבן שוין געשריבן וועגן אָפּערייטערז אין דעם אַרטיקל, ו װ ז ײ האב ן גערעד ט װעג ן ד י פונדאמענטאל ע אידע ן או ן פרינציפ ן פו ן זײע ר ארבעט . אָבער אויב דער מאַטעריאַל איז געווען מער אַ מיינונג פון די זייַט פון אָפּערייטינג פאַרטיק קאַמפּאָונאַנץ פֿאַר Kubernetes, די איבערזעצונג פון די נייַע אַרטיקל איצט פארגעלייגט איז שוין די זעאונג פון אַ דעוועלאָפּער / DevOps ינזשעניר פּאַזאַלד דורך די ימפּלאַמענטיישאַן פון אַ נייַע אָפּעראַטאָר.

שרייבן אַן אָפּעראַטאָר פֿאַר Kubernetes אין Golang

איך באַשלאָסן צו שרייַבן דעם פּאָסטן מיט אַ פאַקטיש-לעבן בייַשפּיל נאָך מיין פרווון צו געפֿינען דאַקיומענטיישאַן אויף קריייטינג אַן אָפּעראַטאָר פֿאַר Kubernetes, וואָס איז דורכגעקאָכט דורך לערנען דעם קאָד.

דער ביישפּיל וואָס וועט זיין דיסקרייבד איז דאָס: אין אונדזער קובערנעטעס קנויל, יעדער Namespace רעפּראַזענץ אַ מאַנשאַפֿט ס זאַמדקאַסטן סוויווע, און מיר געוואלט צו באַגרענעצן אַקסעס צו זיי אַזוי אַז טימז קענען בלויז שפּילן אין זייער אייגענע זאַמדבאָקסעס.

איר קענען דערגרייכן וואָס איר ווילט דורך אַסיינינג אַ באַניצער אַ גרופּע וואָס האט RoleBinding צו ספּעציפיש Namespace и ClusterRole מיט רעדאקציע רעכט. די YAML פאַרטרעטונג וועט קוקן ווי דאָס:

---
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אין רוי)

שאַפֿן איין RoleBinding איר קענען טאָן דאָס מאַניואַלי, אָבער נאָך אַריבער די הונדערט נאָמען ספּייסאַז, עס ווערט אַ טידיאַס אַרבעט. דאָס איז ווו Kubernetes אָפּערייטערז קומען אין האַנטיק - זיי לאָזן איר צו אָטאַמייט די שאַפונג פון Kubernetes רעסורסן באזירט אויף ענדערונגען צו רעסורסן. אין אונדזער פאַל מיר ווילן צו שאַפֿן RoleBinding בשעת שאפן Namespace.

ערשטער פון אַלע, לאָזן אונדז דעפינירן די פֿונקציע mainוואָס טוט די פארלאנגט סעטאַפּ צו לויפן די ויסזאָגונג און דאַן רופט די ויסזאָגונג קאַמף:

(נאטיץ. טראַנסל.: דאָ און אונטן די באַמערקונגען אין די קאָד זענען איבערגעזעצט אין רוסיש. אין אַדישאַן, די ינדענטיישאַן איז קערעקטאַד צו ספּייסאַז אַנשטאָט פון [רעקאַמענדיד אין גיין] טאַבס בלויז פֿאַר די ציל פון בעסער לייענען אין די Habr אויסלייג. נאָך יעדער ליסטינג עס זענען לינקס צו דער אָריגינעל אויף GitHub, ווו ענגליש-שפּראַך באַמערקונגען און טאַבס זענען סטאָרד.)

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אין רוי)

מיר טאָן די פאלגענדע:

  1. מיר קאַנפיגיער אַ האַנדלער פֿאַר ספּעציפיש אַפּערייטינג סיסטעם סיגנאַלז צו פאַרשאַפן גראַציעז טערמאַניישאַן פון דער אָפּעראַטאָר.
  2. מיר נוצן WaitGroupצו גראַציעז האַלטן אַלע גאָראָוטינז איידער טערמאַנייטינג די אַפּלאַקיישאַן.
  3. מיר צושטעלן אַקסעס צו דעם קנויל דורך קריייטינג clientset.
  4. קאַטער NamespaceController, אין וואָס אַלע אונדזער לאָגיק וועט זיין ליגן.

איצט מיר דאַרפֿן אַ יסוד פֿאַר לאָגיק, און אין אונדזער פאַל דאָס איז דער דערמאנטער 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אין רוי)

דאָ מיר קאַנפיגיער SharedIndexInformer, וואָס וועט יפעקטיוולי (ניצן אַ קאַש) וואַרטן פֿאַר ענדערונגען אין נאָמען ספּייסאַז (לייענען מער וועגן ינפאָרמערז אין דעם אַרטיקל "ווי אַזוי אַרבעט די Kubernetes סקעדזשולער?"- אַפּפּראָקס. איבערזעצונג). נאָך דעם מיר פאַרבינדן EventHandler צו די ינפאָרמער, אַזוי אַז ווען אַדינג אַ נאָמען פּלאַץ (Namespace) פֿונקציע איז גערופן createRoleBinding.

דער ווייַטער שריט איז צו דעפינירן דעם פֿונקציע 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.goאין רוי)

מיר באַקומען די נאָמען ספּייס ווי obj און גער עס צו אַ כייפעץ Namespace. דעמאָלט מיר דעפינירן RoleBinding, באזירט אויף די YAML טעקע דערמאנט אין די אָנהייב, ניצן די צוגעשטעלט כייפעץ Namespace און שאפן RoleBinding. צום סוף, מיר קלאָץ צי די שאַפונג איז געראָטן.

די לעצטע פֿונקציע צו זיין דיפיינד איז 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אין רוי)

דאָ מיר זענען גערעדט WaitGroupאַז מיר קאַטער די גאָראָוטינע און דעמאָלט רופן namespaceInformer, וואס איז פריער דעפינירט געווארן. ווען דער האַלטן סיגנאַל ערייווז, עס וועט סוף די פֿונקציע, מיטטיילן WaitGroup, וואָס איז ניט מער עקסאַקיוטאַד, און די פֿונקציע וועט אַרויסגאַנג.

אינפֿאָרמאַציע וועגן בויען און לויפן דעם ויסזאָגונג אויף אַ Kubernetes קנויל קענען זיין געפֿונען אין ריפּאַזאַטאָריז אויף GitHub.

אַז ס עס פֿאַר דער אָפּעראַטאָר וואָס קריייץ RoleBinding ווען Namespace אין די קובערנעטעס קנויל, גרייט.

מקור: www.habr.com

לייגן אַ באַמערקונג