Kubernetes Pod IP Allocation: CIDR, Lifecycle, CNI IPAM, Dual‑Stack and IP Reservation
The article explains how Kubernetes allocates Pod IPs—from cluster and node CIDR configuration, through the kubelet‑driven Pod lifecycle and CNI IPAM process, including dual‑stack support, hostNetwork handling, and optional IP reservation/reclamation mechanisms for stable stateful workloads.
01 Overview
Pod is a first‑class citizen in Kubernetes. It hosts the core containers and is the smallest scheduling unit. Understanding Pod initialization—including network, storage and runtime—helps to grasp Kubernetes container orchestration.
The article explains the Pod IP allocation mechanism by covering IP CIDR configuration, Pod lifecycle, kubelet core logic, CNI IPAM, dual‑stack (IPv4/IPv6), and IP reservation/reclamation.
02 K8s IP CIDRs
2.1 Pod IP CIDR
When a cluster is created, the --cluster-cidr flag of kube-controller-manager defines the Pod IP CIDR (e.g., 10.0.0.0/16 ), which can accommodate up to 65 536 Pod IPs.
2.2 Node CIDR
Each Node receives a subnet CIDR from the cluster CIDR via --allocate-node-cidrs=true and --node-cidr-mask-size=24 . By default a Node can allocate 256 Pod IPs (half of which are usually usable).
Dual‑stack : enabling --dual-stack allows separate IPv4 and IPv6 Node CIDRs with --node-cidr-mask-size-ipv4=24 and --node-cidr-mask-size-ipv6=64 .
2.3 Service IP CIDR
Service ClusterIP ranges are controlled by --service-cluster-ip-range (e.g., 10.96.0.0/12 ). Example output of kubectl get svc -n demo :
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
demo-clusterip ClusterIP 10.96.0.46
80/TCP 6d11h
demo-nodeport NodePort 10.96.0.25
80:31666/TCP 6d11h
demo-loadbalancer ClusterIP 10.96.0.250 11.234.50.12 80/TCP 153d
demo-headless ClusterIP None
8080/TCP,8081/TCP 20dClusterIP is reachable only inside the cluster via DNS.
03 Pod Lifecycle
3.1 Pod creation sources
Kubelet creates Pods from three sources: kube-apiserver , staticPodPath , and staticPodURL . The most common is kube-apiserver (e.g., kubectl apply -f pod.yaml ).
3.2 kubelet manages Pod lifecycle
Kubelet watches Pods assigned to its Node using a field selector on spec.nodeName :
// kubernetes/pkg/kubelet/config/apiserver.go
func NewSourceApiserver(c clientset.Interface, nodeName types.NodeName, nodeHasSynced func() bool, updates chan<- interface{}) {
lw := cache.NewListWatchFromClient(c.CoreV1().RESTClient(), "pods", metav1.NamespaceAll, fields.OneTermEqualSelector("spec.nodeName", string(nodeName)))
// ... watch logic ...
}It processes ADD/UPDATE/DELETE events via asynchronous channels (configCh, plegCh, syncCh, housekeepingCh) to drive the Pod through Pending → Running → Terminating.
Key steps performed by kubelet:
ListAndWatch detects Pods scheduled to the Node.
CRI creates a PodSandbox, initializes the network namespace, and invokes CNI to obtain a Pod IP.
CRI creates the first pause container and attaches it to the namespace and IP.
CRI creates initContainers and the main containers.
Probes verify container health; when all succeed, the Pod status becomes Running.
04 Pod IP Allocation Process
4.1 CNI‑IPAM assigns IP
The CRI (e.g., containerd) first creates a PodSandbox, which calls CNI to allocate an IP. Pods share a single IP among all containers, reducing address consumption.
CNI plugins are categorized as Main, Meta, and IPAM. IPAM plugins (dhcp, host‑local, static) manage address allocation.
Reference: https://www.cni.dev/plugins/current/
4.2 IPAM host‑local demo
Example host-local.conf :
{
"cniVersion":"0.3.1",
"name":"examplenet",
"ipam":{
"type":"host-local",
"ranges":[[{"subnet":"10.10.0.0/16","rangeStart":"10.10.1.21","rangeEnd":"10.10.1.28","gateway":"10.10.0.254"}]],
"dataDir":"/tmp/cni-example"
}
}Allocate IPs with CNI ADD command:
# First execution (ns1)
CNI_COMMAND=ADD CNI_CONTAINERID=ns1 CNI_NETNS=/var/run/netns/ns1 CNI_IFNAME=eth0 CNI_PATH=/opt/cni/bin/ /opt/cni/bin/host-local < host-local.conf
{
"cniVersion": "0.3.1",
"ips": [{"version": "4", "address": "10.10.1.21/16", "gateway": "10.10.0.254"}],
"dns": {}
}
# Second execution (ns2)
CNI_COMMAND=ADD CNI_CONTAINERID=ns2 CNI_NETNS=/var/run/netns/ns2 CNI_IFNAME=eth0 CNI_PATH=/opt/cni/bin/ /opt/cni/bin/host-local < host-local.conf
{
"cniVersion": "0.3.1",
"ips": [{"version": "4", "address": "10.10.1.22/16", "gateway": "10.10.0.254"}],
"dns": {}
}Allocated IP files are stored under /tmp/cni-example :
/tmp/cni-example/examplenet]# ll
total 64
-rw-r--r-- 1 root root 7 Jul 8 22:01 10.10.1.21
-rw-r--r-- 1 root root 7 Jul 8 22:02 10.10.1.22
-rw-r--r-- 1 root root 10 Jul 9 14:41 last_reserved_ip.0
-rwxr-x--- 1 root root 0 Jul 8 21:05 lockWhen a Pod is deleted, the DEL command removes the IP file.
CNI_COMMAND=DEL CNI_CONTAINERID=ns1 CNI_NETNS=/var/run/netns/ns1 CNI_IFNAME=eth0 CNI_PATH=/opt/cni/bin/ /opt/cni/bin/host-local < host-local.conf
/tmp/cni-example/examplenet]# ll
total 64
-rw-r--r-- 1 root root 7 Jul 8 22:02 10.10.1.22
-rw-r--r-- 1 root root 10 Jul 9 14:41 last_reserved_ip.0
-rwxr-x--- 1 root root 0 Jul 8 21:05 lock4.3 hostNetwork uses Node IP
If pod.spec.hostNetwork=true , the Pod IP is set to the Node’s IP. kubelet code snippet:
// kubernetes/pkg/kubelet/kubelet_pods.go
if kl.kubeClient != nil {
hostIPs, err := kl.getHostIPsAnyWay()
if err == nil {
s.HostIP = hostIPs[0].String()
if kubecontainer.IsHostNetworkPod(pod) {
if s.PodIP == "" { s.PodIP = hostIPs[0].String(); s.PodIPs = []v1.PodIP{{IP: s.PodIP}} }
if len(hostIPs) == 2 && len(s.PodIPs) == 1 { s.PodIPs = append(s.PodIPs, v1.PodIP{IP: hostIPs[1].String()}) }
}
}
}05 Pod IP Dual‑Stack
Kubernetes v1.20+ supports IPv4/IPv6 dual‑stack. Component flags include:
kube-apiserver --service-cluster-ip-range= ,
kube-controller-manager --cluster-cidr= , --service-cluster-ip-range= , --node-cidr-mask-size-ipv4|--node-cidr-mask-size-ipv6
kube-proxy --cluster-cidr= ,
kubelet --node-ip= , (when no cloud provider)
Users can explore dual‑stack IP allocation and reclamation.
06 Pod IP Reservation & Reclamation
Stateful workloads often need stable Pod IPs. By using a custom ReservedIP CRD together with a CNI plugin, a Pod’s IP can be reserved for a TTL (e.g., 24 h) after deletion, preventing immediate reuse.
Calico provides a built‑in way to request a specific IP via annotations:
apiVersion: v1
kind: Pod
metadata:
name: fixed-pod-ip
annotations:
cni.projectcalico.org/ipAddrs: "[\"192.168.0.1\"]"
spec:
...07 Summary
The complete Pod IP allocation flow in Kubernetes is:
kube‑apiserver receives a request and creates the Pod.
kube‑scheduler selects a suitable Node.
kubelet on that Node watches the Pod and starts creation.
CRI creates a PodSandbox, invokes CNI IPAM (or uses Node IP for hostNetwork) to obtain an IP.
The pause container is created and attached to the network namespace.
InitContainers and main containers are started.
Probes verify health; the Pod status becomes Running.
References: Kubernetes source code, containerd source, CNI plugin list, dual‑stack documentation, Node podCIDR API.
Tencent Cloud Developer
Official Tencent Cloud community account that brings together developers, shares practical tech insights, and fosters an influential tech exchange community.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.