Nota. transl.: Els operadors són programari auxiliar per a Kubernetes dissenyat per automatitzar l'execució d'accions rutinàries en objectes de clúster durant determinats esdeveniments. Ja hem escrit sobre els operadors a
Vaig decidir escriure aquesta publicació amb un exemple de la vida real després que els meus intents de trobar documentació sobre la creació d'un operador per a Kubernetes, van passar per l'estudi del codi.
L'exemple que es descriu és el següent: al nostre clúster de Kubernetes, cadascun Namespace
representa l'entorn sandbox d'un equip, i volíem restringir-hi l'accés perquè els equips només puguin jugar als seus propis sandbox.
Pots aconseguir el que vulguis assignant un usuari a un grup que tingui RoleBinding
a concret Namespace
и ClusterRole
amb drets d'edició. La representació de YAML tindrà aquest aspecte:
---
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
Crear tal RoleBinding
podeu fer-ho manualment, però un cop supereu el centenar d'espais de noms, esdevé una tasca tediosa. Aquí és on els operadors de Kubernetes són útils: us permeten automatitzar la creació de recursos de Kubernetes en funció dels canvis en els recursos. En el nostre cas, volem crear RoleBinding
mentre es crea Namespace
.
Primer de tot, anem a definir la funció main
, que fa la configuració necessària per executar la instrucció i després invoca l'acció de la instrucció:
(Nota. transl.: d'ara endavant, els comentaris del codi es tradueixen al rus. A més, el sagnat es corregeix per als espais en comptes de les pestanyes [recomanat a Go] només amb la finalitat d'una millor llegibilitat en el marc del disseny de Habr. Després de cada llistat, hi ha enllaços a l'original a GitHub, on es guarden els comentaris i les pestanyes en anglès.)
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() // Ожидаем, что все остановлено
}
Fem el següent:
- Hem configurat un controlador per a senyals específics del sistema operatiu perquè l'operador surti amb gràcia.
- Fem servir
WaitGroup
per aturar amb gràcia totes les goroutines abans que finalitzi l'aplicació. - Proporcionem accés al clúster mitjançant la creació
clientset
. - Llançament
NamespaceController
, en el qual s'ubicarà tota la nostra lògica.
Ara necessitem una base per a la lògica, i en el nostre cas és l'esmentat 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
}
(
Aquí estem fixant SharedIndexInformer
, que de manera eficient (utilitzant la memòria cau) esperarà els canvis d'espai de noms (llegiu més sobre informadors a l'article "EventHandler
a l'informador, gràcies al qual, en afegir un espai de noms (Namespace
) s'anomena la funció createRoleBinding
.
El següent pas és definir aquesta funció 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))
}
}
(
Obtenim l'espai de noms com obj
i convertir-lo en un objecte Namespace
. Després definim RoleBinding
, basat en el fitxer YAML esmentat al principi, utilitzant l'objecte proporcionat Namespace
i creant RoleBinding
. Finalment, registrem si la creació va tenir èxit.
L'última funció a definir és − 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
}
(
Aquí estem parlant WaitGroup
que comencem la goroutina i després cridem namespaceInformer
que s'ha definit prèviament. Quan arribi un senyal de parada, finalitzarà la funció, informa WaitGroup
, que ja no s'està executant, i aquesta funció sortirà.
Podeu trobar informació sobre com crear i executar aquesta declaració en un clúster de Kubernetes a
En aquesta declaració, que crea RoleBinding
Quan Namespace
en un clúster de Kubernetes, llest.
Font: www.habr.com