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

Эскертүү. котормо.: Операторлор Kubernetes үчүн жардамчы программалык камсыздоо болуп саналат, алар белгилүү бир окуялар болгондо кластердик объектилерде күнүмдүк аракеттердин аткарылышын автоматташтыруу үчүн иштелип чыккан. Биз буга чейин операторлор жөнүндө жазганбыз бул макалада, анда алар ездерунун ишинин фундаменталдуу идеялары жана принциптери женунде айтышты. Бирок, эгерде бул материал Kubernetes үчүн даяр компоненттерди иштетүү жагынан көбүрөөк көрүнүш болсо, анда азыр сунушталган жаңы макаланын котормосу жаңы оператордун ишке ашырылышы менен таң калган иштеп чыгуучунун/DevOps инженеринин көрүнүшү.

Голангдагы 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боюнча чийки)

Бир түзүү RoleBinding Сиз муну кол менен кыла аласыз, бирок жүз мейкиндиктин белгисин кесип өткөндөн кийин, бул тажатма иш болуп калат. Бул жерде Kubernetes операторлору жардамга келет — алар ресурстарды өзгөртүүнүн негизинде Kubernetes ресурстарын түзүүнү автоматташтырууга мүмкүндүк берет. Биздин учурда биз түзүүнү каалайбыз RoleBinding түзүү учурунда Namespace.

Биринчиден, функцияны аныктайлы mainал билдирүүнү иштетүү үчүн талап кылынган орнотууларды жасайт жана андан кийин билдирүү аракетин чакырат:

(Эскертүү. котормо.: бул жерде жана ылдыйда коддогу комментарийлер орус тилине которулган. Кошумчалай кетсек, чегинүү [Өтүүдө сунушталган] өтмөктөрдүн ордуна боштуктарга оңдолду. Ар бир листингден кийин англис тилиндеги комментарийлер жана өтмөктөр сакталган 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 Kubernetes кластеринде, даяр.

Source: www.habr.com

Комментарий кошуу