DoiT Cloud Intelligence™

Scaling Kubernetes: How to Seamlessly Expand Service IP Ranges

By Chimbu ChinnaduraiNov 18, 20245 min read
Scaling Kubernetes: How to Seamlessly Expand Service IP Ranges

When managing Kubernetes clusters, ensuring sufficient IP addresses for Kubernetes Services can become a critical aspect of scaling and maintaining your infrastructure.

Services provide an abstract way to expose an application running on a set of Pods. Different types of Services, like ClusterIP, NodePort, and LoadBalancer, utilize a unique cluster-scoped virtual IP address called ClusterIP.

Each Service cluster IP address within the entire cluster must be unique. Attempting to create a Service with a ClusterIP that has already been allocated will result in an error. As your deployment expands, the default Service IP range may become inadequate, potentially leading to bottlenecks in network resources.

Previously, users could not resize or increase the ranges of IPs assigned to Services, causing problems when there were overlapping networks or the cluster ran out of available IPs.

However, kube-apiserver that have enabled theMultiCIDRServiceAllocator feature gate and the networking.k8s.io/v1alpha1 API allows users to dynamically expand the number of IPs available for Services. This feature is graduated to the beta stage in kubernetes 1.31 and it is recommended to wait for the feature to graduate to Stable before using it in production. Refer to KEP-1880—Multiple Service CIDRs for more details about the implementation.

In this blog post, we’ll explore how to extend the Service IP range in a Google Kubernetes Engine (GKE) cluster. You can also test the feature in other kubernetes distributions and refer to the release notes specific to the distributions for configuration or limitation details.

Prerequisites

You must have a GKE cluster with version 1.31.1-gke.1361000 or later, and ensure that the kubectl command-line tool is configured to communicate with your cluster.

Enable and Verify the Beta APIs

Starting with Kubernetes version 1.24, new beta APIs are disabled by default in new GKE clusters. Existing clusters created running a version earlier than 1.24 keep existing beta APIs enabled.

You can either enable the beta APIs during the cluster creation process or later. To enable the required beta APIs, follow the instructions in Use Kubernetes beta APIs with GKE clusters.

Sample command to enable the required beta APIs on an existing cluster:

gcloud container clusters update <<GKE_CLUSTER_NAME>> \
  --enable-kubernetes-unstable-apis=networking.k8s.io/v1beta1/ipaddresses,networking.k8s.io/v1beta1/servicecidrs
  --region <<GKE_CLUSTER_REGION>> \
  --project <<GCP_PROJECT_NAME>>

The newly enabled APIs will create a ServiceCIDR object using a well-known name kubernetes and an IP address range based on the initial service CIDR.

Run the below command to verify the new ServiceCIDR object,

Chimbus-MacBook-Pro:~ chimbu$ kubectl get servicecidr
NAME         CIDRS          AGE
kubernetes   10.96.0.0/28   98m
Chimbus-MacBook-Pro:~ chimbu$

Adding a new ServiceCIDR

For testing purposes, I have created a cluster with /28 range for services which contains only 14 IP addresses. The kubernetes.default Service is always created; for this example, that leaves you with only 13 possible Services.

For testing purposes, I have created a cluster with a /28range for services that contains only 14 IP addresses. The kubernetes.default service is always created and that leaves you with just 13 possible Services.

Try creating additional kubernetes services and the request will fail with an internal error once all the ip addresses in the service range are utilized.

Chimbus-MacBook-Pro:~ chimbu$ for i in $(seq 1 13); do kubectl create service clusterip "service-test-$i" --tcp 80 -o json | jq -r .spec.clusterIP; done
10.96.0.13
10.96.0.11
10.96.0.7
10.96.0.14
10.96.0.3
10.96.0.12
10.96.0.9
10.96.0.4
10.96.0.5
error: failed to create ClusterIP service: Internal error occurred: failed to allocate a serviceIP: range is full
error: failed to create ClusterIP service: Internal error occurred: failed to allocate a serviceIP: range is full
error: failed to create ClusterIP service: Internal error occurred: failed to allocate a serviceIP: range is full
error: failed to create ClusterIP service: Internal error occurred: failed to allocate a serviceIP: range is full
Chimbus-MacBook-Pro:~ chimbu$

You can now expand the number of available IP addresses for Services by creating a new ServiceCIDR that extends or adds new IP address ranges.

During the beta phase, GKE allows you to only create Service CIDRs in the 34.118.224.0/20 reserved IP address range to avoid possible issues with overlapping IP address ranges.

Apply the below manifest to create a new ServiceCIDR that adds a new IP address range.

cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1beta1
kind: ServiceCIDR
metadata:
  name: newcidr1
spec:
  cidrs:
  - 34.118.224.0/20
EOF
Chimbus-MacBook-Pro:~ chimbu$ kubectl get servicecidrs.networking.k8s.io
NAME         CIDRS             AGE
kubernetes   10.96.0.0/28      104m
newcidr1     34.118.224.0/20   5s
Chimbus-MacBook-Pro:~ chimbu$

Try creating new kubernetes services and the cluster IP address will be picked from this new range.

Chimbus-MacBook-Pro:~ chimbu$ for i in $(seq 1 13); do kubectl create service clusterip "service-test-new-$i" --tcp 80 -o json | jq -r .spec.clusterIP; done
34.118.235.89
34.118.234.162
34.118.230.252
34.118.226.209
34.118.227.183
34.118.227.182
^C
Chimbus-MacBook-Pro:~ chimbu$ kubectl get service
NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes           ClusterIP   10.96.0.1        <none>        443/TCP   105m
service-test-1       ClusterIP   10.96.0.13       <none>        80/TCP    3m41s
service-test-2       ClusterIP   10.96.0.11       <none>        80/TCP    3m39s
service-test-3       ClusterIP   10.96.0.7        <none>        80/TCP    3m39s
service-test-4       ClusterIP   10.96.0.14       <none>        80/TCP    3m38s
service-test-5       ClusterIP   10.96.0.3        <none>        80/TCP    3m37s
service-test-6       ClusterIP   10.96.0.12       <none>        80/TCP    3m36s
service-test-7       ClusterIP   10.96.0.9        <none>        80/TCP    3m35s
service-test-8       ClusterIP   10.96.0.4        <none>        80/TCP    3m34s
service-test-9       ClusterIP   10.96.0.5        <none>        80/TCP    3m33s
service-test-new-1   ClusterIP   34.118.235.89    <none>        80/TCP    8s
service-test-new-2   ClusterIP   34.118.234.162   <none>        80/TCP    8s
service-test-new-3   ClusterIP   34.118.230.252   <none>        80/TCP    7s
service-test-new-4   ClusterIP   34.118.226.209   <none>        80/TCP    6s
service-test-new-5   ClusterIP   34.118.227.183   <none>        80/TCP    5s
service-test-new-6   ClusterIP   34.118.227.182   <none>        80/TCP    4s
Chimbus-MacBook-Pro:~ chimbu$

Deleting a ServiceCIDR

The ServiceCIDRs are protected with finalizers to avoid leaving Service ClusterIPs orphans; the finalizer is only removed if no IP addresses are assigned to any service belonging to the subnet.

So you need to first delete all the Kubernetes services containing the IP addresses of the ServiceCIDR and then delete the ServiceCIDR object.

In summary, as Kubernetes clusters expand, increasing the Service IP range becomes crucial to avoid IP exhaustion and facilitate smooth scalability. The new features offer flexibility for environments that require additional service IP addresses over time without risking resource bottlenecks.

I trust this blog post has provided valuable insights. If you’d like to know more or are interested in our services, don’t hesitate to get in touch. You can contact us here.