Kubernetes Handbook

    Kubernetes Service Discovery and DNS Resolution

    Alright, imagine you’re at a massive party in your Kubernetes cluster, and your apps (Pods) are guests mingling around. You’ve got Services like ClusterIP and LoadBalancer acting as door signs to guide traffic (if you caught my last blog, you’re already a pro at exposing apps 😎). But how do these apps find each other in the crowd? That’s where Kubernetes Service Discovery and DNS resolution swoop in, acting like a super-smart party host who knows everyone’s name and where they’re hanging out. In this blog, we’re diving deep into how Kubernetes makes it easy for your apps to locate and talk to each other without hardcoding IPs or losing their minds.

    Here’s the thing: Pods are like nomads, constantly moving or disappearing, so their IPs are about as reliable as a weather forecast. Kubernetes Service Discovery solves this by giving Services stable names that apps can use to find each other, backed by Kubernetes’ built-in DNS system. By the end of this post, you’ll understand how DNS resolution works, how to use it with Kubernetes Services, and how to troubleshoot when things go wonky. I’ll be real—my first attempt at service discovery was a mess, but you’ll nail it with these tips. Let’s dive in!

    What Is Kubernetes Service Discovery?#

    Picture your Kubernetes cluster as a busy city with Pods as workers in ever-changing offices. If one app (like a frontend) needs to call another (like a backend), it can’t just knock on a random door—Pods’ IPs change when they restart or scale. Kubernetes Service Discovery is like a phonebook for your cluster, letting apps look up other apps by a stable name instead of flaky IPs. It’s a core part of container orchestration, ensuring your apps can communicate reliably.

    Service discovery in Kubernetes relies on DNS resolution, where each Service gets a DNS name that resolves to its ClusterIP. This means your frontend can call backend-service instead of memorizing an IP like 10.96.123.45. Kubernetes’ internal DNS server (usually CoreDNS) handles these lookups, making communication seamless.

    Why Do You Need Service Discovery?#

    When I started with Kubernetes, I tried hardcoding Pod IPs in my app’s config. Spoiler: it broke spectacularly when a Pod restarted. 😅 Service discovery saves you from this chaos by:

    • Stable Naming: Services have fixed DNS names, even as Pods come and go.
    • Load Balancing: DNS resolves to a Service’s ClusterIP, which distributes traffic across Pods.
    • Simplicity: Apps use human-readable names (e.g., mysql-service) instead of IPs.

    How DNS Resolution Works in Kubernetes#

    Let’s break down the magic of DNS resolution in Kubernetes. Every time you create a Service, Kubernetes assigns it a ClusterIP and registers a DNS name with the cluster’s DNS server (CoreDNS by default). When an app queries that DNS name, CoreDNS resolves it to the Service’s ClusterIP, which then routes traffic to the right Pods.

    The DNS name for a Service follows this format:

    <service-name>.<namespace>.svc.cluster.local
    • <service-name>: The Service’s name (e.g., backend-service).
    • <namespace>: The namespace where the Service lives (e.g., dev-namespace).
    • svc.cluster.local: The default DNS suffix for Services in the cluster.

    For example, a Service named backend-service in dev-namespace gets the DNS name:

    backend-service.dev-namespace.svc.cluster.local

    Apps in the same namespace can use the short name backend-service, while apps in other namespaces need the full name or a partial version like backend-service.dev-namespace.

    Using Service Discovery in Action#

    Let’s get hands-on with Kubernetes Service Discovery. We’ll create a Service, test DNS resolution, and see how apps use it to communicate. You’ll need a cluster (Minikube works great for testing) and some Pods to play with.

    Example 1: Creating a Service and Testing DNS

    Suppose you have a Deployment with three Pods running a backend app labeled app: backend. You create a ClusterIP Service to expose it:

    apiVersion: v1 kind: Service metadata: name: backend-service namespace: dev-namespace spec: selector: app: backend ports: - protocol: TCP port: 80 targetPort: 8080 type: ClusterIP

    Apply it:

    kubectl apply -f backend-service.yaml

    Output:

    service/backend-service created

    Check the Service:

    kubectl get service -n dev-namespace

    Output:

    NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE backend-service ClusterIP 10.96.123.45 <none> 80/TCP 10s

    Now, let’s test DNS resolution from another Pod. Spin up a temporary Pod with a curl command to query the Service:

    kubectl run -it --rm test-pod --image=busybox --namespace=dev-namespace -- sh

    Inside the Pod, run:

    nslookup backend-service

    Output:

    Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: backend-service Address 1: 10.96.123.45 backend-service.dev-namespace.svc.cluster.local

    The nslookup command shows that backend-service resolves to the ClusterIP 10.96.123.45. Now, try accessing the Service:

    wget -q -O - <http://backend-service>

    Output (assuming the backend serves a simple webpage):

    <html><body><h1>Welcome to the Backend!</h1></body></html>

    This confirms that the frontend Pod can reach the backend using the Service’s DNS name. No hardcoded IPs needed!

    Cross-Namespace Service Discovery#

    What if your frontend is in a different namespace, like frontend-namespace? Kubernetes makes this easy with DNS resolution across namespaces. The frontend can use the full DNS name backend-service.dev-namespace.svc.cluster.local to reach the backend.

    Example 2: Accessing a Service Across Namespaces

    Let’s create a frontend Pod in frontend-namespace and test cross-namespace discovery. First, ensure the namespaces exist:

    kubectl create namespace frontend-namespace

    Create a frontend Pod:

    apiVersion: v1 kind: Pod metadata: name: frontend-pod namespace: frontend-namespace spec: containers: - name: busybox image: busybox command: ["sleep", "3600"]

    Apply it:

    kubectl apply -f frontend-pod.yaml

    Output:

    pod/frontend-pod created

    Exec into the Pod:

    kubectl exec -it frontend-pod -n frontend-namespace -- sh

    Run nslookup:

    nslookup backend-service.dev-namespace.svc.cluster.local

    Output:

    Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: backend-service.dev-namespace.svc.cluster.local Address 1: 10.96.123.45 backend-service.dev-namespace.svc.cluster.local

    Now, access the Service:

    wget -q -O - <http://backend-service.dev-namespace.svc.cluster.local>

    Output:

    <html><body><h1>Welcome to the Backend!</h1></body></html>

    This shows how Kubernetes Service Discovery works across namespaces, letting apps in different namespaces communicate seamlessly. Pro tip: If you’re in a pinch, you can use the shorter backend-service.dev-namespace from another namespace, as long as the DNS suffix is configured correctly.

    Headless Services: Discovery Without a ClusterIP#

    Sometimes, you don’t want a single ClusterIP for load balancing—like when you need to connect directly to individual Pods (e.g., for stateful apps like databases). Enter headless Services, which skip the ClusterIP and return DNS records for each Pod’s IP.

    Example 3: Creating a Headless Service

    Let’s create a headless Service for a stateful database app with Pods labeled app: database. Here’s the YAML:

    apiVersion: v1 kind: Service metadata: name: database-service namespace: dev-namespace spec: selector: app: database ports: - protocol: TCP port: 3306 targetPort: 3306 clusterIP: None # Makes it headless

    Apply it:

    kubectl apply -f database-service.yaml

    Output:

    service/database-service created

    Check the Service:

    kubectl get service -n dev-namespace

    Output:

    NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE database-service ClusterIP None <none> 3306/TCP 10s

    Test DNS resolution from a Pod:

    kubectl run -it --rm test-pod --image=busybox --namespace=dev-namespace -- sh

    Run:

    nslookup database-service

    Output (assuming two database Pods):

    Server: 10.96.0.10 Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local Name: database-service Address 1: 10.244.0.15 database-pod-0.dev-namespace.svc.cluster.local Address 2: 10.244.0.16 database-pod-1.dev-namespace.svc.cluster.local

    Instead of a single ClusterIP, the DNS query returns the IPs of individual Pods (e.g., 10.244.0.15 and 10.244.0.16). This is perfect for apps like MySQL or MongoDB, where clients need to connect to specific instances.

    When to Use Headless Services#

    I used to think headless Services were just a weird edge case, but they’re super handy for stateful apps or when you need direct Pod access. Use them for databases, message queues, or any app where load balancing isn’t the goal.

    Troubleshooting Service Discovery Issues#

    Let’s be real—Kubernetes Service Discovery is awesome, but it’s not always smooth sailing. Here are common issues I ran into and how to fix them:

    DNS Not Resolving: If nslookup backend-service fails, check if CoreDNS is running:

    kubectl get pods -n kube-system -l k8s-app=kube-dns

    Ensure the Pods are Running. If not, debug with kubectl logs.

    Wrong Selector: If a Service can’t find Pods, verify the selector matches the Pods’ labels:

    kubectl get pods -n dev-namespace -l app=backend

    Namespace Confusion: If cross-namespace DNS fails, double-check the full DNS name and namespace.

    Best Practices for Service Discovery#

    To master Kubernetes Service Discovery, keep these tips in mind:

    • Use Meaningful Service Names: Names like backend-service or mysql-service are clear and intuitive.
    • Leverage Namespaces: Organize Services in namespaces and use full DNS names for cross-namespace access.
    • Consider Headless Services for Stateful Apps: Skip ClusterIP for databases or apps needing direct Pod access.
    • Monitor DNS Health: Watch CoreDNS logs and ensure it’s running smoothly.
    • Test DNS Resolution: Use nslookup or curl from a Pod to verify Service names resolve correctly.

    Conclusion#

    In this blog, you unlocked the magic of Kubernetes Service Discovery and DNS resolution, learning how Kubernetes makes it easy for apps to find and talk to each other. We covered how DNS assigns stable names to Services, explored cross-namespace discovery, and tackled headless Services for stateful apps. With hands-on examples like creating Services and testing DNS with nslookup, you’re ready to connect your apps like a container orchestration pro. Don’t worry if DNS feels like wizardry at first—tinker with a test cluster, and it’ll start making sense!

    Next up in our Kubernetes Handbook, we’ll dive into Persistent Storage in Kubernetes, where you’ll learn how to give your apps a place to store data that sticks around, using Volumes, Persistent Volumes, and Dynamic Provisioning.

    Last updated on May 06, 2025