Opmerking vertaler.: Operators (operators) zijn hulpprogramma's voor Kubernetes, bedoeld om de uitvoering van routinematige taken op clusterobjecten te automatiseren bij bepaalde gebeurtenissen. We hebben al eerder over operators geschreven in , waar we de fundamentele ideeën en principes van hun werking hebben besproken. Maar waar dat materiaal meer een kijk was vanuit het perspectief van het gebruik van kant-en-klare componenten voor Kubernetes, biedt deze nieuwe vertaling van een artikel nu het perspectief van een ontwikkelaar/DevOps-engineer, die zich bezighoudt met de implementatie van een nieuwe operator.

Dit bericht met een praktijkvoorbeeld heb ik geschreven na mijn pogingen om documentatie te vinden voor het maken van een operator voor Kubernetes, wat leidde tot het bestuderen van de code.
Het voorbeeld dat zal worden beschreven, is als volgt: in onze Kubernetes-cluster vertegenwoordigt elke Namespace een sandbox-omgeving voor een bepaald team, en we wilden de toegang hiertoe beperken, zodat teams alleen in hun eigen sandboxes konden werken.
Dit kan worden bereikt door een gebruiker aan een groep toe te wijzen, die een RoleBinding heeft voor een specifieke Namespace en ClusterRole met het recht om te bewerken. De YAML-representatie zou er als volgt uitzien:
---
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(, in )
Het is ook mogelijk om dit handmatig te maken, maar na meer dan honderd namespaces wordt dit een vermoeiende taak. Hier komen Kubernetes-operators van pas: ze maken het mogelijk om de creatie van Kubernetes-resources te automatiseren, gebaseerd op wijzigingen in resources. In ons geval willen we creëren RoleBinding bij het creëren van RoleBinding Allereerst definiëren we een functie Namespace.
, die de vereiste configuratie voor het starten van de operator uitvoert en vervolgens de actie van de operator aanroept: main: hier en verder zijn de opmerkingen in de code vertaald naar het Nederlands. Bovendien zijn de inspringingen gecorrigeerd naar spaties in plaats van [door Go aanbevolen] tabs, uitsluitend voor een betere leesbaarheid binnen de layout van Habr. Na elke listing zijn er links naar de originele op GitHub, waar de Engelstalige opmerkingen en tabs zijn behouden.
(Opmerking vertaler.: Hier en verder zijn de opmerkingen in de code vertaald naar het Russisch. Bovendien zijn de inspringingen aangepast van tabs naar spaties in plaats van [recommended in Go] puur voor een betere leesbaarheid in de opmaak van Habr. Na elke listing volgen links naar de originele GitHub, waar de Engelstalige opmerkingen en tabs zijn behouden.)
func main() {
// Stel de loguitvoer in op de console STDOUT
log.SetOutput(os.Stdout)
sigs := make(chan os.Signal, 1) // Maak een kanaal voor het ontvangen van OS-signalen
stop := make(chan struct{}) // Maak een kanaal voor het ontvangen van een stop-signaal
// Registreer het ontvangen van SIGTERM in het kanaal sigs
signal.Notify(sigs, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
// Goroutines kunnen zichzelf toevoegen aan de WaitGroup,
// zodat hun voltooiing kan worden afgewacht
wg := &sync.WaitGroup{}
runOutsideCluster := flag.Bool("run-outside-cluster", false, "Stel deze vlag in wanneer u buiten de cluster draait.")
flag.Parse()
// Maak clientset voor interactie met de Kubernetes-cluster
clientset, err := newClientSet(*runOutsideCluster)
if err != nil {
panic(err.Error())
}
controller.NewNamespaceController(clientset).Run(stop, wg)
<-sigs // Wacht op signalen (totdat er een signaal is ontvangen, gebeurt er verder niets)
log.Printf("Afsluiten...")
close(stop) // Geef de goroutines de opdracht om te stoppen
wg.Wait() // Wacht tot alles is gestopt
}(, in )
Wij doen het volgende:
- We stellen handlers in voor specifieke signals van het besturingssysteem om een correcte (graceful) afsluiting van de operator te triggeren.
- We gebruiken
WaitGroup, om ervoor te zorgen dat alle goroutines correct stoppen voordat de applicatie wordt afgesloten. - We bieden toegang tot de cluster door
clientset. - We starten
NamespaceController, waarin al onze logica zal worden geplaatst.
Nu is er basis nodig voor de logica, en in ons geval is dat de eerdergenoemde 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
}(, in )
Hier stellen we de SharedIndexInformer, in, die efficiënt (met behulp van cache) wijzigingen in de namespaces zal afwachten. (voor meer informatie over informers, lees het artikel '» — opmerking van de vertaler.). Daarna verbinden we de EventHandler met de informer, zodat wanneer een namespace wordt toegevoegd (Namespace) de functie createRoleBinding.
De volgende stap is deze functie te definiëren func (c *NamespaceController) createRoleBinding(obj interface{}) { namespaceObj := obj.(*v1.Namespace) namespaceName := namespaceObj.NameroleBinding := &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("Het maken van Role Binding is mislukt: %s", err.Error())) } else { log.Println(fmt.Sprintf("AD RoleBinding voor Namespace gemaakt: %s", roleBinding.Name)) } } createRoleBinding:
We ontvangen het namespace als(, in )
obj obj en transformeren het in een object Namespace. Vervolgens bepalen we RoleBinding, op basis van wat in het eerder vermelde YAML-bestand staat, met behulp van het verstrekte object Namespace en creëren RoleBinding. Ten slotte loggen we of de creatie succesvol is verlopen.
De laatste functie die we moeten definiëren, is 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
}(, in )
Hier zeggen we WaitGroup, dat we een goroutine zullen starten en daarna aanroepen namespaceInformer, die eerder was gedefinieerd. Wanneer het stop signaal binnenkomt, zal het de functie beëindigen, melden WaitGroup, dat het niet meer uitgevoerd wordt, en deze functie zal zijn werk beëindigen.
Informatie over het bouwen en uitvoeren van deze operator in een Kubernetes-cluster is te vinden in .
Hier maakt de operator, die RoleBinding wanneer er Namespace in het Kubernetes-cluster verschijnt, zich klaar.
Bron: habr.com
