Thành phần Informer trong client-go

Các thành phần Kubernetes cần giám sát và truy vấn đối tượng tài nguyên trong cụm. Ví dụ bộ điều khiển Deployment phải theo dõi trạng thái của Deployment và ReplicaSet, đồng bộ hóa liên tục để đảm bảo ReplicaSet khớp với cấu hình người dùng. Việc truy vấn thường xuyên này tạo gánh nặng cho API Server và ETCD.

Giao tiếp giữa các thành phần với API Server sử dụng HTTP mà không qua middleware bên ngoài, đòi hỏi đảm bảo độ tin cậy, thứ tự và tính thời gian thực.

Thành phần Informer trong client-go giải quyết các vấn đề này. Đây là thành phần quan trọng khi phát triển bộ điều khiển cho CRD tùy chỉnh.

Cơ chế lõi của Informer là List/Watch. Khi kết nối API Server, Informer đầu tiên lấy toàn bộ đối tượng tài nguyên (chế độ List) và lưu vào bộ nhớ cục bộ, sau đó giám sát thay đổi (chế độ Watch). Khi tài nguyên thay đổi, Informer cập nhật bộ đệm để duy trì sự nhất quán với ETCD. Ưu điểm chính:

  1. Truy vấn tài nguyên thực hiện qua bộ đệm cục bộ, tốc độ cao và giảm tải cho API Server
  2. Hỗ trợ đăng ký sự kiện qua ShareInformer.AddEventHandler, cần thiết cho bộ điều khiển tài nguyên tích hợp sẵn và CRD

Cách triển khai tối ưu: Sử dụng Clientset cho thao tác tạo/xóa/sửa tài nguyên và Informer cho truy vấn.

Kiến trúc Informer:

  1. Reflector khởi động List/Watch, lấy tài nguyên từ API Server vào hàng đợi DeltaFIFO
  2. processLoop xử lý dữ liệu từ DeltaFIFO bằng hàm callback HandlerDetal
  3. HandlerDetal lưu thao tác Add/Update/Delete vào Indexer (bao bọc ThreadSafeMap) để hỗ trợ truy vấn hiệu quả
  4. Dữ liệu được phân phối tới ShareInformer để kích hoạt sự kiện qua AddEventHandler
  5. Bộ điều khiển đưa sự kiện vào workqueue
  6. Bộ điều khiển xử lý logic chính từ workqueue, đồng bộ trạng thái tài nguyên. Lỗi xử lý sẽ đưa lại vào hàng đợi

Lưu ý: Controller trong bước 1-2 (k8s.io/client-go/tools/cache/controller.go) khác với bộ điều khiển tài nguyên trong bước 5-6.

Ví dụ triển khai Informer:

Hàm tạo Clientset:

func TaoClientset(duongdan string) (*kubernetes.Clientset, *rest.Config) {
    cauHinh, loi := clientcmd.BuildConfigFromFlags("", duongdan)
    if loi != nil {
        panic(loi)
    }
    clientset, loi := kubernetes.NewForConfig(cauHinh)
    if loi != nil {
        panic(loi)
    }
    return clientset, cauHinh
}

Cấu trúc bộ điều khiển:

type BoDieuKhien struct {
    cauHinh    *rest.Config
    clientset  *kubernetes.Clientset
    factory    informers.SharedInformerFactory
    podInformer coreinformers.PodInformer
    podLister  corelisters.PodLister
    daDongBo   cache.InformerSynced
}

Hàm khởi tạo:

func TaoBoDieuKhien(cauHinh *rest.Config, clientset *kubernetes.Clientset, tgDongBo time.Duration) *BoDieuKhien {
    bdk := &BoDieuKhien{cauHinh: cauHinh, clientset: clientset}
    bdk.factory = informers.NewSharedInformerFactory(clientset, tgDongBo)
    bdk.podInformer = bdk.factory.Core().V1().Pods()
    bdk.podLister = bdk.podInformer.Lister()
    bdk.daDongBo = bdk.podInformer.Informer().HasSynced
    return bdk
}

Khởi động Informer:

func (bdk *BoDieuKhien) KhoiDong(ngungChan chan struct{}) {
    bdk.factory.Start(ngungChan)
    if !cache.WaitForCacheSync(ngungChan, bdk.daDongBo) {
        panic("Lỗi đồng bộ bộ đệm")
    }
    <-ngungChan
}

Sử dụng trong main:

func main() {
    clientset, cauHinh := TaoClientset("cau_hinh/config")
    bdk := TaoBoDieuKhien(cauHinh, clientset, time.Minute*50)
    ngungChan := make(chan struct{})
    go bdk.KhoiDong(ngungChan)
    
    pod, _ := bdk.podLister.Pods("ten-namespace").Get("ten-pod")
    fmt.Printf("Node: %s", pod.Spec.NodeName)
}

Lưu ý: SharedInformerFactory dùng chung Reflector cho cùng loại tài nguyên, tránh gọi List/Watch trùng lặp. Cấu trúc lưu trữ:

type sharedInformerFactory struct {
    client        kubernetes.Interface
    dongBoMacDinh time.Duration
    informers     map[reflect.Type]cache.SharedIndexInformer
}

Thẻ: Kubernetes client-go Informer Controller CRD

Đăng vào ngày 9 tháng 7 lúc 15:14