Lesson 4.2: Services (ClusterIP, NodePort, LoadBalancer, and ExternalName)
A Service in Kubernetes is a way to expose an application running in your cluster as a single, stable network endpoint. It allows you to make a set of Pods (which are ephemeral and can be created/destroyed dynamically) accessible to other applications, either within the cluster or externally.
Why Do We Need Services?
- Pods Are Ephemeral:
- Pods are temporary and can be created, destroyed, or replaced at any time.
- Each Pod has its own IP address, but these IPs change when Pods are replaced.
- Dynamic Scaling:
- When using a Deployment, the number of Pods can scale up or down based on demand.
- This makes it hard for other applications (e.g., frontends) to keep track of which Pods (backends) to connect to.
- Service Discovery:
- Services provide a stable IP address and DNS name for a group of Pods, so other applications don’t need to worry about the changing IPs of individual Pods.
- Abstraction:
- Services act as an abstraction layer, hiding the complexity of Pod management from other applications.
- If you’re running a microservices architecture, each service should only need to know the Service name of its dependencies, not the Pod IPs.
- Scalability:
- Services automatically handle scaling by updating their Endpoints with the latest Pod IPs.
- Health Checks:
- Services integrate with Kubernetes health checks and only route traffic to healthy Pods.
How Do Services Work?
- Logical Abstraction:
- A Service groups a set of Pods together using a selector (e.g., env: demo).
- It provides a single endpoint (IP address and port) to access these Pods.
- Load Balancing:
- When you send a request to a Service, it automatically distributes the traffic (load balances) across all the Pods that match the selector.
- Endpoints:
- Kubernetes continuously updates the list of Pod IPs (called Endpoints) that the Service routes traffic to.
ClusterIP
A ClusterIP is the default type of Service in Kubernetes. It provides a stable, internal IP address that other applications within the cluster can use to access a group of Pods. ClusterIP Services are not accessible from outside the cluster, making them ideal for internal communication between microservices or components.
Key Features of ClusterIP
- Internal Access Only:
- ClusterIP Services are only accessible from within the Kubernetes cluster.
- They are not exposed to the external network.
- Stable IP Address:
- The Service is assigned a stable, cluster-internal IP address that does not change as long as the Service exists.
- Load Balancing:
- Traffic sent to the ClusterIP is automatically load-balanced across all the Pods that match the Service’s selector.
- DNS Resolution:
- The Service is assigned a DNS name in the format
<service-name>.<namespace>.svc.cluster.local
, which can be used by other applications in the cluster to resolve the Service’s IP address.
- The Service is assigned a DNS name in the format
How ClusterIP Works
- Selector: A ClusterIP Service uses a selector to identify the Pods it should route traffic to.
- Endpoints: Kubernetes automatically creates Endpoints for the Service, which are the IP addresses and ports of the Pods that match the selector.
- Traffic Routing: When a request is sent to the ClusterIP, Kubernetes routes the traffic to one of the Pods in the Endpoints list. The traffic is load-balanced across all the Pods.
[root@master ~]# cat clusterip.yml apiVersion: v1 kind: Service metadata: name: cluster-svc labels: env: demo spec: type: ClusterIP ports: - port: 80 targetPort: 80 selector: env: demo [root@master ~]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE cluster-svc ClusterIP 10.96.48.224 <none> 80/TCP 4m41s [root@master ~]# kubectl describe svc cluster-svc Name: cluster-svc Namespace: default Labels: env=demo Annotations: <none> Selector: env=demo Type: ClusterIP IP Family Policy: SingleStack IP Families: IPv4 IP: 10.96.48.224 IPs: 10.96.48.224 Port: <unset> 80/TCP TargetPort: 80/TCP Endpoints: 10.244.2.3:80,10.244.1.2:80,10.244.2.2:80 Session Affinity: None Internal Traffic Policy: Cluster Events: <none>
- Check if the service is accessible, from pods inside the cluster.
# Using pod nginx-rc-8ct8h [root@master ~]# kubectl exec -it nginx-rc-8ct8h -- sh # curl 10.96.48.224:80 <!DOCTYPE html> ... <title>Welcome to nginx!</title> ... </html> # Using pod nginx-rc-gfhzz [root@master ~]# kubectl exec -it nginx-rc-gfhzz -- sh # curl 10.96.48.224:80 <!DOCTYPE html> ... <title>Welcome to nginx!</title> ... </html> # curl cluster-svc:80 <!DOCTYPE html> ... <title>Welcome to nginx!</title> ... </html>
- Service Creation: When you apply the YAML file, Kubernetes creates a Service with a ClusterIP (e.g., 10.96.48.224).
- Endpoints: Kubernetes identifies the Pods that match the selector (app: my-app) and adds their IPs and ports to the Service’s Endpoints.
- Accessing the Service: Other applications in the cluster can access the Service using:
- The ClusterIP (e.g., 10.96.48.224:80).
- The DNS name (e.g., cluster-svc:80).
- Load Balancing: Traffic sent to the Service is distributed evenly across the Pods in the Endpoints list.
NodePort
A NodePort is a type of Kubernetes Service that exposes an application externally by opening a static port on every node in the cluster. This allows external traffic to access your application by targeting any node’s IP address and the specified NodePort. NodePort is often used for development, testing, or exposing services to external users when a cloud provider’s load balancer (e.g., LoadBalancer Service) is not available.
Key Features of NodePort
- External Access:
- Exposes the Service externally via a static port (default range: 30000–32767) on every node in the cluster.
- Accessible using
<NodeIP>:<NodePort> (e.g., http://192.168.1.100:30001)
.
- Internal Access:
- Also creates a ClusterIP internally, so the Service is accessible within the cluster via
<ClusterIP>:<Port>
.
- Also creates a ClusterIP internally, so the Service is accessible within the cluster via
- Load Balancing:
- Distributes traffic evenly across all Pods matching the Service’s selector.
- Port Mapping:
- Maps three ports:
- nodePort: The static port opened on nodes (e.g., 30001).
- port: The port exposed by the Service internally (e.g., 80).
- targetPort: The port on which Pods are listening (e.g., 80).
- Maps three ports:
How NodePort Works
- Selector:
- The Service uses a selector (e.g.,
env: demo
) to identify the Pods it should route traffic to.
- The Service uses a selector (e.g.,
- Port Mapping:
- External traffic arrives at
<NodeIP>:<NodePort>
(e.g., 192.168.1.100:30001). - Kubernetes routes the traffic to the Service’s port (e.g., 80).
- The Service forwards the traffic to the Pods’ targetPort (e.g., 80).
- External traffic arrives at
- Endpoints:
- Kubernetes maintains a list of Pod IPs (Endpoints) that match the selector. Traffic is load-balanced across these Pods.
[root@master ~]# cat nodeport.yml apiVersion: v1 kind: Service metadata: name: nodeport-svc labels: env: demo spec: type: NodePort ports: - port: 80 targetPort: 80 nodePort: 30001 selector: env: demo [root@master ~]# kubectl apply -f nodeport.yml service/nodeport-svc created [root@master ~]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 9m52s nodeport-svc NodePort 10.96.152.17 <none> 80:30001/TCP 5s
[root@master ~]# kubectl describe svc nodeport-svc Name: nodeport-svc Namespace: default Labels: env=demo Annotations: <none> Selector: env=demo Type: NodePort IP Family Policy: SingleStack IP Families: IPv4 IP: 10.96.152.17 IPs: 10.96.152.17 Port: <unset> 80/TCP TargetPort: 80/TCP NodePort: <unset> 30001/TCP Endpoints: 10.244.2.2:80,10.244.2.3:80,10.244.1.2:80 Session Affinity: None External Traffic Policy: Cluster Internal Traffic Policy: Cluster Events: <none>
[root@master ~]# curl localhost:30001 <!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>
endpoint
[root@master ~]# kubectl get ep NAME ENDPOINTS AGE cluster-svc 10.244.1.2:80,10.244.2.2:80,10.244.2.3:80 84s kubernetes 172.18.0.3:6443 41m nodeport-svc 10.244.1.2:80,10.244.2.2:80,10.244.2.3:80 32m
LoadBalancer
A LoadBalancer Service in Kubernetes exposes an application externally using a cloud provider's load balancer (e.g., AWS ALB, GCP Network Load Balancer, Azure Load Balancer). It automatically provisions an external IP address and routes traffic to your Pods, making it ideal for production environments requiring reliable external access.
Key Features
- External Access:
- Assigns a cloud-provided external IP/DNS name to access the Service from the internet.
- Automatic Integration:
- Works seamlessly with cloud providers to provision and configure the load balancer.
- Port Mapping:
- Routes traffic from the load balancer to NodePorts (and Pods) in the cluster.
- Health Checks:
- Cloud load balancers perform health checks to ensure traffic is only sent to healthy Pods.
How It Works
- When you create a LoadBalancer Service:
- Kubernetes provisions a cloud load balancer.
- The load balancer directs traffic to a NodePort (auto-created if not specified) on cluster nodes.
- NodePort forwards traffic to Pods via the ClusterIP (internal IP).
- The cloud load balancer’s external IP/DNS becomes the entry point for your application.
[root@master ~]# cat lb.yml apiVersion: v1 kind: Service metadata: name: lb-svc labels: env: demo spec: type: LoadBalancer ports: - port: 80 selector: env: demo [root@master ~]# kubectl apply -f lb.yml service/lb-svc created [root@master ~]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE lb-svc LoadBalancer 10.96.33.179 <pending> 80:30885/TCP 68s
EXTERNAL-IP
: The cloud load balancer’s IP (e.g., 203.0.113.100). In this case we are not using cloud load balancer, so it is showing the status<pending>
Use Cases
- Exposing a public-facing web application (e.g., a frontend).
- Production workloads requiring high availability and cloud integration.
Considerations
- Cloud Dependency: Requires integration with a cloud provider (doesn’t work on bare metal without tools like MetalLB).
- Cost: Cloud load balancers may incur additional costs.
- Annotations: Cloud-specific features (e.g., SSL termination, routing rules) are configured via annotations.
ExternalName
An ExternalName Service maps a Kubernetes Service to an external DNS name (e.g., a database hosted outside the cluster). It acts as a DNS alias, allowing internal applications to reference external services using a Kubernetes Service name.
Key Features
- DNS Redirection:
- Resolves to a CNAME record pointing to an external domain (e.g., my-database.example.com).
- No Proxying:
- Does not create endpoints or forward traffic—purely DNS-level redirection.
- Simplifies Configurations:
- Lets applications use a consistent Service name, even if the external service’s address changes.
How It Works
- When an application accesses the ExternalName Service (e.g.,
external-db.default.svc.cluster.local
), the cluster DNS (CoreDNS/kube-dns) returns the CNAME record pointing to the external DNS. - No Kubernetes networking (e.g., kube-proxy) is involved.
apiVersion: v1 kind: Service metadata: name: external-db spec: type: ExternalName externalName: my-database.example.com
Use Cases
- Connecting to external databases (e.g., AWS RDS, MongoDB Atlas).
- Integrating with legacy systems or APIs outside the cluster.
- Simplifying configurations when migrating services into Kubernetes incrementally.
Considerations
- No Traffic Control: Does not proxy or load balance traffic.
- DNS Reliance: Requires DNS resolution to work (ensure network policies allow DNS queries).
- No Ports: ExternalName Services do not define ports (since they are DNS aliases).