Let wel. vertaal.: Operateurs is hulpprogrammatuur vir Kubernetes wat ontwerp is om die uitvoering van roetine-aksies op groepvoorwerpe tydens sekere gebeurtenisse te outomatiseer. Ons het reeds geskryf oor operateurs in
Ek het besluit om hierdie pos met 'n werklike voorbeeld te skryf nadat my pogings om dokumentasie te vind oor die skep van 'n operateur vir Kubernetes, deur die studie van die kode gegaan het.
Die voorbeeld wat beskryf sal word, is die volgende: in ons Kubernetes-kluster, elk Namespace
verteenwoordig 'n span se sandbox-omgewing, en ons wou toegang daartoe beperk sodat spanne net in hul eie sandboxes kan speel.
Jy kan bereik wat jy wil deur 'n gebruiker toe te wys aan 'n groep wat het RoleBinding
spesifiek Namespace
ΠΈ ClusterRole
met redigeerregte. Die YAML-voorstelling sal soos volg lyk:
---
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
(
Skep so RoleBinding
jy kan dit met die hand doen, maar sodra jy meer as honderd naamruimtes kry, word dit 'n vervelige taak. Dit is waar Kubernetes-operateurs handig te pas kom - hulle laat jou toe om die skepping van Kubernetes-hulpbronne te outomatiseer op grond van veranderinge in hulpbronne. In ons geval wil ons skep RoleBinding
terwyl jy skep Namespace
.
Eerstens, laat ons die funksie definieer main
, wat die vereiste opstelling doen om die stelling uit te voer en dan die stellingsaksie aanroep:
(Let wel. vertaal.: hierna word die opmerkings in die kode in Russies vertaal. Daarbenewens word die inkeping gekorrigeer vir spasies in plaas van [aanbeveel in Gaan] oortjies uitsluitlik vir die doel van beter leesbaarheid binne die raamwerk van Habr se uitleg. Na elke aanbieding is daar skakels na die oorspronklike op GitHub, waar Engelstalige opmerkings en oortjies gestoor word.)
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() // ΠΠΆΠΈΠ΄Π°Π΅ΠΌ, ΡΡΠΎ Π²ΡΠ΅ ΠΎΡΡΠ°Π½ΠΎΠ²Π»Π΅Π½ΠΎ
}
Ons doen die volgende:
- Ons het 'n hanteerder vir spesifieke bedryfstelselseine opgestel om die operateur grasieus te laat verlaat.
- Ons gebruik
WaitGroup
om alle goroutines grasieus te stop voordat die toepassing beΓ«indig word. - Ons bied toegang tot die groepering deur te skep
clientset
. - Begin
NamespaceController
, waarin al ons logika geleΓ« sal wees.
Nou het ons 'n basis vir logika nodig, en in ons geval is dit die genoemde 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
}
Hier sit ons SharedIndexInformer
, wat doeltreffend (met behulp van die kas) sal wag vir naamruimteveranderings (lees meer oor informante in die artikel "EventHandler
aan die informant, waardeur, wanneer 'n naamruimte bygevoeg word (Namespace
) word die funksie genoem createRoleBinding
.
Die volgende stap is om hierdie funksie te definieer 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))
}
}
Ons kry die naamruimte as obj
en omskep dit na 'n voorwerp Namespace
. Dan definieer ons RoleBinding
, gebaseer op die YAML-lΓͺer wat aan die begin genoem is, deur die verskafde voorwerp te gebruik Namespace
en skep RoleBinding
. Ten slotte teken ons aan of die skepping suksesvol was.
Die laaste funksie wat gedefinieer moet word is β 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
}
Hier praat ons WaitGroup
dat ons die goroutine begin en dan bel namespaceInformer
wat voorheen gedefinieer is. Wanneer 'n stopsein inkom, sal dit die funksie beΓ«indig, laat weet WaitGroup
, wat nie meer uitgevoer word nie, en hierdie funksie sal verlaat.
Inligting oor die bou en uitvoer van hierdie stelling op 'n Kubernetes-kluster kan gevind word by
Op hierdie stelling, wat skep RoleBinding
wanneer Namespace
in 'n Kubernetes-kluster, gereed.
Bron: will.com