سیستم عامل در k8s با Golang – هر CRD را تماشا کنید

عکس پروفایل نویسنده

@ryandawsonukرایان داوسون

مهندس در seldon.io. همکار سال هکرنون – مهندسی.

بیایید بگوییم شما می خواهید با Kubernetes بیشتر از اجرای برنامه های موجود در فروشگاه استفاده کنید. شاید شما بخواهید برنامه ها را با هم به یک بستر سفارش دهید. تصور کنید که وقتی کاربر شما دکمه ای را کلیک می کند می خواهید یک پایگاه داده جدید تهیه کنید یا یک نقطه انتهایی جدید با چهره عمومی باز کنید.

اگر فقط از ابزارها و برنامه های خارج از قفسه استفاده نمی کنید ، احتمالاً k8s yaml manifests و نمودارهای هدایت را پوشش داده اید. تعامل با Kubernetes خود حوزه منابع و اپراتورهای سفارشی است. بیایید این را قبل از ورود به یک مورد خاص از نوع پلت فرم منبع سفارشی درک کنیم.

منابع و اپراتورهای سفارشی

بیایید بگوییم آنچه شما ارائه می دهید بیشتر یک ابزار است تا یک برنامه. شاید شما یک بخش داشته باشید که یک نمونه از ابزار را اجرا می کند و می خواهید بخشهای بیشتری نمونه های خود را به طور خودکار تهیه کنند. پایگاه داده ها ، ابزارهای نظارت و ابزارهای ذخیره سازی ممکن است مواردی از این دست باشد. یک الگوی معمول این است که API Kubernetes را با ایجاد تعریف Custom Resource (تعریف منبع شخصی) خود گسترش دهید.

MongoDB مثال زیر را برای یک منبع سفارشی برای ایجاد یک نمونه MongoDB ارائه می دهد:

apiVersion: mongodb.com/v1
kind: MongoDB
metadata:
  name: my-standalone
spec:
  version: 4.4.0-ent
  service: my-service

  opsManager:
    configMapRef:
      name: my-project
  credentials: my-credentials
  type: Standalone

  persistent: true

این را می توان با ایجاد کرد

kubectl apply

. اپراتور MongoDB منبع سفارشی را مدیریت می کند و اطمینان حاصل می کند که نوع مناسبی از MongoDB در پاسخ ایجاد می شود. بسیاری از این اپراتورهای دیگر در operatorhub ذکر شده است.

اگر می خواهید یک اپراتور ایجاد کنید ، بهترین پشتیبانی برای این کار با Golang است. بهترین گزینه ها kubebuilder یا operator-sdk هستند (که در حال ساخت مجدد هستند و براساس kubebuilder ساخته می شوند). من قصد ندارم در مورد ایجاد یک اپراتور صحبت کنم ، زیرا مواد خوبی وجود دارد – kubebuilder یک کتاب الکترونیکی رسمی در آن دارد.

آنچه یافتن اطلاعات در آن دشوارتر است پروژه هایی است که با چندین منبع سفارشی تعامل دارند. تصور کنید که شما می خواهید یک بستر برای ارائه نمونه هایی از ابزار نظارت داشته باشید و همچنین نقاط انتهایی جدید خارجی را برای آنها نشان دهید. یا هنگام ایجاد پایگاه های داده جدید تماشا کنید و به طور خودکار نظارت را در اطراف آنها اضافه کنید. سپس شما مجبور خواهید شد فراتر از ابزار داربستی کوببیلدر یا حتی آنچه کتاب کوب ساز به شما می گوید ، بروید.

تعامل با CRD های متعدد

برای اپراتورهای واقعی شما می خواهید مسئولیت مدیریت یک CRD را منزوی کنید. اما ممکن است لازم باشد با CRD های دیگری که اپراتور مدیریت نمی کند تعامل داشته باشید. به عنوان مثال ، شاید شما در حال ارائه راهی برای تهیه نمونه هایی از سیستم مدیریت محتوا هستید و سیستم مدیریت محتوا نیز از mongodb به عنوان یک وابستگی استفاده می کند. بنابراین ممکن است بخواهید اپراتور مدیریت محتوای شما موارد mongobdb را به kubernetes API ارسال کند.

کار با چندین منبع سفارشی در کد Golang می تواند جالب باشد.

یکی از راه های کار با منابع سفارشی در Go استفاده از ClientSet است. بسیاری از پروژه ها ClientSet را به عنوان راهی آسان برای وارد کردن کد برای کار با منابع سفارشی ارائه می دهند. به عنوان مثال ، پروژه Seldon منابع سفارشی را برای استقرار مدل های یادگیری ماشین در Kubernetes فراهم می کند و در حال حاضر یک ClientSet را نشان می دهد. سپس می توانم بسته را وارد کنم ، یک ClientSet را نمونه سازی کنم و از آن برای لیست کردن یا ایجاد SeldonDeployments استفاده کنم:

import (
	"context"

	machinelearningv1 "github.com/seldonio/seldon-core/operator/apis/machinelearning.seldon.io/v1"
	seldonclientset "github.com/seldonio/seldon-core/operator/client/machinelearning.seldon.io/v1/clientset/versioned"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	_ "k8s.io/client-go/plugin/pkg/client/auth"
	"k8s.io/client-go/tools/clientcmd"
)

var clientset *seldonclientset.Clientset

func init() {
	clientset, _ = GetSeldonClientSet()
}

func GetSeldonClientSet() (*seldonclientset.Clientset, error) {
	config, err := clientcmd.BuildConfigFromFlags("", "")
	if err != nil {
		return nil, err
	}
	kubeClientset, err := seldonclientset.NewForConfig(config)
	if err != nil {
		return nil, err
	}
	return kubeClientset, nil
}

func ListSeldonDeployments(namespace string) (result *machinelearningv1.SeldonDeploymentList, err error) {
	return clientset.MachinelearningV1().SeldonDeployments(namespace).List(context.TODO(), metav1.ListOptions{})
}

func CreateSeldonDeployment(deployment *machinelearningv1.SeldonDeployment, namespace string) (sdep *machinelearningv1.SeldonDeployment, err error) {
	return clientset.MachinelearningV1().SeldonDeployments(namespace).Create(context.TODO(), deployment, metav1.CreateOptions{})
}

خواندن و به روزرسانی بسیار شبیه به هم هستند. اما اگر بخواهید تغییرات موجود در منابع را ردیابی کنید ، چه می کنید؟ شاید شما بخواهید بلافاصله از آماده شدن منبع مطلع شوید. که خواستار تماشای ساعت است و اجرای آن کمی متفاوت است.

مشاهده SeldonDeployment با وارد کردن امکان پذیر است:

github.com/seldonio/seldon-core/operator/client/machinelearning.seldon.io/v1/informers/externalversions

و با استفاده از یک Informer. کمی ساده ، که در اصل به نظر می رسد:

import (
	SeldonVersion "github.com/seldonio/seldon-core/operator/apis/machinelearning.seldon.io/v1"
	SeldonInformers "github.com/seldonio/seldon-core/operator/client/machinelearning.seldon.io/v1/informers/externalversions"
)

seldonFactory := SeldonInformers.NewSharedInformerFactoryWithOptions(s.Settings.SeldonAPI.Clientset, 0, SeldonInformers.WithNamespace(namespace))
	runSeldonInformer(seldonFactory)

func runSeldonInformer(seldonFactory SeldonInformers.SharedInformerFactory) {
	seldoninformer := seldonFactory.Machinelearning().V1().SeldonDeployments().Informer()
	stopper := make(chan struct{})
	defer close(stopper)
	seldoninformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			d := obj.(*SeldonVersion.SeldonDeployment)
			//...
		},
		DeleteFunc: func(obj interface{}) {
			d := obj.(*SeldonVersion.SeldonDeployment)
			//...
		},
		UpdateFunc: func(oldObj, newObj interface{}) {
			d := newObj.(*SeldonVersion.SeldonDeployment)
			//...
		},
	})

	seldoninformer.Run(stopper)
}

در اینجا توجه داشته باشید که ما وارداتی به نام SeldonVersion و همچنین SeldonInformers داریم. SeldonVersion در اینجا کد Go را به ما می دهد تا نشان دهد منبع سفارشی SeldonDeployment چیست. این نوع Go است ، در حالی که ClientSet راهی برای تعامل با نمونه هایی از این نوع است.

همچنین یک مشتری جعلی مفید برای آزمایش وجود دارد:

(

github.com/seldonio/seldon-core/operator/client/machinelearning.seldon.io/v1/clientset/versioned/fake

)

همه اینها به طور موثر از kubebuilder ناشی می شود زیرا کد clientet های وارد شده توسط kubebuilder تولید شده است. با این حال ، kubebuilder دیگر مشتری ایجاد نمی کند.

تعامل با CRD – راه جدید

اکنون پیشنهاد kubebuilder استفاده از بسته کنترل کننده زمان اجرا Kubernetes برای تعامل با CRD است. بسته کنترل کننده-زمان اجرا روش های تعامل با Kubernetes را ارائه می دهد که کمی انعطاف پذیر تر هستند اما همچنین فنی تر از kubernetes client-go هستند. (مشتری کنترل کننده زمان اجرا FWIW از client-go استفاده می کند.)

هنوز هم پیشنهاد می شود برای نمایش منابع سفارشی ، انواع Go را وارد کنید. معمولاً انواع آن در بسته ای است که پس از نسخه API برای آن نوع نامگذاری شده است. آنچه اکنون متفاوت است استفاده از کنترل زمان اجرا برای تعاملات به جای ClientSet است. در اینجا مثالی برای v1 منبع سفارشی SeldonDeployment با توابع برای لیست کردن و ایجاد SeldonDeployments آورده شده است.

import (
  "context"
  "log"

  SeldonVersion "github.com/seldonio/seldon-core/operator/apis/machinelearning.seldon.io/v1"
  "k8s.io/apimachinery/pkg/runtime"
  ctrl "sigs.k8s.io/controller-runtime"
  "sigs.k8s.io/controller-runtime/pkg/client"
)

var kclient client.Client

func init() {
  kclient = GetClient()
}

func GetClient() client.Client {
  scheme := runtime.NewScheme()
  SeldonVersion.AddToScheme(scheme)
  kubeconfig := ctrl.GetConfigOrDie()
  controllerClient, err := client.New(kubeconfig, client.Options{Scheme: scheme})
  if err != nil {
     log.Fatal(err)
     return nil
  }
  return controllerClient
}

func ListSeldonDeployments(namespace string) (result *SeldonVersion.SeldonDeploymentList, err error) {
  list := &SeldonVersion.SeldonDeploymentList{}
  err = kclient.List(context.TODO(), list, &client.ListOptions{Namespace: namespace})
  return list, err
}

func CreateSeldonDeployment(deployment *SeldonVersion.SeldonDeployment) (sdep *SeldonVersion.SeldonDeployment, err error) {
  err = kclient.Create(context.TODO(), deployment)
  return deployment, err
}

مشتری کنترل زمان اجرا همچنین یک برنامه جعلی برای اهداف آزمایش در دسترس دارد –

sigs.k8s.io/controller-runtime/pkg/client/fake

. یک تابع NewFakeClientWithScheme وجود دارد که می تواند برای ایجاد یک کلاینت جعلی که معادل آزمایشات واحد مشتری واقعی از GetClient در بالا است ، استفاده شود.

ساعت بدون مشتری با ساعت با یک مشتری کاملا متفاوت است. از همه مهمتر ، به اطلاع دهنده باید گفته شود كه چه نوع را تماشا كند و اشیایی كه به ما می گوید انواع عمومی غیر ساختاری است كه باید به نوع مورد نظر تبدیل شود.

import (
  "fmt"

  SeldonVersion "github.com/seldonio/seldon-core/operator/apis/machinelearning.seldon.io/v1"

  corev1 "k8s.io/api/core/v1"
  "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
  "k8s.io/apimachinery/pkg/runtime"
  "k8s.io/apimachinery/pkg/runtime/schema"
  "k8s.io/client-go/dynamic"
  "k8s.io/client-go/dynamic/dynamicinformer"
  "k8s.io/client-go/informers"
  "k8s.io/client-go/tools/cache"
  ctrl "sigs.k8s.io/controller-runtime"
)

func GetDynamicInformer(resourceType string) (informers.GenericInformer, error) {
  cfg := ctrl.GetConfigOrDie()

  // Grab a dynamic interface that we can create informers from
  dc, err := dynamic.NewForConfig(cfg)
  if err != nil {
     return nil, err
  }
  // Create a factory object that can generate informers for resource types
  factory := dynamicinformer.NewFilteredDynamicSharedInformerFactory(dc, 0, corev1.NamespaceAll, nil)
  // "GroupVersionResource" to say what to watch e.g. "deployments.v1.apps" or "seldondeployments.v1.machinelearning.seldon.io"
  gvr, _ := schema.ParseResourceArg(resourceType)
  // Finally, create our informer for deployments!
  informer := factory.ForResource(*gvr)
  return informer, nil
}

func (s *SeldonDeployServer) seldonCRDWatcher(namespace string) {
  //dynamic informer needs to be told which type to watch
  seldoninformer, _ := GetDynamicInformer("seldondeployments.v1.machinelearning.seldon.io")
  stopper := make(chan struct{})
  defer close(stopper)
  runSeldonCRDInformer(stopper, seldoninformer.Informer(), namespace)
}

func runSeldonCRDInformer(stopCh <-chan struct{}, s cache.SharedIndexInformer, namespace string) {
  handlers := cache.ResourceEventHandlerFuncs{
     AddFunc: func(obj interface{}) {

        d := &SeldonVersion.SeldonDeployment{}
        // try following https://erwinvaneyk.nl/kubernetes-unstructured-to-typed/
        err := runtime.DefaultUnstructuredConverter.
           FromUnstructured(obj.(*unstructured.Unstructured).UnstructuredContent(), d)
        if err != nil {
           fmt.Println("could not convert obj to SeldonDeployment")
           fmt.Print(err)
           return
        }
        // do what we want with the SeldonDeployment/event
     },
     DeleteFunc: func(obj interface{}) {
        // convert the obj as above do what we want with the SeldonDeployment/event
     },
     UpdateFunc: func(oldObj, newObj interface{}) {
        // convert the obj as above do what we want with the SeldonDeployment/event
     },
  }
  s.AddEventHandler(handlers)
  s.Run(stopCh)
}

مدیریت وابستگی ها

یکی از نکات مثبت رویکرد کنترل زمان برای کنترل CRD این است که به شما انعطاف بیشتری در هنگام وارد کردن کتابخانه های Go می دهد. اگر از ClientSet استفاده نمی کنید ، می توانید انواع CRD را با کمترین اتصال به نسخه k8s وارد کنید.

ClientSets برای استفاده از نسخه خاصی از client-go تولید می شوند و ممکن است در همه نسخه ها تغییراتی ایجاد کنند ، مانند پارامتر زمینه در بسیاری از توابع اصلی. وارد کردن چندین ClientSets می تواند منجر به درگیری وابستگی شود. با رویکرد کنترل زمان اجرا می توان از این امر جلوگیری کرد.

خلاصه

استفاده از Go انعطاف پذیری زیادی برای تعامل با Kubernetes به ما می دهد. ما می توانیم منابع سفارشی را مدیریت کنیم و با منابع سفارشی که مدیریت نمی کنیم تعامل داشته باشیم. حتی می توانیم با منابع سفارشی تعامل داشته باشیم که برخی از خصوصیات آنها را می شناسیم اما تعاریف کامل آنها را نمی دانیم.

در اینجا ما فقط مواردی را توضیح داده ایم که نوع کامل آن را بیشتر می دانیم ، اما مقالاتی برای مواردی وجود دارد که شما هم انواع آن را نمی دانید. دامنه گزینه ها ممکن است کمی گیج کننده باشد. امیدوارم که این مقاله به شما در انتخاب گزینه ها کمک کند.

(عنوان تصویر تصویر از دوربین شکاری نمای آب توسط kisistvan77 بر پیکسابای.)

عکس پروفایل نویسنده

داستان های من را بخوانید

مهندس در seldon.io. همکار سال هکرنون – مهندسی.

برچسب ها

با هکر نون همراه باشید

حساب رایگان خود را ایجاد کنید تا قفل تجربه خواندن سفارشی خود را باز کنید.

سئو PBN | خبر های جدید سئو و هک و سرور