Bemærk. overs.: Operatører er hjælpesoftware til Kubernetes, designet til at automatisere udførelsen af rutinehandlinger på klyngeobjekter, når visse hændelser opstår. Vi har allerede skrevet om operatører i
Jeg besluttede at skrive dette indlæg med et eksempel fra det virkelige liv efter mine forsøg på at finde dokumentation om oprettelse af en operatør til Kubernetes, som gik gennem at studere koden.
Eksemplet, der vil blive beskrevet, er dette: i vores Kubernetes-klynge, hver Namespace
repræsenterer et holds sandkassemiljø, og vi ønskede at begrænse adgangen til dem, så hold kun kunne spille i deres egne sandkasser.
Du kan opnå det, du ønsker, ved at tildele en bruger en gruppe, der har RoleBinding
til specifik Namespace
и ClusterRole
med redigeringsrettigheder. YAML-repræsentationen vil se sådan ud:
---
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
Skab en RoleBinding
Du kan gøre det manuelt, men efter at have krydset de hundrede navneområder, bliver det en kedelig opgave. Det er her, Kubernetes-operatører er nyttige - de giver dig mulighed for at automatisere oprettelsen af Kubernetes-ressourcer baseret på ændringer af ressourcer. I vores tilfælde ønsker vi at skabe RoleBinding
mens du skaber Namespace
.
Først og fremmest, lad os definere funktionen main
som udfører den nødvendige opsætning for at køre sætningen og derefter kalder sætningshandlingen:
(Bemærk. overs.: her og nedenfor er kommentarerne i koden oversat til russisk. Derudover er indrykket blevet rettet til mellemrum i stedet for [anbefalet i Go] faner udelukkende med henblik på bedre læsbarhed i Habr-layoutet. Efter hver liste er der links til originalen på GitHub, hvor engelsksprogede kommentarer og faner er gemt.)
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() // Ожидаем, что все остановлено
}
Vi gør følgende:
- Vi konfigurerer en handler til specifikke operativsystemsignaler for at forårsage en yndefuld opsigelse af operatøren.
- Vi bruger
WaitGroup
for yndefuldt at stoppe alle goroutiner, før applikationen afsluttes. - Vi giver adgang til klyngen ved at oprette
clientset
. - Lad os starte
NamespaceController
, hvori al vores logik vil være placeret.
Nu mangler vi et grundlag for logik, og i vores tilfælde er det den nævnte 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
}
(
Her konfigurerer vi SharedIndexInformer
, som effektivt (ved hjælp af en cache) vil vente på ændringer i navnerum (læs mere om informanter i artiklen "EventHandler
til informanten, så når du tilføjer et navneområde (Namespace
) funktion kaldes createRoleBinding
.
Det næste trin er at definere denne funktion 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))
}
}
(
Vi får navnerummet som obj
og konvertere det til et objekt Namespace
. Så definerer vi RoleBinding
, baseret på YAML-filen nævnt i begyndelsen, ved hjælp af det medfølgende objekt Namespace
og skabe RoleBinding
. Til sidst logger vi, om oprettelsen lykkedes.
Den sidste funktion, der skal defineres, er 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
}
(
Her snakker vi WaitGroup
at vi starter goroutinen og derefter kalder namespaceInformer
, som tidligere er defineret. Når stopsignalet kommer, vil det afslutte funktionen, oplys WaitGroup
, som ikke længere udføres, og denne funktion afsluttes.
Information om at bygge og køre denne erklæring på en Kubernetes-klynge kan findes i
Det er det for operatøren, der skaber RoleBinding
hvornår Namespace
i Kubernetes-klyngen, klar.
Kilde: www.habr.com