Белешка. трансл.: Оператори су помоћни софтвер за Кубернетес, дизајниран да аутоматизује извршавање рутинских радњи на објектима кластера када се догоде одређени догађаји. Већ смо писали о оператерима у , где су говорили о темељним идејама и принципима свог рада. Али ако је тај материјал више био поглед са стране рада са готовим компонентама за Кубернетес, онда је сада предложени превод новог чланка већ визија програмера/ДевОпс инжењера збуњеног имплементацијом новог оператера.

Одлучио сам да напишем овај пост са примером из стварног живота након покушаја да пронађем документацију о креирању оператора за Кубернетес, који је прошао кроз проучавање кода.
Пример који ће бити описан је следећи: у нашем Кубернетес кластеру, сваки Namespace представља окружење пешчаног окружења тима, и желели смо да им ограничимо приступ тако да тимови могу да играју само у својим сандбоковима.
Можете постићи оно што желите тако што ћете доделити кориснику групу која има RoleBinding do specifičnih Namespace и ClusterRole са уређивачким правима. ИАМЛ репрезентација ће изгледати овако:
---
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 Можете то да урадите ручно, али након што пређете стотину именских простора, то постаје досадан задатак. Ово је место где Кубернетес оператери долазе на руку — они вам омогућавају да аутоматизујете креирање Кубернетес ресурса на основу промена ресурса. У нашем случају желимо да стварамо RoleBinding при стварању Namespace.
Пре свега, хајде да дефинишемо функцију mainкоји врши потребно подешавање за покретање наредбе, а затим позива акцију наредбе:
(Белешка. трансл.: овде и испод коментари у коду су преведени на руски. Поред тога, увлачење је исправљено на размаке уместо [препоручено у Го] картицама искључиво у сврху боље читљивости у оквиру Хабр распореда. После сваке листе постоје везе до оригинала на ГитХуб-у, где се чувају коментари и картице на енглеском језику.)
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() // Ожидаем, что все остановлено
}(У )
Радимо следеће:
- Конфигуришемо руковалац за специфичне сигнале оперативног система да изазове грациозан завршетак оператора.
- Користимо
WaitGroupда грациозно зауставите све горрутине пре окончања апликације. - Омогућавамо приступ кластеру креирањем
clientset. - Лансирање
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
}(У )
Овде конфигуришемо SharedIndexInformer, који ће ефективно (користећи кеш) чекати промене у именским просторима (више о информаторима прочитајте у чланку „"- прибл. превод). Након овога се повезујемо 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))
}
}(У )
Добијамо именски простор као obj и претворити га у објекат Namespace. Затим дефинишемо RoleBinding, на основу ИАМЛ датотеке поменуте на почетку, користећи обезбеђени објекат 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
}(У )
Ево ми причамо WaitGroupда покренемо гороутину и затим позовемо namespaceInformer, што је претходно дефинисано. Када стигне сигнал за заустављање, он ће прекинути функцију, обавестите WaitGroup, који се више не извршава, а ова функција ће изаћи.
Информације о изградњи и покретању ове изјаве на Кубернетес кластеру можете пронаћи у .
То је то за оператера који креира RoleBinding када Namespace у Кубернетес кластеру, спреман.
Извор: ввв.хабр.цом
