How Kubernetes Schedules and Runs a Pod – Complete Workflow
When you create a Pod in Kubernetes, it passes through several components before it starts running on a worker node.
This article explains the complete lifecycle of a Pod from the moment you execute kubectl apply until the container is running.
Step 1: User Creates a Pod
Run the following command:
kubectl apply -f nginx.yaml
kubectl reads the YAML file and sends a REST API request to the kube-apiserver.
kubectl
|
| HTTP REST API
v
kube-apiserver
Step 2: API Server Validates the Request
The API Server performs several checks before accepting the request:
- Authentication
- Authorization (RBAC)
- Admission Controllers
- Schema Validation
If everything is valid, the API Server stores the Pod object inside etcd.
kubectl
|
v
kube-apiserver
|
| Store Pod
v
etcd
At this stage, the Pod exists only as an object inside etcd.
It is not running on any node yet.
Example Pod object:
Pod:
name: nginx
nodeName: ""
status: Pending
Notice that:
nodeName = empty
No worker node has been selected yet.
Step 3: kube-scheduler Notices a Pending Pod
The kube-scheduler continuously watches the API Server.
It does not communicate directly with etcd.
Conceptually:
Scheduler
Watch all Pods
IF nodeName == empty
schedule it
Communication flow:
Scheduler
|
Watch API Server
|
API Server
Step 4: Scheduler Reads the Pod Information
The scheduler retrieves the Pod details through the API Server, including:
- Pod requirements
- CPU requests
- Memory requests
- Affinity rules
- Taints
- Tolerations
- Node labels
- Volume requirements
The API Server fetches this information from etcd.
Scheduler
|
| Watch Pod
v
API Server
|
| Read
v
etcd
Step 5: Scheduler Selects the Best Worker Node
Suppose the cluster contains three worker nodes:
Worker-1
Worker-2
Worker-3
The scheduler evaluates each node.
Example:
Worker1
CPU: 90%
Worker2
CPU: 10%
Worker3
CPU: 50%
The scheduler selects:
Worker2
Step 6: Scheduler Informs the API Server
Important:
The scheduler does not write directly to etcd.
Instead, it tells the API Server:
Assign Pod nginx
to Worker2
The API Server updates the Pod object inside etcd.
Before scheduling:
nodeName=""
After scheduling:
nodeName="worker2"
Flow:
Scheduler
|
v
API Server
|
v
etcd
Step 7: kubelet Watches the API Server
Every worker node runs a kubelet.
Worker1
kubelet
Worker2
kubelet
Worker3
kubelet
Each kubelet continuously watches the API Server.
It periodically checks:
Do you have any Pods assigned to me?
The kubelet communicates only with the API Server.
It never communicates directly with:
- etcd
- kube-scheduler
kubelet
|
Watch
|
API Server
Step 8: Worker2 Receives the Pod
The kubelet running on Worker2 discovers:
Pod nginx
Assigned to Worker2
It then downloads the complete Pod specification.
containers
volumes
environment variables
ports
container image
The API Server retrieves these details from etcd.
kubelet
|
v
API Server
|
v
etcd
Step 9: kubelet Communicates with the Container Runtime
The kubelet does not create containers itself.
Instead, it communicates with the container runtime.
Examples:
- containerd
- CRI-O
- Docker (older Kubernetes versions)
This communication happens through the Container Runtime Interface (CRI).
kubelet
|
| CRI
v
containerd
Step 10: containerd Creates the Pod
containerd performs several operations:
- Pulls the container image
docker.io/nginx
- Creates the Pod Sandbox
- Creates the Network Namespace
- Prepares the Container Filesystem
Finally, containerd invokes:
runc
runc interacts with the Linux kernel to create the actual Linux containers.
Flow:
kubelet
|
containerd
|
runc
|
Linux Kernel
Step 11: Pod Starts Running
Once the container starts, the kubelet reports the Pod status back to the API Server.
Status progression:
Pending
↓
ContainerCreating
↓
Running
Flow:
kubelet
|
v
API Server
|
v
etcd
Step 12: kube-proxy Configures Networking
One important clarification:
kube-proxy does not create networking rules simply because a Pod is created.
Instead, kube-proxy watches:
- Services
- Endpoints / EndpointSlices
When a Service points to one or more Pods, kube-proxy creates networking rules using:
- iptables
- IPVS
- nftables (depending on the configuration)
Example:
Service
10.96.0.5
↓
Pod1
Pod2
Pod3
Requests sent to 10.96.0.5 are automatically load-balanced across the available Pods.
Flow:
API Server
|
Watch Services / EndpointSlices
|
kube-proxy
|
iptables / IPVS / nftables rules
Complete Kubernetes Pod Scheduling Flow
kubectl
|
|
v
kube-apiserver
|
| Store Pod
v
etcd
^
|
(Only API Server talks to etcd)
|
|
kube-scheduler
Watches API Server
|
| Select Worker2
v
kube-apiserver
|
| Update nodeName
v
etcd
^
|
|
Worker2 kubelet watches API Server
|
| Get Pod Specification
v
kube-apiserver
|
v
etcd
|
|
v
kubelet
|
| CRI
v
containerd
|
v
runc
|
v
Linux Kernel
|
v
Pod Running
kube-proxy
|
| Watches Services and EndpointSlices
| through the API Server
v
Creates iptables / IPVS / nftables rules
Key Takeaways
- ✔ kubectl → kube-apiserver → etcd is the correct initial flow.
- ✔ The kube-scheduler watches the API Server for Pods whose
nodeNameis empty. - ✔ The scheduler selects the most suitable worker node based on available resources and scheduling constraints.
- ✔ The scheduler informs the API Server, which updates the Pod object in etcd.
- ✔ Every kubelet watches the API Server. When it detects a Pod assigned to its node, it retrieves the Pod specification.
- ✔ The kubelet asks the container runtime (such as containerd) to create the Pod using the Container Runtime Interface (CRI).
- ✔ containerd uses runc to create the Linux containers.
- ✔ kube-proxy watches Services and EndpointSlices and configures networking rules using iptables, IPVS, or nftables.
- ✔ The kube-apiserver is the central communication hub. Every Kubernetes core component communicates with the API Server, and only the API Server reads from and writes to etcd.
























Add Comment