Notera. transl.: Operatörer är hjälpprogram för Kubernetes, utformad för att automatisera utförandet av rutinåtgärder på klusterobjekt när vissa händelser inträffar. Vi har redan skrivit om operatörer i , där de pratade om de grundläggande idéerna och principerna för sitt arbete. Men om det materialet mer var en vy från sidan av driften av färdiga komponenter för Kubernetes, så är översättningen av den nya artikeln som nu föreslagits redan visionen för en utvecklare/DevOps-ingenjör som förbryllas över implementeringen av en ny operatör.

Jag bestämde mig för att skriva det här inlägget med ett exempel från verkligheten efter mina försök att hitta dokumentation om att skapa en operatör för Kubernetes, som gick igenom koden.
Exemplet som kommer att beskrivas är detta: i vårt Kubernetes-kluster, var och en Namespace representerar ett lags sandlådemiljö, och vi ville begränsa tillgången till dem så att lag bara kunde spela i sina egna sandlådor.
Du kan uppnå vad du vill genom att tilldela en användare en grupp som har RoleBinding till specifikt Namespace и ClusterRole med redigeringsrättigheter. YAML-representationen kommer att se ut så här:
---
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
(I )
Skapa en RoleBinding Du kan göra det manuellt, men efter att ha passerat hundra namnutrymmen blir det en tråkig uppgift. Det är här Kubernetes-operatörerna kommer till nytta – de låter dig automatisera skapandet av Kubernetes-resurser baserat på ändringar av resurser. I vårt fall vill vi skapa RoleBinding medan du skapar Namespace.
Först av allt, låt oss definiera funktionen mainsom gör den nödvändiga inställningen för att köra satsen och sedan anropar satsåtgärden:
(Notera. transl.: här och nedan är kommentarerna i koden översatta till ryska. Dessutom har indraget korrigerats till mellanslag istället för [rekommenderas i Go]-flikar enbart i syfte att förbättra läsbarheten inom Habr-layouten. Efter varje listning finns det länkar till originalet på GitHub, där engelskspråkiga kommentarer och flikar lagras.)
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() // Ожидаем, что все остановлено
}
(I )
Vi gör följande:
- Vi konfigurerar en hanterare för specifika operativsystemsignaler för att orsaka graciös uppsägning av operatören.
- Vi använder
WaitGroupatt graciöst stoppa alla goroutiner innan du avslutar applikationen. - Vi ger tillgång till klustret genom att skapa
clientset. - Vi lanserar
NamespaceController, där all vår logik kommer att finnas.
Nu behöver vi en grund för logik, och i vårt fall är det den som nämns 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
}
(I )
Här konfigurerar vi SharedIndexInformer, som effektivt (med hjälp av en cache) väntar på ändringar i namnutrymmen (läs mer om informatörer i artikeln ""- cirka. översättning). Efter detta ansluter vi EventHandler till informatören, så att när du lägger till ett namnområde (Namespace) funktionen anropas createRoleBinding.
Nästa steg är att definiera denna 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))
}
}
(I )
Vi får namnutrymmet som obj och konvertera det till ett objekt Namespace. Sedan definierar vi RoleBinding, baserat på YAML-filen som nämndes i början, med hjälp av det angivna objektet Namespace och skapande RoleBinding. Slutligen loggar vi om skapandet lyckades.
Den sista funktionen som ska definieras är 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
}
(I )
Här pratar vi WaitGroupatt vi startar goroutinen och sedan ringer namespaceInformer, som tidigare har definierats. När stoppsignalen kommer kommer den att avsluta funktionen, informera WaitGroup, som inte längre körs, och den här funktionen avslutas.
Information om att bygga och köra detta uttalande på ett Kubernetes-kluster finns i .
Det är det för operatören som skapar RoleBinding när Namespace i Kubernetes-klustret, redo.
Källa: will.com
