Home > Azure > Kubernetes

Architecting Kubernetes Services on Azure AKS: ClusterIP vs NodePort vs LoadBalancer

@admin 5 min read

In Kubernetes, container pods are ephemeral and mortal. When a pod is rescheduled, terminated, or replaced during a rolling update, it is assigned a completely new internal IP address from the cluster subnet. If your frontend web applications or dependent microservices tried to communicate directly with these fluctuating pod IPs, your network traffic would break instantly with every pod restart.

A Kubernetes Service solves this challenge by acting as a durable, static network gateway and intelligent layer-4 load balancer. A Service maintains a single stable IP address and DNS hostname, automatically routing client traffic across healthy pod replicas matching its label selector.

In this hands-on guide, you will master Kubernetes networking primitives on Azure Kubernetes Service (AKS):

  • Service Networking Fundamentals: Understanding how Services decouple client communication from dynamic pod IP lifecycles.
  • ClusterIP (Internal Communication): Deploying private internal services isolated from the public internet with automatic CoreDNS discovery.
  • NodePort (Static Node Port Routing): Opening direct port forwarding gateways across physical AKS worker node IPs.
  • LoadBalancer (Automated Azure Ingress): Triggering the Azure cloud provider to automatically provision a public IP and Azure Standard Load Balancer.
  • CoreDNS Service Discovery Verification: Testing internal DNS hostname resolution and traffic load balancing across pod endpoints.

📌 Lab Prerequisites: To follow along, you need an active Azure Subscription (Free Trial, Student Credits, or Pay-As-You-Go) and a running AKS cluster with Azure CLI and kubectl connected locally. If you need to spin up a cluster first, refer to our foundational guide: Deploy Your First Containerized App on Azure Kubernetes Service (AKS).

📦 GitHub Lab Repository & Manifest Files

To clone, star, or download all ready-to-use Kubernetes service manifests used in this guide, visit our official repository:
👉 StackLayerOfficially/azure-aks-hands-on-labs (07-kubernetes-service-types-clusterip-nodeport-loadbalancer)

⚠️ Disclaimer & Cloud Cost Notice

This hands-on guide is built strictly for educational and lab purposes. The configurations and manifests shown here are simplified demonstrations and should not be used directly in live production environments without independent security reviews. Stack Layer and its technical team assume no responsibility or liability for any issues, outages, or data loss resulting from applying these commands. Provisioning an Azure Public Load Balancer incurs standard Azure cloud consumption costs (billed per hour). Always follow Step-06 to delete your public services and pause or tear down cluster resources when finished.

Understanding the Core Concepts

1. How Services Track Pods: Labels & Endpoints

A Kubernetes Service does not directly manage containers. Instead, it continuously watches the cluster using a Label Selector (e.g., app: stacklayer-backend). Behind the scenes, the Kubernetes control plane automatically maintains an Endpoints object containing the live, real-time IP addresses of every healthy container matching that label. When a pod crashes or scales up, the Endpoints object updates immediately without disrupting client traffic.

2. The Three Primary Kubernetes Service Types

Each service type builds on the capabilities of the previous tier:

Service Type Network Scope Port Mechanism Azure Cloud Provisioning Primary Use Case
ClusterIP (Default) Internal Cluster Only Service Port (e.g., 80, 8080) ❌ None (Free internal routing) Databases, Redis, microservices, internal APIs
NodePort Cluster + Node IP Network Static High Port (30000–32767) ❌ None (Opens port on each VM) Direct node diagnostics, internal testing, bare-metal
LoadBalancer Public Internet Ingress Standard Ports (80, 443) ✅ Azure Standard Load Balancer + Public IP Public websites, mobile app backends, external APIs

Connect Your Local CLI to AKS

Authenticate your local terminal and configure your active Kubernetes context:

[code] # Connect Azure CLI to your running AKS cluster az aks get-credentials --resource-group stacklayer-aks-rg --name stacklayer-aks-cluster --overwrite-existing # Verify cluster connectivity and active worker nodes kubectl cluster-info kubectl get nodes [/code]

Command Breakdown:

  • az aks get-credentials: Pulls cluster certificates and admin authentication context directly into your local ~/.kube/config.
  • --resource-group stacklayer-aks-rg: The target Azure Resource Group containing your AKS cluster.
  • --name stacklayer-aks-cluster: Identifies your target AKS cluster instance.
  • --overwrite-existing: Overwrites any expired local tokens or stale kubeconfig contexts.
  • kubectl cluster-info: Queries the Kubernetes API server endpoint to verify healthy cluster communication.
  • kubectl get nodes: Confirms that worker nodes are in the Ready state and able to schedule workloads.

Step-01: Deploy an Internal Backend Workload with ClusterIP

ClusterIP is the default service type in Kubernetes. It assigns an internal, non-routable IP address accessible exclusively from within the cluster. This is ideal for internal microservices, caching layers, and databases that should never be reachable from the outside world.

First, create a deployment file named 01-backend-deployment.yaml that spins up two replicas of an NGINX microservice:

[code] apiVersion: apps/v1 kind: Deployment metadata: name: stacklayer-backend-deployment labels: app: stacklayer-backend spec: replicas: 2 selector: matchLabels: app: stacklayer-backend template: metadata: labels: app: stacklayer-backend spec: containers: - name: backend-api image: stacklayer/kubernetes:nginx-v1 ports: - containerPort: 80 [/code]

Manifest Breakdown:

  • replicas: 2: Instructs the cluster controller to ensure two identical pod instances run across worker nodes for redundancy.
  • labels: app: stacklayer-backend: The identifier used by the service to discover and balance traffic across these pods.
  • containerPort: 80: Declares the network port exposed by the web container inside the pod network namespace.

Now, declare a ClusterIP service manifest named 02-backend-clusterip-service.yaml:

[code] apiVersion: v1 kind: Service metadata: name: stacklayer-backend-service labels: app: stacklayer-backend spec: type: ClusterIP selector: app: stacklayer-backend ports: - port: 80 targetPort: 80 name: http [/code]

Manifest Breakdown:

  • type: ClusterIP: Tells Kubernetes to assign an internal virtual IP address from the cluster service CIDR block.
  • selector: app: stacklayer-backend: Routes incoming requests to any pod labeled app: stacklayer-backend.
  • port: 80: The port where the service listens internally inside the cluster.
  • targetPort: 80: The container port to which the incoming request is forwarded.

Apply both manifests to your AKS cluster:

[code] # Deploy the backend workload and internal ClusterIP service kubectl apply -f 01-backend-deployment.yaml kubectl apply -f 02-backend-clusterip-service.yaml # Verify the ClusterIP service status kubectl get svc stacklayer-backend-service [/code]

Command Breakdown:

  • kubectl apply -f ...: Submits the deployment and service declarations to the Kubernetes API server.
  • kubectl get svc: Inspects the provisioned service. Notice the CLUSTER-IP (e.g., 10.0.124.52) and that EXTERNAL-IP is set to <none>, confirming the service is completely private.

Step-02: Test Internal CoreDNS Service Discovery

One of Kubernetes' greatest superpowers is built-in CoreDNS Service Discovery. Pods do not need to hardcode IP addresses; they simply communicate using the service name (http://stacklayer-backend-service). Let's launch a temporary diagnostic client pod and verify internal resolution.

[code] # Launch a temporary client container with curl and dnsutils kubectl run curl-client --image=curlimages/curl -it --rm -- restart=Never -- sh [/code]

Inside the interactive terminal prompt of the client pod, execute these commands:

[code] # 1. Query the backend service using its clean DNS name curl http://stacklayer-backend-service # 2. Inspect the Fully Qualified Domain Name (FQDN) resolved by CoreDNS nslookup stacklayer-backend-service [/code]

Type exit to leave and automatically terminate the client pod.

Inspect the live network endpoints discovered by the service:

[code] # View the individual container IPs attached to the service kubectl get endpoints stacklayer-backend-service [/code]

Command Breakdown:

  • curl http://stacklayer-backend-service: Proves that CoreDNS dynamically translates the service name into the virtual ClusterIP, routing traffic seamlessly to the pods.
  • nslookup ...: Reveals the internal FQDN: stacklayer-backend-service.default.svc.cluster.local.
  • kubectl get endpoints: Displays the real-time private IPs of your two backend pods, showing that the service maintains an active pool of healthy targets.

Step-03: Expose Workloads on Worker Node Ports with NodePort

A NodePort service builds directly on top of ClusterIP. In addition to creating an internal ClusterIP, Kubernetes opens a dedicated static port (between 30000 and 32767) on every physical worker node in the cluster. Any traffic arriving at NodeIP:NodePort is forwarded to the service.

Create a manifest named 03-backend-nodeport-service.yaml:

[code] apiVersion: v1 kind: Service metadata: name: stacklayer-nodeport-service labels: app: stacklayer-backend spec: type: NodePort selector: app: stacklayer-backend ports: - port: 80 targetPort: 80 nodePort: 31080 name: http [/code]

Manifest Breakdown:

  • type: NodePort: Instructs the kube-proxy agent on every node to allocate a dedicated port for incoming traffic.
  • nodePort: 31080: Explicitly locks the static listening port on all cluster nodes. (If omitted, Kubernetes randomly assigns an available port from the 30000–32767 range).

Apply the NodePort service and inspect the result:

[code] # Deploy the NodePort service kubectl apply -f 03-backend-nodeport-service.yaml # Check the allocated NodePort kubectl get svc stacklayer-nodeport-service [/code]

Let's find the internal IP address of one of our AKS worker nodes and test connectivity:

[code] # Retrieve internal IP addresses of all cluster worker nodes kubectl get nodes -o wide # Test the NodePort from an internal cluster test pod kubectl run test-nodeport --image=curlimages/curl -it --rm -- restart=Never -- curl http://:31080 [/code]

Command Breakdown:

  • kubectl get nodes -o wide: Displays internal IP addresses assigned by Azure to your VM scale set instances.
  • curl http://<NODE_INTERNAL_IP>:31080: Demonstrates that hitting any worker node on port 31080 automatically proxies the request through the service directly to the backend pods.

Step-04: Expose Workloads to the World with Azure LoadBalancer

While NodePort works on internal networks, opening manual firewall rules and relying on raw node IPs is unsuitable for public internet traffic. The LoadBalancer service type is the standard production mechanism on AKS.

When you declare type: LoadBalancer on AKS, Kubernetes talks to the underlying Azure cloud provider through the Azure Cloud Controller Manager. Azure automatically provisions an Azure Standard Load Balancer, allocates a dedicated Public IP address, and configures health probes and NAT load balancing rules pointing to your pods.

Create a manifest named 04-backend-loadbalancer-service.yaml:

[code] apiVersion: v1 kind: Service metadata: name: stacklayer-public-service labels: app: stacklayer-backend spec: type: LoadBalancer selector: app: stacklayer-backend ports: - port: 80 targetPort: 80 name: http [/code]

Manifest Breakdown:

  • type: LoadBalancer: Triggers the Azure cloud provider to automatically create a public IP and configure an Azure Standard Load Balancer.
  • port: 80: The public-facing port exposed to internet users on the Azure Load Balancer.
  • targetPort: 80: The container port to which the public traffic is securely forwarded.

Apply the manifest and stream live public IP allocation:

[code] # Submit the LoadBalancer service to AKS kubectl apply -f 04-backend-loadbalancer-service.yaml # Stream the service status until EXTERNAL-IP changes from to a live public IP kubectl get svc stacklayer-public-service -w [/code]

Once the EXTERNAL-IP appears (e.g., 20.xxx.xxx.xxx), press Ctrl + C.

Command Breakdown:

  • kubectl get svc ... -w: Watches the service creation in real time. Azure requires approximately 60–90 seconds to provision the public IP and set up health probes on the Azure Standard Load Balancer.

Step-05: Live Verification & Load Balancing Inspection

Open your web browser and navigate directly to your allocated public IP address (e.g., http://20.xxx.xxx.xxx), or query it from your local terminal using curl:

[code] # Query your new Azure Public IP directly from your workstation PUBLIC_IP=$(kubectl get svc stacklayer-public-service -o jsonpath="{.status.loadBalancer.ingress[0].ip}") curl http://$PUBLIC_IP [/code]

You will receive an immediate HTTP 200 response from your NGINX application.

Inspecting the Provisioned Azure Resources in the Portal GUI

To see the underlying cloud infrastructure that Kubernetes built for you automatically:

  1. Sign in to the Azure Portal.
  2. Navigate to your cluster's node resource group (starts with MC_stacklayer-aks-rg_stacklayer-aks-cluster_<region>).
  3. Locate the kubernetes Standard Load Balancer.
  4. Under Settings, click Frontend IP configuration to view your active public IP address.
  5. Click Load balancing rules to see the automated rule routing port 80 traffic into your cluster node pool.

Step-06: Resource Teardown & Cost Optimization

Public Load Balancers incur continuous cloud billing while active. Always delete your public services and clean up cluster resources when you finish practicing:

[code] # 1. Delete all deployed services to release the Azure Public IP immediately kubectl delete -f 04-backend-loadbalancer-service.yaml kubectl delete -f 03-backend-nodeport-service.yaml kubectl delete -f 02-backend-clusterip-service.yaml # 2. Delete the backend deployment kubectl delete -f 01-backend-deployment.yaml # 3. Verify all services and pods are terminated kubectl get svc kubectl get pods [/code]

Command Breakdown:

  • kubectl delete -f 04-backend-loadbalancer-service.yaml: Instructs the Azure Cloud Controller Manager to deprovision the Azure Public IP and dismantle load balancing rules, stopping ongoing consumption charges.
  • kubectl get svc: Confirms that only the default internal kubernetes API service remains.

Option A: Pause Cluster Nodes (Recommended If Practicing Later Labs)

If you plan to follow additional hands-on guides, stop your cluster nodes to halt VM compute charges while preserving your cluster configuration:

[code] # Stop the AKS cluster nodes to halt compute billing az aks stop --resource-group stacklayer-aks-rg --name stacklayer-aks-cluster [/code]

Command Breakdown:

  • az aks stop: Stops the underlying Azure VM worker nodes, deallocating compute resources so you are not billed for hourly VM run time. All cluster settings, network topologies, and configurations are preserved.
  • az aks start: Resumes cluster operations and wakes up worker nodes in approximately 2–3 minutes.

Option B: Complete Deletion (Zero Cost)

If you are finished experimenting and want to wipe out all cloud resources:

[code] # Delete the entire Resource Group and all associated cluster resources az group delete --name stacklayer-aks-rg --yes --no-wait [/code]

Command Breakdown:

  • az group delete: Permanently deletes the resource group, cluster, networking, disks, and associated load balancers.
  • --name stacklayer-aks-rg: Target resource group name.
  • --yes: Bypasses interactive confirmation prompts.
  • --no-wait: Executes asynchronously in the background so your terminal returns immediately.

Summary

In this hands-on guide, you mastered the fundamental building blocks of Kubernetes service networking on Microsoft Azure:

  • How Services solve the problem of ephemeral pod IP churn by providing static virtual IPs and DNS hostnames.
  • How ClusterIP isolates internal microservices and enables automatic service discovery through CoreDNS.
  • How NodePort opens static high-ports (30000–32767) across all physical cluster worker nodes for direct testing.
  • How LoadBalancer automates the provisioning of a real Azure Standard Load Balancer and public IP for production traffic.
  • How to inspect live backend endpoints with kubectl get endpoints.
  • Safeguarded your cloud balance using az aks stop and az group delete.

“Did your services route and your endpoints align, connecting your cluster by flawless design?”

If yes, share your victory in the comments below! If questions arise, let your thoughts freely flow.

Share:

Enjoying our guides? Support Stack Layer!

If you find our tutorials helpful, consider buying us a coffee!

Buy me a coffee

Discussions

Show Ads here — Add AdSense or HTML widget in Layout tab
Show Ads here — Add AdSense or HTML widget in Layout tab