Nota. transl.: L'operatori sò software ausiliari per Kubernetes, cuncepitu per automatizà l'esekzione di l'azzioni di rutina nantu à l'oggetti di cluster quandu certi avvenimenti accadenu. Avemu digià scrittu annantu à l'operatori in
Aghju decisu di scrive stu post cun un esempiu di a vita reale dopu à i mo tentativi di truvà documentazione nantu à a creazione di un operatore per Kubernetes, chì passava per studià u codice.
L'esempiu chì serà descrittu hè questu: in u nostru cluster Kubernetes, ognunu Namespace
rapprisenta l'ambiente sandbox di una squadra, è vulemu limità l'accessu à elli in modu chì e squadre puderanu ghjucà solu in i so propii sandbox.
Pudete ottene ciò chì vulete assignendu un utilizatore un gruppu chì hà RoleBinding
à specifichi Namespace
и ClusterRole
cù diritti di editazione. A rapprisintazioni YAML sarà cusì:
---
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
Crea unu RoleBinding
Pudete fà manualmente, ma dopu avè attraversatu a marca di centu spazii di nomi, diventa un compitu tedious. Hè quì chì l'operatori di Kubernetes sò utili - permettenu di automatizà a creazione di risorse Kubernetes basatu nantu à i cambiamenti à e risorse. In u nostru casu vulemu creà RoleBinding
mentre crea Namespace
.
Prima di tuttu, definiscemu a funzione main
chì face a configurazione necessaria per eseguisce a dichjarazione è poi chjama l'azzione di dichjarazione:
(Nota. transl.: quì è sottu i cumenti in u codice sò tradutti in Russu. Inoltre, l'indentazione hè stata curretta à spazii invece di tabulazioni [consigliate in Go] solu per u scopu di megliu leghjibilità in u layout Habr. Dopu ogni listinu ci sò ligami à l'uriginale in GitHub, induve i cumenti è e tabulazioni in lingua inglese sò almacenati.)
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() // Ожидаем, что все остановлено
}
Facemu i seguenti:
- Configuremu un gestore per signali specifichi di u sistema operatore per causà a fine grazia di l'operatore.
- Avemu aduprà
WaitGroup
per piantà graziamente tutte e goroutines prima di finisce l'applicazione. - Avemu furnisce l'accessu à u cluster creendu
clientset
. - Lanciari
NamespaceController
, in quale sarà situata tutta a nostra logica.
Avà avemu bisognu di una basa per a logica, è in u nostru casu questu hè quellu mintuatu 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
}
(
Quì avemu cunfigurà SharedIndexInformer
, chì in modu efficace (usendu una cache) aspittà per i cambiamenti in i namespaces (leghjite più nantu à l'informatori in l'articulu "EventHandler
à l'informatore, perchè quandu aghjunghje un spaziu di nomi (Namespace
) a funzione hè chjamata createRoleBinding
.
U prossimu passu hè di definisce sta funzione 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))
}
}
(
Avemu u spaziu di nomi cum'è obj
è cunvertisce in un oggettu Namespace
. Allora avemu definitu RoleBinding
, basatu annantu à u schedariu YAML mintuatu à u principiu, utilizendu l'ughjettu furnitu Namespace
è crea RoleBinding
. Infine, registremu se a creazione hà successu.
L'ultima funzione per esse definita hè 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
}
(
Quì avemu parlatu WaitGroup
chì avemu lanciatu u goroutine è dopu chjamate namespaceInformer
, chì hè statu definitu prima. Quandu u signale di stop arriva, finisce a funzione, informa WaitGroup
, chì ùn hè più eseguitu, è sta funzione esce.
L'infurmazione nantu à a custruzzione è a gestione di sta dichjarazione nantu à un cluster Kubernetes pò esse truvata in
Hè per l'operatore chì crea RoleBinding
quandu Namespace
in u cluster Kubernetes, pronta.
Source: www.habr.com