Bilješka. prev.: Operatori su pomoćni softver za Kubernetes, dizajniran za automatiziranje izvršavanja rutinskih radnji na objektima klastera kada se dogode određeni događaji. Već smo pisali o operaterima u
Odlučio sam napisati ovaj post s primjerom iz stvarnog života nakon mojih pokušaja da pronađem dokumentaciju o stvaranju operatora za Kubernetes, što je prošlo kroz proučavanje koda.
Primjer koji će biti opisan je sljedeći: u našem Kubernetes klasteru, svaki Namespace
predstavlja timsko sandbox okruženje i htjeli smo im ograničiti pristup tako da timovi mogu igrati samo u svojim sandboxovima.
Možete postići ono što želite tako da korisniku dodijelite grupu koja ima RoleBinding
na konkretne Namespace
и ClusterRole
s pravom uređivanja. YAML prikaz će izgledati ovako:
---
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
Stvori jedan RoleBinding
Možete to učiniti ručno, ali nakon što prijeđete granicu od stotinu imenskih prostora, to postaje dosadan zadatak. Tu Kubernetes operatori dolaze od koristi—omogućuju vam da automatizirate stvaranje Kubernetes resursa na temelju promjena resursa. U našem slučaju želimo stvarati RoleBinding
pri stvaranju Namespace
.
Prije svega, definirajmo funkciju main
koji izvodi potrebnu postavku za izvođenje naredbe i zatim poziva radnju naredbe:
(Bilješka. prev.: ovdje i ispod su komentari u kodu prevedeni na ruski. Osim toga, uvlačenje je ispravljeno na razmake umjesto [preporučeno u Go] karticama isključivo u svrhu bolje čitljivosti unutar Habr izgleda. Nakon svakog popisa nalaze se poveznice na izvornik na GitHubu, gdje su pohranjeni komentari i kartice na engleskom jeziku.)
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() // Ожидаем, что все остановлено
}
(
Radimo sljedeće:
- Konfiguriramo rukovatelja za specifične signale operacijskog sustava kako bismo uzrokovali graciozno prekidanje operatora.
- Koristimo
WaitGroup
kako biste elegantno zaustavili sve goroutine prije prekida aplikacije. - Pristup klasteru omogućujemo stvaranjem
clientset
. - Pokreni
NamespaceController
, u kojem će biti smještena sva naša logika.
Sada nam treba osnova za logiku, au našem slučaju to je spomenuta 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
}
Ovdje konfiguriramo SharedIndexInformer
, koji će učinkovito (pomoću predmemorije) čekati promjene u imenskim prostorima (više o doušnicima pročitajte u članku “EventHandler
u informator, tako da prilikom dodavanja imenskog prostora (Namespace
) poziva se funkcija createRoleBinding
.
Sljedeći korak je definiranje ove funkcije 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))
}
}
Dobivamo imenski prostor kao obj
i pretvoriti ga u objekt Namespace
. Zatim definiramo RoleBinding
, na temelju YAML datoteke spomenute na početku, koristeći navedeni objekt Namespace
i stvaranje RoleBinding
. Na kraju, bilježimo je li stvaranje bilo uspješno.
Posljednja funkcija koju treba definirati je 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
}
Evo razgovaramo WaitGroup
da pokrenemo goroutine i zatim pozovemo namespaceInformer
, koji je prethodno definiran. Kada stigne signal za zaustavljanje, prekinut će funkciju, informirajte WaitGroup
, koji se više ne izvršava, a ova funkcija će izaći.
Informacije o izgradnji i pokretanju ove izjave na Kubernetes klasteru mogu se pronaći u
To je sve za operatera koji stvara RoleBinding
kada Namespace
u klasteru Kubernetes, spreman.
Izvor: www.habr.com