Golang میں Kubernetes کے لیے ایک آپریٹر لکھنا

نوٹ. ترجمہ: آپریٹرز Kubernetes کے لیے معاون سافٹ ویئر ہیں، جو مخصوص واقعات کے پیش آنے پر کلسٹر آبجیکٹ پر معمول کی کارروائیوں کو خود کار طریقے سے انجام دینے کے لیے ڈیزائن کیا گیا ہے۔ ہم آپریٹرز کے بارے میں پہلے ہی لکھ چکے ہیں۔ یہ مضمونجہاں انہوں نے اپنے کام کے بنیادی نظریات اور اصولوں کے بارے میں بات کی۔ لیکن اگر یہ مواد Kubernetes کے لیے ریڈی میڈ اجزاء کو چلانے کی طرف سے زیادہ نظریہ تھا، تو اب تجویز کردہ نئے مضمون کا ترجمہ پہلے سے ہی ایک ڈویلپر/DevOps انجینئر کا وژن ہے جو ایک نئے آپریٹر کے نفاذ سے پریشان ہے۔

Golang میں 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میں خام)

ایک بناؤ RoleBinding آپ اسے دستی طور پر کرسکتے ہیں، لیکن سو نام کی جگہ کے نشان کو عبور کرنے کے بعد، یہ ایک مشکل کام بن جاتا ہے۔ یہ وہ جگہ ہے جہاں Kubernetes آپریٹرز کام آتے ہیں — وہ آپ کو وسائل میں تبدیلیوں کی بنیاد پر Kubernetes وسائل کی تخلیق کو خودکار بنانے کی اجازت دیتے ہیں۔ ہمارے معاملے میں ہم بنانا چاہتے ہیں۔ RoleBinding تخلیق کرتے وقت Namespace.

سب سے پہلے، آئیے فنکشن کی وضاحت کرتے ہیں۔ mainجو اسٹیٹمنٹ کو چلانے کے لیے مطلوبہ سیٹ اپ کرتا ہے اور پھر اسٹیٹمنٹ ایکشن کو کال کرتا ہے:

(نوٹ. ترجمہ: یہاں اور نیچے کوڈ کے تبصروں کا روسی میں ترجمہ کیا گیا ہے۔ اس کے علاوہ، صرف حبر لے آؤٹ کے اندر بہتر پڑھنے کی اہلیت کے مقصد کے لیے [Go میں تجویز کردہ] ٹیبز کے بجائے انڈینٹیشن کو خالی جگہوں پر درست کیا گیا ہے۔ ہر فہرست کے بعد 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 کلسٹر میں، تیار ہے۔

ماخذ: www.habr.com

نیا تبصرہ شامل کریں