Cloud Native 21 min read

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.

Tencent Cloud Developer
Tencent Cloud Developer
Tencent Cloud Developer
Kubernetes Pod IP Allocation: CIDR, Lifecycle, CNI IPAM, Dual‑Stack and IP Reservation

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 20d

ClusterIP 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 lock

When 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 lock

4.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.

kubernetesCNIDual-StackIPAMkubeletPod IP
Tencent Cloud Developer
Written by

Tencent Cloud Developer

Official Tencent Cloud community account that brings together developers, shares practical tech insights, and fosters an influential tech exchange community.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.