How Service Discovery works in Kubernetes?

Service discovery in Kubernetes enables microservices to locate and communicate with each other efficiently. Kubernetes automatically manages service discovery using DNS, making it a foundational aspect of microservices communication within a Kubernetes cluster. This tutorial will explore the concepts, configurations, and examples of service discovery using Kubernetes DNS.

How Service Discovery Works in Kubernetes

Kubernetes provides built-in DNS-based service discovery that ensures each service within the cluster can communicate with others using DNS names. The DNS server runs inside the cluster and maintains records for all services.

DNS-Based Service Discovery

  • Automatic DNS Naming: Each service is assigned a DNS name of the format: service-name.namespace.svc.cluster.local.
    • service-name: The name of the service.
    • namespace: The namespace where the service resides (e.g., default).
    • svc.cluster.local: The default DNS suffix for Kubernetes services.
  • Internal Communication: Services within the cluster communicate using this DNS name, allowing seamless inter-service communication.

Example Scenario: Order-Service Communicating with User-Service and Payment-Service

Imagine you have three microservices:

  1. Order-Service: Manages orders.
  2. User-Service: Manages user data.
  3. Payment-Service: Handles payment transactions.

Architecture Overview

🏢 Kubernetes Cluster

DNS Name: user-service.default.svc.cluster.local

DNS Name: payment-service.default.svc.cluster.local

Internal Communication

Internal Communication

Place Order

🟢 User-Service

🔵 Order-Service

🟠 Payment-Service

👤 Client


Implementing Service Discovery

1. Defining User-Service

apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
    spec:
      containers:
        - name: user-service
          image: user-service-image
          ports:
            - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: user-service
spec:
  selector:
    app: user-service
  ports:
    - port: 80
      targetPort: 8080

2. Defining Payment-Service

apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: payment-service
  template:
    metadata:
      labels:
        app: payment-service
    spec:
      containers:
        - name: payment-service
          image: payment-service-image
          ports:
            - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: payment-service
spec:
  selector:
    app: payment-service
  ports:
    - port: 80
      targetPort: 8080

3. Defining Order-Service

apiVersion: apps/v1
kind: Deployment
metadata:
  name: order-service
spec:
  replicas: 2
  selector:
    matchLabels:
      app: order-service
  template:
    metadata:
      labels:
        app: order-service
    spec:
      containers:
        - name: order-service
          image: order-service-image
          ports:
            - containerPort: 8080
          env:
            - name: USER_SERVICE_HOST
              value: "user-service.default.svc.cluster.local"
            - name: USER_SERVICE_PORT
              value: "80"
            - name: PAYMENT_SERVICE_HOST
              value: "payment-service.default.svc.cluster.local"
            - name: PAYMENT_SERVICE_PORT
              value: "80"
---
apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  selector:
    app: order-service
  ports:
    - port: 80
      targetPort: 8080

How Communication is Configured

  • Environment Variables: In the Order-Service deployment, environment variables specify the DNS names of the User-Service and Payment-Service.
    • USER_SERVICE_HOST and PAYMENT_SERVICE_HOST are set to the DNS names of the corresponding services.
  • DNS Resolution: Kubernetes automatically resolves these DNS names to the respective service endpoints.

Workflow

  1. Client Places an Order:
    • The client sends a request to the Order-Service.
  2. Order-Service Calls User-Service:
    • The Order-Service uses the DNS name user-service.default.svc.cluster.local to access the User-Service and retrieve user details.
  3. Order-Service Calls Payment-Service:
    • After retrieving user information, Order-Service calls Payment-Service using the DNS name payment-service.default.svc.cluster.local to process the payment.
  4. DNS Resolution:
    • Kubernetes DNS resolves the service names to the corresponding service IPs, enabling communication.
🟠 Payment-Service🟢 User-Service🔵 Order-Service👤 Client🟠 Payment-Service🟢 User-Service🔵 Order-Service👤 ClientPlace OrderFetch User DetailsUser DetailsProcess PaymentPayment ConfirmationOrder Placed

DNS Configuration in Kubernetes

Automatic DNS Setup

  • Kubernetes automatically configures DNS for all services created within the cluster.
  • The Kube-DNS add-on or CoreDNS (the default DNS server in Kubernetes) manages DNS records, ensuring that all services can be accessed using their DNS names.
  • You don’t need to manually configure DNS records for internal services.

Internal Communication Example

  • In the Order-Service code, you can use the USER_SERVICE_HOST environment variable to make a request to the User-Service.
    const http = require('http');
    
    const userServiceHost = process.env.USER_SERVICE_HOST;
    const userServicePort = process.env.USER_SERVICE_PORT;
    
    const options = {
        hostname: userServiceHost,
        port: userServicePort,
        path: '/users',
        method: 'GET'
    };
    
    const req = http.request(options, (res) => {
        let data = '';
    
        res.on('data', (chunk) => {
            data += chunk;
        });
    
        res.on('end', () => {
            console.log(`User Details: ${data}`);
        });
    });
    
    req.on('error', (error) => {
        console.error(`Error connecting to User-Service: ${error.message}`);
    });
    
    req.end();

Key Takeaways

  • DNS-Based Service Discovery: Kubernetes uses DNS to manage service discovery, making communication between microservices seamless.
  • Automatic DNS Configuration: Kubernetes automatically assigns DNS names to services, removing the need for manual configuration.
  • Environment Variables: You can use environment variables to set the DNS names of services in the deployment configurations, making the communication dynamic and reliable.
  • Internal Communication: Services can communicate within the cluster using these DNS names, ensuring connectivity.

FAQs

Q1: How does Kubernetes manage DNS for service discovery?

  • Kubernetes runs a DNS server (e.g., CoreDNS) within the cluster that automatically assigns DNS names to services. This allows microservices to communicate using service names, which are resolved to the service IP addresses.

Q2: Do I need to configure DNS manually for service discovery?

  • No, Kubernetes automatically handles DNS configuration for internal services. You only need to use the service name, namespace, and DNS suffix for communication.

Q3: What happens if a service’s IP changes?

  • Kubernetes DNS dynamically updates DNS records if a service’s IP changes, ensuring consistent connectivity across the cluster.

Q4: Can services in different namespaces communicate using DNS?

  • Yes, services in different namespaces can communicate using fully qualified domain names (e.g., user-service.other-namespace.svc.cluster.local).

Q5: How is service discovery secured in Kubernetes?

  • Kubernetes supports TLS encryption, RBAC (Role-Based Access Control), and Network Policies to secure communication between services.
Clap here if you liked the blog