Märkus tõlke kohta.: Operaatorid (operators) on Kubernetes'e abimoodulid, mis on loodud automaatseks rutiinsete toimingute tegemiseks klastrite objektide üle teatud sündmuste korral. Oleme juba kirjutanud operaatori kohta , kus rääkisime nende töö põhialustest ja põhimõtetest. Kui eelmine materjal oli pigem pilk valmi komponentide kasutamisele Kubernetes'es, siis nüüd pakutud uus artikli tõlge on arendaja/DevOps-inseneri vaatenurk, kes on hädas uue operaatori rakendamisega.

Selle postituse reaalse elu näitena otsustasin kirjutada pärast oma katseid leida dokumentatsiooni Kubernetes'e operaatori loomise kohta, mis hõlmas koodi uurimist.
Näide, mida kirjeldame, on järgmine: meie Kubernetes'e klastris iga Namespace esindab mingisuguse meeskonna liivakasti, ja soovisime juurdepääsu piirata nii, et meeskonnad saaksid mängida ainult oma liivakastides.
Selle saavutamiseks on vajalik määrata kasutajale grupp, millel on RoleBinding konkreetsete Namespace ja ClusterRole redigeerimisõigustega. YAML-esitus näeb välja järgmine:
---
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(, in )
Saab luua ka käsitsi, kuid pärast saja nimede ruumi ületamist muutub see tüütuks. Just siin aitavad Kubernetes'e operaatorid — nad võimaldavad automatiseerida Kubernetes'e ressursside loomist, tuginedes ressursside muudatustele. Meie puhul tahame luua RoleBinding loome loomisel RoleBinding Esimesena määratleme funktsiooni Namespace.
, mis teeb vajalikud seaded operaatori käivitamiseks ja seejärel kutsub operaatori tegevuse: main: siin ja edasi on koodis kommentaarid tõlgitud eesti keelde. Lisaks on sisestused parandatud ruumideks, mitte [Go's soovitatavaks] tabulatsioonideks ainult parema loetavuse huvides Habr'i disaini kontekstis. Iga loendi järel on lingid originaalile GitHub'is, kus on säilitatud ingliskeelsed kommentaarid ja tabulatsioonid.)
(Märkus tõlke kohta.: siin ja edaspidi on koodi kommentaarid tõlgitud vene keelde. Lisaks on sisestused parandatud ruutude asemel [Go soovitatud] tühikuteks, et saavutada paremat loetavust Habra vormindamise raames. Iga nimekirja järel on lingid originaalidele GitHubis, kus on säilitatud ingliskeelsed kommentaarid ja ruudud.)
func main() {
// Seadame logide väljundi konsooli STDOUT
log.SetOutput(os.Stdout)
sigs := make(chan os.Signal, 1) // Loome kanali operatsioonisüsteemi signaalide saamiseks
stop := make(chan struct{}) // Loome kanali peatamis-signaali saamiseks
// Registreerime SIGTERM signaalide saamiseks sigs kanalisse
signal.Notify(sigs, os.Interrupt, syscall.SIGTERM, syscall.SIGINT)
// Goroutines saavad end ise lisada WaitGroup'i,
// et oodata nende lõpetamist
wg := &sync.WaitGroup{}
runOutsideCluster := flag.Bool("run-outside-cluster", false, "Seadke see lipp, kui töötate väljaspool klastrit.")
flag.Parse()
// Loome clientset'i Kubernetes klassiga suhtlemiseks
clientset, err := newClientSet(*runOutsideCluster)
if err != nil {
panic(err.Error())
}
controller.NewNamespaceController(clientset).Run(stop, wg)
<-sigs // Ootame signaale (enne signaali saamist ei juhtu rohkem midagi)
log.Printf("Suleme...\n")
close(stop) // Ütleme goroutines'ile, et peatuda
wg.Wait() // Ootame, kuni kõik on peatatud
}(, in )
Teeme järgmist:
- Konfigureerime spetsiaalse operatsioonisüsteemi signaalide töötlejat, et kutsuda esile korrektne (graceful) operaatori lõpetamine.
- Kasutame
WaitGroup, et peatada kõik goroutines enne rakenduse lõpetamist. - Pakume juurdepääsu klastrile ülesande loomisega
clientset. - Käivitame
NamespaceController, kuhu peame kõik oma loogika paigutama.
Nüüd on meil vajalik loogika alus, ja meie jaoks on see mainitud 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
}(, in )
Siin seadistame SharedIndexInformer, mis ootab tõhusalt (kasutades vahemälu) muudatusi nimedevahelistes ruumides (lisainformatsiooni informeerijate kohta leiate artiklist „» — märkus tõlke kohta.). Seejärel ühendame EventHandler informeerijaga, mis tähendab, et nimedevahelise ruumi lisamisel (Namespace) kutsutakse välja funktsioon createRoleBinding.
Järgmine samm — määrata see funktsioon 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("Rolli sidumise loomine ebaõnnestus: %s", err.Error()))
} else {
log.Println(fmt.Sprintf("Loodud AD Rolli sidumine nimedevahelise ruumi jaoks: %s", roleBinding.Name))
}
}(, in )
Saame nimedevahelise ruumi nagu obj ja teisendame selle objektiks Namespace. Seejärel määrame RoleBinding, lähtudes eelmainitud YAML-failist, kasutades antud objekti Namespace ja luues RoleBinding. Lõpuks logime, kas loomine õnnestus.
Viimane funktsioon, mille määratlemine on vajalik, on Käita:
// 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
}(, in )
Siin räägime WaitGroup, et käivitame goroutine'i ja seejärel kutsume namespaceInformer'i, mis on eelnevalt määratletud. Kui tuleb peatamise signaal, lõpetab see funktsioon töö, teatab WaitGroup, et see ei toimi enam, ja see funktsioon lõpetab oma töö.
Teavet selle operaatori kogumise ja käitamise kohta Kubernetes'i klastris leiate .
Sellega operaator, mis loob RoleBinding ilmudes Namespace Kubernetes'i klastris, on valmis.
Allikas: habr.com
