Голанг дахь Kubernetes-д оператор бичиж байна

Анхаарна уу. орчуулга.: Операторууд нь Kubernetes-д зориулсан туслах программ хангамж бөгөөд тодорхой үйл явдал тохиолдох үед кластерын объектууд дээрх ердийн үйлдлүүдийг автоматжуулахад зориулагдсан. Бид аль хэдийн операторуудын талаар бичсэн энэ нийтлэл, тэд өөрсдийн ажлын үндсэн санаа, зарчмуудын талаар ярилцав. Гэхдээ энэ материал нь 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

(үүрэг гүйцэтгэх.yaml, in түүхий эд)

Нэгийг үүсгэ RoleBinding Та үүнийг гараар хийж болно, гэхдээ зуун нэрийн зайг давсны дараа энэ нь уйтгартай ажил болно. Энд Kubernetes-ийн операторууд хэрэг болдог—тэдгээр нь нөөцийн өөрчлөлтөд үндэслэн Kubernetes нөөцийг автоматжуулах боломжийг олгодог. Манай тохиолдолд бид бүтээхийг хүсч байна RoleBinding бүтээх явцад Namespace.

Юуны өмнө функцийг тодорхойлъё mainЭнэ нь мэдэгдлийг ажиллуулахын тулд шаардлагатай тохиргоог хийж, дараа нь мэдэгдлийн үйлдлийг дууддаг:

(Анхаарна уу. орчуулга.: энд болон доор кодын тайлбарыг орос хэл рүү орчуулсан болно. Нэмж дурдахад, [Go-д санал болгосон] табын оронд зөвхөн 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Аппликешныг дуусгахаас өмнө бүх goroutines-ийг эелдэг байдлаар зогсоох.
  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гэж бид goroutine ажиллуулж, дараа нь залгана namespaceInformer, өмнө нь тодорхойлсон. Зогсоох дохио ирэхэд функцийг дуусгах болно, мэдэгдээрэй WaitGroup, энэ нь цаашид ажиллахаа больсон бөгөөд энэ функц гарах болно.

Kubernetes кластер дээр энэ мэдэгдлийг бүтээх, ажиллуулах талаархи мэдээллийг эндээс олж болно GitHub дээрх агуулахууд.

Энэ нь үүсгэгч операторын хувьд юм RoleBinding хэзээ Namespace Kubernetes кластерт бэлэн байна.

Эх сурвалж: www.habr.com

сэтгэгдэл нэмэх