Catatan. terjemahan: Operator adalah perangkat lunak tambahan untuk Kubernetes, yang dirancang untuk mengotomatisasi pelaksanaan tindakan rutin pada objek cluster ketika peristiwa tertentu terjadi. Kami telah menulis tentang operator di , di mana mereka berbicara tentang ide-ide dasar dan prinsip-prinsip pekerjaan mereka. Namun jika materi tersebut lebih merupakan pandangan dari sisi pengoperasian komponen siap pakai untuk Kubernetes, maka terjemahan artikel baru yang diusulkan sekarang sudah merupakan visi seorang pengembang/insinyur DevOps yang bingung dengan implementasi operator baru.

Saya memutuskan untuk menulis postingan ini dengan contoh kehidupan nyata setelah upaya saya menemukan dokumentasi tentang pembuatan operator untuk Kubernetes, melalui pembelajaran kode.
Contoh yang akan dijelaskan adalah ini: di cluster Kubernetes kita, masing-masing Namespace mewakili lingkungan sandbox tim, dan kami ingin membatasi akses ke sana sehingga tim hanya bisa bermain di sandbox mereka sendiri.
Anda dapat mencapai apa yang Anda inginkan dengan menugaskan pengguna ke grup yang dimilikinya RoleBinding untuk spesifik Namespace и ClusterRole dengan hak mengedit. Representasi YAML akan terlihat seperti ini:
---
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(Di )
Buat satu RoleBinding Anda dapat melakukannya secara manual, tetapi setelah melewati tanda seratus namespace, itu menjadi tugas yang membosankan. Di sinilah operator Kubernetes berguna—mereka memungkinkan Anda mengotomatiskan pembuatan sumber daya Kubernetes berdasarkan perubahan pada sumber daya. Dalam kasus kami, kami ingin membuat RoleBinding saat membuat Namespace.
Pertama-tama, mari kita definisikan fungsinya mainyang melakukan pengaturan yang diperlukan untuk menjalankan pernyataan dan kemudian memanggil tindakan pernyataan:
(Catatan. terjemahan: di sini dan di bawah komentar dalam kode diterjemahkan ke dalam bahasa Rusia. Selain itu, lekukan telah diperbaiki menjadi spasi, bukan tab [disarankan di Go] semata-mata untuk tujuan keterbacaan yang lebih baik dalam tata letak Habr. Setelah setiap daftar ada tautan ke aslinya di GitHub, tempat komentar dan tab berbahasa Inggris disimpan.)
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() // Ожидаем, что все остановлено
}(Di )
Kami melakukan hal berikut:
- Kami mengonfigurasi pengendali untuk sinyal sistem operasi tertentu untuk menyebabkan penghentian operator secara baik-baik.
- Gunakan
WaitGroupuntuk menghentikan semua goroutine dengan baik sebelum menghentikan aplikasi. - Kami menyediakan akses ke cluster dengan membuat
clientset. - Meluncurkan
NamespaceController, di mana semua logika kita akan ditempatkan.
Sekarang kita memerlukan dasar logika, dan dalam kasus kita inilah yang disebutkan 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
}(Di )
Di sini kita mengkonfigurasi SharedIndexInformer, yang secara efektif (menggunakan cache) menunggu perubahan pada namespace (baca lebih lanjut tentang informan di artikel “"- kira-kira. terjemahan). Setelah ini kita terhubung EventHandler ke informan, sehingga ketika menambahkan namespace (Namespace) fungsi dipanggil createRoleBinding.
Langkah selanjutnya adalah mendefinisikan fungsi ini 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))
}
}(Di )
Kami mendapatkan namespace sebagai obj dan mengubahnya menjadi sebuah objek Namespace. Lalu kita definisikan RoleBinding, berdasarkan file YAML yang disebutkan di awal, menggunakan objek yang disediakan Namespace dan menciptakan RoleBinding. Terakhir, kami mencatat apakah pembuatannya berhasil.
Fungsi terakhir yang didefinisikan adalah 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
}(Di )
Di sini kita berbicara WaitGroupbahwa kita meluncurkan goroutine dan kemudian memanggil namespaceInformer, yang telah didefinisikan sebelumnya. Ketika sinyal berhenti tiba, fungsinya akan berakhir, informasikan WaitGroup, yang tidak lagi dijalankan, dan fungsi ini akan keluar.
Informasi tentang membuat dan menjalankan pernyataan ini pada cluster Kubernetes dapat ditemukan di .
Itu saja untuk operator yang membuat RoleBinding Kapan Namespace di cluster Kubernetes, siap.
Sumber: www.habr.com
