Noat. transl.: Operators binne auxiliary software foar Kubernetes, ûntworpen om de útfiering fan routine aksjes op klusterobjekten te automatisearjen as bepaalde eveneminten foarkomme. Wy hawwe al skreaun oer operators yn
Ik besleat dit berjocht te skriuwen mei in foarbyld fan 'e echte libben nei myn besykjen om dokumintaasje te finen oer it meitsjen fan in operator foar Kubernetes, dy't gie troch it bestudearjen fan de koade.
It foarbyld dat beskreaun wurdt is dit: yn ús Kubernetes-kluster, elk Namespace
fertsjintwurdiget de sânboxomjouwing fan in team, en wy woenen de tagong ta har beheine, sadat teams allinich yn har eigen sânboxen koene spielje.
Jo kinne berikke wat jo wolle troch in brûker in groep ta te jaan dy't hat RoleBinding
nei spesifyk Namespace
и ClusterRole
mei bewurkingsrjochten. De YAML-fertsjintwurdiging sil der sa útsjen:
---
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
(
Meitsje ien RoleBinding
Jo kinne it mei de hân dwaan, mar nei it oerstekken fan de hûndert nammeromten, wurdt it in ferfeelsume taak. Dit is wêr't Kubernetes-operators goed fan pas komme - se kinne jo it oanmeitsjen fan Kubernetes-boarnen automatisearje op basis fan feroarings oan boarnen. Yn ús gefal wolle wy meitsje RoleBinding
wylst it meitsjen Namespace
.
Lit ús earst de funksje definiearje main
dy't de fereaske opset docht om de ferklearring út te fieren en dan de ferklearringaksje opropt:
(Noat. transl.: hjir en ûnder de opmerkings yn 'e koade binne oerset yn it Russysk. Derneist is de ynspringing korrizjearre nei spaasjes ynstee fan [oanrikkemandearre yn Go] ljeppers allinich foar it doel fan bettere lêsberens binnen de Habr-opmaak. Nei elke fermelding binne d'r keppelings nei it orizjineel op GitHub, wêr't Ingelsktalige opmerkings en ljeppers wurde opslein.)
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() // Ожидаем, что все остановлено
}
Wy dogge it folgjende:
- Wy konfigurearje in handler foar spesifike bestjoeringssysteem sinjalen foar in feroarsaakje sierlike beëiniging fan de operator.
- Wy brûke
WaitGroup
om alle goroutines sierlik te stopjen foardat de applikaasje beëinige wurdt. - Wy jouwe tagong ta it kluster troch te meitsjen
clientset
. - Launch
NamespaceController
, wêryn al ús logika lizze sil.
No hawwe wy in basis nedich foar logika, en yn ús gefal is dit de neamde 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
}
(
Hjir konfigurearje wy SharedIndexInformer
, dy't effektyf (mei in cache) wachtsje op feroaringen yn nammeromten (lês mear oer ynformanten yn it artikel "EventHandler
oan de ynformateur, sadat by it tafoegjen fan in nammeromte (Namespace
) funksje wurdt neamd createRoleBinding
.
De folgjende stap is om dizze funksje te definiearjen 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))
}
}
(
Wy krije de nammeromte as obj
en konvertearje it nei in objekt Namespace
. Dan definiearje wy RoleBinding
, basearre op it YAML-bestân neamd oan it begjin, mei it opjûne objekt Namespace
en oanmeitsjen RoleBinding
. As lêste, loggen wy oft de skepping wie suksesfol.
De lêste funksje om te definiearjen 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
}
(
Hjir prate wy WaitGroup
dat wy lansearje de goroutine en dan belje namespaceInformer
, dy't earder definiearre is. As it stopsinjaal komt, sil de funksje einigje, ynformearje WaitGroup
, dy't net mear útfierd wurdt, en dizze funksje sil ôfslute.
Ynformaasje oer it bouwen en útfieren fan dizze ferklearring op in Kubernetes-kluster is te finen yn
Dat is it foar de operator dy't skept RoleBinding
wannear Namespace
yn de Kubernetes kluster, klear.
Boarne: www.habr.com