Голангта Kubernetes операторын жазу

Ескерту. аударма: Операторлар - белгілі бір оқиғалар орын алған кезде кластер нысандарындағы әдеттегі әрекеттерді орындауды автоматтандыруға арналған Kubernetes үшін көмекші бағдарламалық құрал. Біз операторлар туралы бұрын жазғанбыз Бұл мақала, онда олар өз жұмыстарының іргелі идеялары мен ұстанымдары туралы әңгімеледі. Бірақ егер бұл материал Kubernetes үшін дайын құрамдастарды пайдалану жағынан көбірек көзқарас болса, онда жаңа мақаланың енді ұсынылып отырған аудармасы қазірдің өзінде жаңа операторды іске асырудан таң қалған әзірлеуші ​​​​/DevOps инженерінің көзқарасы болып табылады.

Голангта Kubernetes операторын жазу

Мен бұл жазбаны кодты зерттеген Kubernetes операторын құру бойынша құжаттаманы табуға тырысқаннан кейін нақты өмірлік мысалмен жазуды шештім.

Сипатталған мысал мынада: біздің 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, in шикі)

Біреуін жасаңыз 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, in шикі)

Біз келесі әрекеттерді орындаймыз:

  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, in шикі)

Мұнда біз конфигурациялаймыз 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, in шикі)

Біз аттар кеңістігін аламыз 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, in шикі)

Міне, біз сөйлесеміз WaitGroupбіз горутинді іске қосамыз, содан кейін қоңырау шаламыз namespaceInformer, ол бұрын анықталған. Тоқтау сигналы келгенде, ол функцияны аяқтайды, хабарлаңыз WaitGroup, ол енді орындалмайды және бұл функция шығады.

Бұл мәлімдемені Kubernetes кластерінде құру және іске қосу туралы ақпаратты мына жерден табуға болады GitHub репозиторийлері.

Бұл жасаушы оператор үшін RoleBinding қашан Namespace Kubernetes кластерінде дайын.

Ақпарат көзі: www.habr.com

пікір қалдыру