Menulis operator untuk Kubernetes di Golang

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 Artikel ini, 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.

Menulis operator untuk Kubernetes di Golang

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

(pengikatan peran.yamlDi mentah)

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()   // Ожидаем, что все остановлено
}

(utama.pergiDi mentah)

Kami melakukan hal berikut:

  1. Kami mengonfigurasi pengendali untuk sinyal sistem operasi tertentu untuk menyebabkan penghentian operator secara baik-baik.
  2. Gunakan WaitGroupuntuk menghentikan semua goroutine dengan baik sebelum menghentikan aplikasi.
  3. Kami menyediakan akses ke cluster dengan membuat clientset.
  4. 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
}

(pengontrol.pergiDi mentah)

Di sini kita mengkonfigurasi SharedIndexInformer, yang secara efektif (menggunakan cache) menunggu perubahan pada namespace (baca lebih lanjut tentang informan di artikel “Bagaimana sebenarnya penjadwal Kubernetes bekerja?"- 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))
  }
}

(pengontrol.pergiDi mentah)

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
}

(pengontrol.pergiDi mentah)

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 repositori di GitHub.

Itu saja untuk operator yang membuat RoleBinding Kapan Namespace di cluster Kubernetes, siap.

Sumber: www.habr.com

Beli hosting yang andal untuk situs dengan perlindungan DDoS, server VPS VDS 🔥 Beli hosting website andal dengan perlindungan DDoS, server VPS VDS | ProHoster