Lesson  in  BETA: Kubernetes the (Very) Hard Way

kubelet

Release date: TBA

What is kubelet?

kubelet is the primary node agent that runs on every worker node in a Kubernetes cluster. As one of the core components that makes Kubernetes work, it acts as the bridge between the Kubernetes control plane and the container runtime on each node.

kubelet operates in a continuous reconciliation loop:

  1. Watches for Pod specifications from the API server
  2. Compares the desired state (what should be running) with the actual state (what is running)
  3. Takes action to bring the actual state in line with the desired state
  4. Reports the current status back to the control plane

In essence, kubelet is the "hands and feet" of Kubernetes on each node— it takes declarative Pod specifications from the control plane and makes them a reality by managing containers on the worker nodes.

However, kubelet doesn't run containers directly. Instead, it delegates this responsibility to a container runtime like containerd or CRI-O. To support multiple runtimes and provide flexibility in choosing the runtime for your environment, kubelet communicates with these runtimes via the Container Runtime Interface (CRI).

What is the Container Runtime Interface (CRI)?

CRI is a gRPC API that defines a standard interface for kubelet to interact with container runtimes. It enables the kubelet to use a wide variety of container runtimes, without having to recompile it.

Many container runtimes (such as containerd and CRI-O) implement the CRI out of the box, others (like Docker) may need a shim layer to bridge the gap between kubelet and the runtime.

kubelet in a Kubernetes cluster

kubelet in a Kubernetes cluster

Important

🐛 Reporting issues

If you encounter any issues throughout the course, please report them here.

Installing kubelet

Follow these steps to install kubelet:

Set the version to install:

KUBE_VERSION=v1.33.3
Latest version
KUBE_VERSION=$(curl -fsL https://dl.k8s.io/release/stable.txt)
Important

Use the same version for all Kubernetes components throughout this course.

Download and install kubelet from the official Kubernetes releases:

curl -fsSLO https://dl.k8s.io/${KUBE_VERSION?}/bin/linux/amd64/kubelet
sudo install -m 755 kubelet /usr/local/bin

Download the systemd unit file to run kubelet as a systemd service:

sudo wget -O /etc/systemd/system/kubelet.service https://labs.iximiuz.com/content/files/courses/kubernetes-the-very-hard-way-0cbfd997.beta/02-worker-node/02-kubelet/__static__/kubelet.service?v=1756470611

With kubelet installed, you can now configure it.

Note

💡 Kubelet supports merging multiple configuration files, allowing you to separate different configuration concerns into separate files.

For now, the only configuration needed is specifying how kubelet should communicate with the container runtime:

/var/lib/kubelet/config.d/99-cri.conf
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration

containerRuntimeEndpoint: unix:///var/run/containerd/containerd.sock
cgroupDriver: systemd
Hint
sudo mkdir -p /var/lib/kubelet/config.d
sudoedit /var/lib/kubelet/config.d/99-cri.conf

Start the kubelet service:

sudo systemctl daemon-reload
sudo systemctl enable --now kubelet

Congratulations! You have successfully installed kubelet. 🎉

Authentication and Authorization

Important

⚠️ The kubelet configuration in this section disables security features for learning purposes.

kubelet exposes an HTTP API endpoint (typically on port 10250) that allows the Kubernetes API server and other components to interact with it.

This endpoint provides access to:

  • Pod logs and exec sessions
  • Node metrics and health information

In production environments, this endpoint must be secured through proper authentication and authorization mechanisms.

kubelet authentication and authorization

kubelet authentication and authorization

To simplify the setup for this lesson, you'll disable kubelet authentication and authorization:

/var/lib/kubelet/config.d/70-authnz.conf
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration

authentication:
    anonymous:
        enabled: true
    webhook:
        enabled: false

authorization:
    mode: AlwaysAllow
Hint
sudoedit /var/lib/kubelet/config.d/70-authnz.conf

Restart the kubelet service to apply the configuration changes:

sudo systemctl restart kubelet

Verify that the kubelet API endpoint is accessible:

curl -k https://localhost:10250/healthz

Static Pods

Static pods are pods managed directly by kubelet on a specific node rather than by the Kubernetes API server.

Note

💡 Static pods are useful for running cluster components and node-level services.

Unlike regular pods created and managed through the cluster's control plane, static pods are defined by placing pod manifest files in a directory that kubelet monitors.

When kubelet finds a pod manifest in the static pod directory, it automatically creates and manages that pod. If the pod crashes or stops, kubelet automatically restarts it.

Important

⚠️ Static pods cannot reference other Kubernetes API objects like Secrets, ConfigMaps, or ServiceAccounts.

They can only use resources directly available on the node, such as hostPath or emptyDir volumes.

kubelet also creates a mirror pod in the Kubernetes API server for each static pod. This mirror pod allows you to see the static pod when you run kubectl get pods, but you cannot control the static pod through the API server—only kubelet can manage it directly.

Static pod functionality is disabled by default. To enable it, you need to configure kubelet.

Create the static pod directory:

sudo mkdir -p /etc/kubernetes/manifests

Configure kubelet to monitor the static pod directory by creating a configuration file in the kubelet drop-in directory:

/var/lib/kubelet/config.d/50-static-pods.conf
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration

staticPodPath: /etc/kubernetes/manifests
Hint
sudoedit /var/lib/kubelet/config.d/50-static-pods.conf

Restart the kubelet service to apply the configuration changes:

sudo systemctl restart kubelet

Create your first static pod by deploying a simple application called podinfo to verify everything works correctly.

Create the pod manifest:

/etc/kubernetes/manifests/podinfo.yaml
apiVersion: v1
kind: Pod
metadata:
    name: podinfo
spec:
    hostNetwork: true
    containers:
        - name: podinfo
          image: ghcr.io/stefanprodan/podinfo:latest
          ports:
              - containerPort: 9898
Hint
sudoedit /etc/kubernetes/manifests/podinfo.yaml
Note

💡 Notice hostNetwork: true in the pod specification.

This means the pod uses the host's network namespace instead of creating its own. This configuration is common for static pods that need to be accessible directly from the host.

Verify that podinfo is running by checking if it appears in the list of pods:

curl -sfk https://localhost:10250/pods | jq
Note

💡 Notice the pod name is podinfo-worker.

Static pods automatically have the node name appended to their name to ensure uniqueness across the cluster.

Verify that the podinfo application is working correctly:

curl http://localhost:9898

Even though the pod is running, it won’t show up when you list containers directly:

nerdctl
ctr
sudo nerdctl ps
sudo ctr container ls

This happens because containerd organizes containers into namespaces (similar to Kubernetes), and Kubernetes uses the k8s.io namespace by default.

You can verify this by running:

sudo ctr namespace ls

To see containers running in the k8s.io namespace, use the --namespace flag:

nerdctl
ctr
sudo nerdctl ps --namespace k8s.io
sudo ctr --namespace k8s.io container ls

Installing kubeletctl

kubeletctl is a command-line tool that provides direct access to the kubelet API.

It is useful for debugging and inspecting kubelet instances, querying pod information, retrieving logs, and exploring the kubelet's internal state without requiring a full Kubernetes cluster setup.

Follow these steps to install kubeletctl:

Set the version to install:

KUBELETCTL_VERSION=v1.13
Latest version
KUBELETCTL_VERSION=$(curl -s https://api.github.com/repos/cyberark/kubeletctl/releases/latest | jq -r .tag_name)

Download and install kubeletctl from GitHub:

curl -fsSLO https://github.com/cyberark/kubeletctl/releases/download/${KUBELETCTL_VERSION?}/kubeletctl_linux_amd64
sudo install -m 755 kubeletctl_linux_amd64 /usr/local/bin/kubeletctl

With kubeletctl installed, you can perform various operations:

Check the merged configuration:

kubeletctl configz

List all pods:

kubeletctl pods

Return container logs:

kubeletctl containerLogs -p podinfo-worker -c podinfo

Execute a command in a container:

kubeletctl exec "ls /" -p podinfo-worker -c podinfo

Summary

In this lesson, you learned about kubelet, the primary node agent that runs on every worker node in a Kubernetes cluster.

Key takeaways:

  • Control plane bridge: kubelet acts as the essential link between the Kubernetes control plane and the container runtime, translating API server instructions into container operations
  • Reconciliation loop: Continuously watches for pod specifications, compares desired vs actual state, and takes corrective actions to maintain cluster consistency
  • CRI delegation: Doesn't run containers directly but communicates with container runtimes like containerd through the Container Runtime Interface (CRI)
  • Static pods: Enables running critical cluster components directly on nodes before the control plane becomes available, with automatic restart capabilities
  • API endpoint: Provides an HTTPS interface on port 10250 for log access, metrics, and container execution—essential for cluster operations

With kubelet now running and managing static pods, you have established the node-level foundation for Kubernetes workload management.

References

Note

💡 To dive deeper into the concepts covered in this lesson, check out the resources below.

Previous lesson
containerd
Next lesson
etcd