Pastaba. vert.: operatoriai yra pagalbinė „Kubernetes“ programinė įranga, skirta automatizuoti įprastų veiksmų klasterio objektuose vykdymą, kai įvyksta tam tikri įvykiai. Apie operatorius jau rašėme
Nusprendžiau parašyti šį įrašą su realaus gyvenimo pavyzdžiu po to, kai bandžiau rasti Kubernetes operatoriaus kūrimo dokumentaciją, kuri buvo atlikta ištyrus kodą.
Bus aprašytas toks pavyzdys: mūsų „Kubernetes“ klasteryje kiekvienas Namespace
atstovauja komandos smėlio dėžės aplinkai, todėl norėjome apriboti prieigą prie jų, kad komandos galėtų žaisti tik savo smėlio dėžėse.
Galite pasiekti tai, ko norite, priskirdami vartotojui grupę, kuri turi RoleBinding
į konkrečius Namespace
и ClusterRole
su redagavimo teisėmis. YAML reprezentacija atrodys taip:
---
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
(
Sukurk viena RoleBinding
Galite tai padaryti rankiniu būdu, tačiau peržengus šimto vardų erdvių ribą, tai tampa varginančia užduotimi. Čia praverčia „Kubernetes“ operatoriai – jie leidžia automatizuoti „Kubernetes“ išteklių kūrimą pagal išteklių pakeitimus. Mūsų atveju norime kurti RoleBinding
kurdamas Namespace
.
Visų pirma, apibrėžkime funkciją main
kuri atlieka reikalingą sąranką, kad paleistų sakinį, ir tada iškviečia teiginio veiksmą:
(Pastaba. vert.: čia ir žemiau kodo komentarai išversti į rusų kalbą. Be to, įtrauka buvo pakoreguota į tarpus, o ne [rekomenduojama eiti] skirtukus vien dėl geresnio skaitomumo Habr išdėstyme. Po kiekvieno įrašo yra nuorodos į originalą „GitHub“, kur saugomi komentarai ir skirtukai anglų kalba.)
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() // Ожидаем, что все остановлено
}
Mes atliekame šiuos veiksmus:
- Konfigūruojame konkrečių operacinės sistemos signalų tvarkyklę, kad būtų galima maloniai nutraukti operatoriaus darbą.
- Mes naudojame
WaitGroup
grakščiai sustabdyti visas gorutinas prieš nutraukiant paraišką. - Sukurdami suteikiame prieigą prie klasterio
clientset
. - Paleisti
NamespaceController
, kuriame bus visa mūsų logika.
Dabar mums reikia logikos pagrindo, o mūsų atveju tai yra minėta 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
}
Čia mes sukonfigūruojame SharedIndexInformer
, kuri efektyviai (naudodama talpyklą) lauks pakeitimų vardų erdvėse (daugiau apie informatorius skaitykite straipsnyje “EventHandler
informatoriui, kad pridedant vardų erdvę (Namespace
) vadinama funkcija createRoleBinding
.
Kitas žingsnis – apibrėžti šią funkciją 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))
}
}
Vardų erdvę gauname kaip obj
ir paversti jį objektu Namespace
. Tada apibrėžiame RoleBinding
, remiantis pradžioje minėtu YAML failu, naudojant pateiktą objektą Namespace
ir kuriant RoleBinding
. Galiausiai registruojame, ar sukūrimas pavyko.
Paskutinė apibrėžtina funkcija yra 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
}
Čia mes kalbame WaitGroup
kad paleidžiame gorutiną ir tada skambiname namespaceInformer
, kuris buvo apibrėžtas anksčiau. Atėjus stabdymo signalui, jis nutraukia funkciją, informuoja WaitGroup
, kuri nebevykdoma, ir ši funkcija bus uždaryta.
Informacijos apie šio pareiškimo kūrimą ir vykdymą Kubernetes klasteryje rasite
Tai operatoriui, kuris kuria RoleBinding
kada Namespace
„Kubernetes“ klasteryje, paruošta.
Šaltinis: www.habr.com