บันทึก. แปล: ตัวดำเนินการเป็นซอฟต์แวร์เสริมสำหรับ 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
ที่เราเปิด goroutine แล้วจึงเรียก namespaceInformer
ซึ่งได้รับการกำหนดไว้ก่อนหน้านี้ เมื่อสัญญาณหยุดมาถึงจะสิ้นสุดฟังก์ชันแจ้ง WaitGroup
ซึ่งไม่ได้ดำเนินการอีกต่อไป และฟังก์ชันนี้จะออกจากการทำงาน
ข้อมูลเกี่ยวกับการสร้างและเรียกใช้คำสั่งนี้บนคลัสเตอร์ Kubernetes มีอยู่ใน
เพียงเท่านี้สำหรับผู้ปฏิบัติงานที่สร้าง RoleBinding
เมื่อไร Namespace
ในคลัสเตอร์ Kubernetes พร้อมแล้ว
ที่มา: will.com