A ClusterIP Service is the default Kubernetes service type. It provides a virtual IP address (VIP) inside the cluster to allow pods to communicate with each other. It is internal-only, meaning it is not accessible from outside the cluster.
Example Manifests
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: nginx:1.25-alpine # Using Nginx as an example
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app
ports:
- port: 80 # Port exposed by the Service (accessible inside the cluster)
targetPort: 80 # Port on the Pod container
type: ClusterIP # Default type is ClusterIP
You can try it out by running the following commands
Apply the Kubernetes manifest to the test cluster kind-my-cluster. If the test cluster doesn’t exist, you can create it using Kind or Minikube.
kubectl --context kind-my-cluster apply -f deployment-service-clusterip.yaml
Run these commands to check the pods and cluster IPs.
kubectl --context kind-my-cluster get pods
NAME READY STATUS RESTARTS AGE
my-app-7849ff97f7-tznb9 1/1 Running 0 35s
my-app-7849ff97f7-vmmvp 1/1 Running 0 35s
kubectl --context kind-my-cluster get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2m33s
my-service ClusterIP 10.96.144.131 <none> 80/TCP 45s
Create a temporary test pod in the same cluster, then SSH into it to communicate with another pod.
kubectl --context kind-my-cluster run test-client --rm -it --image=busybox:1.36 -- sh
wget -qO- http://10.96.144.131:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
Overall ClusterIP Communication Process
- Service Creation
- When you create a Service of type ClusterIP, Kubernetes assigns it a virtual IP address (the ClusterIP) from the Service CIDR range (e.g., 10.96.0.0/12).
- This IP does not belong to any physical node interface; it only exists virtually inside the cluster.
- kube-proxy Programming Rules
- Each node runs a component called kube-proxy.
- kube-proxy watches the Kubernetes API for Services and their Endpoints (the list of Pods backing the Service).
- It installs routing/forwarding rules on the node using either iptables or IPVS.
- These rules map the ClusterIP:Port to one of the backend Pod IPs.
- Pod Makes a Request
- A Pod tries to access the Service, e.g. http://my-service:80.
- DNS (CoreDNS) resolves my-service.default.svc.cluster.local to the ClusterIP (e.g., 10.96.0.12).
- Traffic Reaches ClusterIP
- The request packet is sent to 10.96.0.12:80.
- The node’s iptables/IPVS rules intercept it and perform DNAT (destination NAT), rewriting the destination to a real Pod IP:Port, e.g., 10.244.1.5:8080.
- Forwarding to the Pod
- If the Pod is on the same node, the packet is delivered directly.
- If the Pod is on a different node, the packet is forwarded across the cluster network (via the CNI plugin like Calico or Flannel).
- Response Path
- The Pod sends back the response.
- The return traffic goes through the reverse NAT process, so the client Pod sees the response as if it came from the ClusterIP, not the real Pod IP.
- This keeps the abstraction consistent.
Summary (data flow)
Client Pod → DNS → ClusterIP (virtual IP) → kube-proxy (iptables/IPVS rules) → One of the backend Pod IPs → Pod processes request and replies → NAT rewrites response back → Client Pod