Забелешка. превод.: Операторите се помошен софтвер за Kubernetes, дизајниран да го автоматизира извршувањето на рутинските дејства на објектите на кластерот кога се случуваат одредени настани. Веќе пишувавме за операторите во
Решив да го напишам овој пост со пример од реалниот живот по моите обиди да најдам документација за создавање оператор за Kubernetes, што помина низ проучување на кодот.
Примерот што ќе биде опишан е овој: во нашиот кластер Kubernetes, секој Namespace
ја претставува околината на песокот на тимот и сакавме да го ограничиме пристапот до нив, така што тимовите ќе можат да играат само во нивните сопствени песок.
Можете да го постигнете она што го сакате со доделување на корисник група која има RoleBinding
до конкретни Namespace
и ClusterRole
со права на уредување. Претставувањето на YAML ќе изгледа вака:
---
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
(
Направете еден RoleBinding
Можете да го направите тоа рачно, но откако ќе ја преминете ознаката за стотина имиња, станува досадна задача. Ова е местото каде што операторите на Kubernetes доаѓаат во рака - тие ви дозволуваат да го автоматизирате создавањето на ресурси на Kubernetes врз основа на промени во ресурсите. Во нашиот случај сакаме да создадеме RoleBinding
додека создавате Namespace
.
Прво, да ја дефинираме функцијата main
кој го прави потребното поставување за да ја изврши изјавата, а потоа го нарекува дејство на изјавата:
(Забелешка. превод.: овде и подолу коментарите во кодот се преведени на руски. Дополнително, вовлекувањето е поправено на празни места наместо табовите [препорачано во Go] исклучиво со цел подобра читливост во распоредот на Habr. По секоја листа, има линкови до оригиналот на GitHub, каде што се чуваат коментарите и картичките на англиски јазик.)
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() // Ожидаем, что все остановлено
}
(
Ние го правиме следново:
- Конфигурираме управувач за специфични сигнали на оперативниот систем за да предизвикаме грациозно прекинување на операторот.
- Ние користиме
WaitGroup
благодатно да ги прекинете сите горутини пред да ја прекинете апликацијата. - Обезбедуваме пристап до кластерот со креирање
clientset
. - Лансира
NamespaceController
, во кој ќе биде сместена сета наша логика.
Сега ни треба основа за логика, а во нашиот случај ова е споменатата 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
}
Тука конфигурираме SharedIndexInformer
, кој ефективно (со користење на кеш) ќе чека промени во именските простори (прочитајте повеќе за информаторите во статијата“EventHandler
на информаторот, така што при додавање именски простор (Namespace
) се повикува функцијата createRoleBinding
.
Следниот чекор е да се дефинира оваа функција 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))
}
}
Именскиот простор го добиваме како obj
и претворете го во објект Namespace
. Потоа дефинираме RoleBinding
, врз основа на датотеката YAML спомната на почетокот, користејќи го дадениот објект Namespace
и создавање RoleBinding
. Конечно, евидентираме дали создавањето е успешно.
Последната функција што треба да се дефинира е 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
}
Тука зборуваме WaitGroup
дека го лансираме горутинот и потоа се јавуваме namespaceInformer
, што е претходно дефинирано. Кога ќе пристигне сигналот за стоп, ќе ја заврши функцијата, информирајте WaitGroup
, што повеќе не се извршува и оваа функција ќе излезе.
Информации за градење и извршување на оваа изјава на кластерот Kubernetes може да се најдат во
Тоа е тоа за операторот што создава RoleBinding
кога Namespace
во кластерот Kubernetes, подготвен.
Извор: www.habr.com