Phân tích mã nguồn Kubelet: Quy trình khởi tạo và đồng bộ Pod

Kubelet đóng vai trò là một "Node Agent" quan trọng trong hệ sinh thái Kubernetes, chịu trách nhiệm quản lý trực tiếp các tài nguyên trên từng node. Nhiệm vụ chính của nó bao gồm việc thực thi vòng đời của Pod, từ khâu khởi tạo, cập nhật trạng thái cho đến khi xóa bỏ và dọn dẹp tài nguyên.

Khởi tạo và cấu hình Kubelet

Kubelet sử dụng framework Cobra để quản lý các dòng lệnh. Quy trình khởi tạo bắt đầu bằng việc phân tích các tham số (flags) và thiết lập các thành phần phụ thuộc (dependencies). Dưới đây là cấu trúc minh họa cách cấu hình môi trường thực thi cho Kubelet:

// Cấu hình mẫu để debug Kubelet
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug-Kubelet",
            "type": "go",
            "request": "launch",
            "mode": "auto",
            "program": "${fileDirname}",
            "args": [
                "--container-runtime-endpoint=unix:///run/containerd/containerd.sock",
                "--v=5",
                "--kubeconfig=/etc/kubernetes/kubelet.conf",
                "--port=10250"
            ]
        }
    ]
}

Trong mã nguồn, hàm NewKubeletCommand khởi tạo lệnh chạy chính. Sau khi các tham số được nạp, hàm Run sẽ được gọi để bắt đầu tiến trình:

// kubernetes/cmd/kubelet/app/server.go
func NewKubeletCommand() *cobra.Command {
    kletFlags := options.NewKubeletFlags()
    kletConfig, _ := options.NewKubeletConfiguration()

    cmd := &cobra.Command{
        RunE: func(cmd *cobra.Command, args []string) error {
            // Thiết lập các đối tượng thực thi
            server := &options.KubeletServer{
                KubeletFlags:         *kletFlags,
                KubeletConfiguration: *kletConfig,
            }

            // Khởi tạo các module phụ thuộc như VolumeManager, NetworkPlugin
            deps, err := UnsecuredDependencies(server, utilfeature.DefaultFeatureGate)
            if err != nil {
                return err
            }

            return Run(context.TODO(), server, deps, utilfeature.DefaultFeatureGate)
        },
    }
    return cmd
}

Vòng lặp đồng bộ chính (Sync Loop)

Sau khi khởi động, Kubelet đi vào một vòng lặp vô hạn gọi là syncLoop. Đây là trái tim của Kubelet, nơi nó liên tục theo dõi các thay đổi từ API Server, các tệp cấu hình cục bộ hoặc các HTTP endpoint.

// kubernetes/pkg/kubelet/kubelet.go
func (kl *Kubelet) syncLoop(ctx context.Context, updates <-chan kubetypes.PodUpdate, handler SyncHandler) {
    // Ticker để thực hiện đồng bộ định kỳ
    periodicSync := time.NewTicker(time.Second)
    defer periodicSync.Stop()

    for {
        // Theo dõi các sự kiện thay đổi Pod
        if !kl.syncLoopIteration(ctx, updates, handler, periodicSync.C, nil, nil) {
            break
        }
    }
}

func (kl *Kubelet) syncLoopIteration(ctx context.Context, configCh <-chan kubetypes.PodUpdate, handler SyncHandler, 
    syncCh <-chan time.Time, housekeepingCh <-chan time.Time, plegCh <-chan *pleg.PodLifecycleEvent) bool {
    
    select {
    case u, open := <-configCh:
        if !open {
            return false
        }
        switch u.Op {
        case kubetypes.ADD:
            // Xử lý khi có Pod mới được tạo
            handler.HandlePodAdditions(u.Pods)
        case kubetypes.UPDATE:
            handler.HandlePodUpdates(u.Pods)
        }
    }
    return true
}

Xử lý Pod thông qua PodWorkers

Khi nhận được lệnh ADD, Kubelet không trực tiếp tạo container ngay lập tức. Thay vào đó, nó cập nhật PodManager để lưu trữ thông tin và chuyển tiếp công việc cho PodWorkers. Mỗi Pod sẽ được quản lý bởi một goroutine riêng biệt để đảm bảo tính bất đồng bộ.

func (kl *Kubelet) HandlePodAdditions(pods []*v1.Pod) {
    for _, p := range pods {
        // Đăng ký Pod vào PodManager
        kl.podManager.AddPod(p)

        // Kiểm tra xem Pod có được phép chạy trên Node này không (Admission Control)
        if ok, reason, msg := kl.canAdmitPod(kl.podManager.GetPods(), p); !ok {
            kl.rejectPod(p, reason, msg)
            continue
        }

        // Gửi yêu cầu cập nhật tới PodWorker
        kl.podWorkers.UpdatePod(UpdatePodOptions{
            Pod:        p,
            UpdateType: kubetypes.SyncPodCreate,
        })
    }
}

Trong podWorkerLoop, Kubelet sẽ gọi hàm SyncPod để thực hiện các bước triển khai thực tế:

  1. Cập nhật trạng thái Pod lên API Server thông qua StatusManager.
  2. Tạo thư mục lưu trữ dữ liệu cho Pod trên Node.
  3. Gắn (Mount) các Volume cần thiết.
  4. Lấy các thông tin bảo mật (Secrets, ConfigMaps).
  5. Yêu cầu Container Runtime (CRI) khởi tạo Sandbox và Container.

Tương tác với Container Runtime Interface (CRI)

Kubelet giao tiếp với các runtime như containerd hoặc CRI-O thông qua gRPC. Hàm SyncPod bên trong module kuberuntime sẽ tính toán sự khác biệt giữa trạng thái mong muốn và trạng thái hiện tại.

// kubernetes/pkg/kubelet/kuberuntime/kuberuntime_manager.go
func (m *kubeGenericRuntimeManager) SyncPod(ctx context.Context, pod *v1.Pod, podStatus *kubecontainer.PodStatus, ...) {
    // 1. Tính toán các hành động cần thiết (tạo mới, xóa, cập nhật)
    actions := m.computePodActions(ctx, pod, podStatus)

    // 2. Khởi tạo Sandbox (Pod Infra Container) nếu cần
    if actions.CreateSandbox {
        sandboxID, _, _ = m.createPodSandbox(ctx, pod, actions.Attempt)
    }

    // 3. Khởi chạy Init Containers
    for _, idx := range actions.InitContainersToStart {
        m.startContainer(ctx, sandboxID, ..., &pod.Spec.InitContainers[idx], ...)
    }

    // 4. Khởi chạy các App Containers chính
    for _, idx := range actions.ContainersToStart {
        m.startContainer(ctx, sandboxID, ..., &pod.Spec.Containers[idx], ...)
    }
}

Cuối cùng, việc tạo Sandbox thực tế được thực hiện qua một lời gọi gRPC tới CRI Service:

func (r *remoteRuntimeService) RunPodSandbox(ctx context.Context, config *runtimeapi.PodSandboxConfig, runtimeHandler string) (string, error) {
    // Gửi yêu cầu qua gRPC tới CRI (ví dụ: containerd)
    resp, err := r.runtimeClient.RunPodSandbox(ctx, &runtimeapi.RunPodSandboxRequest{
        Config:         config,
        RuntimeHandler: runtimeHandler,
    })
    if err != nil {
        return "", err
    }
    return resp.PodSandboxId, nil
}

Quy trình này đảm bảo rằng Kubelet duy trì sự tách biệt giữa logic quản lý của Kubernetes và cơ chế thực thi container lớp dưới, cho phép hệ thống hoạt động linh hoạt với nhiều loại runtime khác nhau.

Thẻ: Kubernetes kubelet cri golang container-runtime

Đăng vào ngày 22 tháng 7 lúc 15:53