Ghi chú. bản dịch.: Toán tử là phần mềm phụ trợ cho Kubernetes, được thiết kế để tự động hóa việc thực thi các hành động thông thường trên các đối tượng cụm khi một số sự kiện nhất định xảy ra. Chúng tôi đã viết về các toán tử trong
Tôi quyết định viết bài đăng này với một ví dụ thực tế sau khi cố gắng tìm tài liệu về cách tạo toán tử cho Kubernetes, trải qua quá trình nghiên cứu mã.
Ví dụ sẽ được mô tả như sau: trong cụm Kubernetes của chúng tôi, mỗi Namespace
đại diện cho môi trường hộp cát của một đội và chúng tôi muốn hạn chế quyền truy cập vào chúng để các đội chỉ có thể chơi trong hộp cát của riêng họ.
Bạn có thể đạt được điều mình muốn bằng cách chỉ định cho người dùng một nhóm có RoleBinding
cụ thể Namespace
и ClusterRole
với quyền chỉnh sửa. Biểu diễn YAML sẽ trông như thế này:
---
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
(
Tạo một cái RoleBinding
Bạn có thể thực hiện thủ công, nhưng sau khi vượt qua hàng trăm không gian tên, nó sẽ trở thành một công việc tẻ nhạt. Đây là lúc các toán tử Kubernetes trở nên hữu ích—chúng cho phép bạn tự động hóa việc tạo tài nguyên Kubernetes dựa trên những thay đổi đối với tài nguyên. Trong trường hợp của chúng tôi, chúng tôi muốn tạo RoleBinding
trong khi tạo Namespace
.
Trước hết hãy xác định hàm main
thực hiện thiết lập cần thiết để chạy câu lệnh và sau đó gọi hành động câu lệnh:
(Ghi chú. bản dịch.: ở đây và bên dưới các nhận xét trong mã được dịch sang tiếng Nga. Ngoài ra, phần thụt lề đã được sửa thành khoảng trắng thay vì tab [được khuyến nghị trong Go] chỉ nhằm mục đích dễ đọc hơn trong bố cục Habr. Sau mỗi danh sách sẽ có các liên kết đến bản gốc trên GitHub, nơi lưu trữ các tab và nhận xét bằng tiếng Anh.)
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() // Ожидаем, что все остановлено
}
Chúng tôi làm như sau:
- Chúng tôi định cấu hình trình xử lý cho các tín hiệu hệ điều hành cụ thể để khiến toán tử chấm dứt một cách nhẹ nhàng.
- Chúng tôi sử dụng
WaitGroup
để dừng tất cả goroutine một cách duyên dáng trước khi chấm dứt ứng dụng. - Chúng tôi cung cấp quyền truy cập vào cụm bằng cách tạo
clientset
. - Phóng
NamespaceController
, trong đó tất cả logic của chúng tôi sẽ được đặt.
Bây giờ chúng ta cần một cơ sở cho logic, và trong trường hợp của chúng ta đây là cơ sở được đề cập 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
}
(
Ở đây chúng tôi cấu hình SharedIndexInformer
, điều này sẽ (sử dụng bộ đệm) chờ đợi những thay đổi trong không gian tên một cách hiệu quả (đọc thêm về người cung cấp thông tin trong bài viết “EventHandler
cho người cung cấp thông tin, để khi thêm một không gian tên (Namespace
) hàm được gọi createRoleBinding
.
Bước tiếp theo là xác định hàm này 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))
}
}
(
Chúng tôi nhận được không gian tên như obj
và chuyển đổi nó thành một đối tượng Namespace
. Sau đó chúng tôi xác định RoleBinding
, dựa trên tệp YAML được đề cập ở phần đầu, sử dụng đối tượng được cung cấp Namespace
và tạo ra RoleBinding
. Cuối cùng, chúng tôi ghi lại xem quá trình tạo có thành công hay không.
Hàm cuối cùng được xác định là 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
}
(
Ở đây chúng ta đang nói chuyện WaitGroup
rằng chúng tôi khởi chạy goroutine và sau đó gọi namespaceInformer
, đã được xác định trước đó. Khi có tín hiệu dừng đến sẽ kết thúc chức năng, thông báo WaitGroup
, không còn được thực thi nữa và hàm này sẽ thoát.
Thông tin về việc xây dựng và chạy câu lệnh này trên cụm Kubernetes có thể được tìm thấy trong
Đó là việc của người vận hành tạo ra RoleBinding
khi nào Namespace
trong cụm Kubernetes đã sẵn sàng.
Nguồn: www.habr.com