K8s Service and Service Discovery

K8s Service and Service Discovery

In this blog let us see how Service and Service Discovery in detail.

The two main important concepts we are going to see today are:

  • Expose k8s workloads using service

  • Discover pods and services in the clusters using DNS and other mechanisms

Service

A Kubernetes Service is a mechanism to expose applications both internally and externally.

In K8s a Service is a single outward-facing endpoint for exposing a network application that is running as one or more Pods in your cluster

A Service in Kubernetes is an object (the same way that a Pod or a ConfigMap is an object).

The service holds access policies and is responsible for enforcing these policies for incoming requests.

The service is assigned a virtual IP address, known as a clusterIP, which persists until it is explicitly destroyed.

You can create, view or modify Service definitions using the Kubernetes API. Usually, you use a tool such as kubectl to make those API calls for you.

Types of Services

Kubernetes allows the creation of these types of services:

  1. ClusterIP

  2. NodePort

  3. LoadBalance

  4. External IP

ClusterIP (default Type of Service)

A Service is created by grouping similar types of pods( Example: web app, db groups) and IP address is assigned to it.

Service receives a name, cluster-internal IP address and a port, making its pods only accessible from within the cluster. This is called as Cluster IP.

In yaml, we need to mention only the target port(backend port) and port(service port) and both are 8

Nodeport

  • A NodePort service builds on top of the ClusterIP service, exposing the node to a port accessible from outside the cluster.

  • The nodePort field in the service manifest is optional and lets you specify a custom port between 30000-32767.

  • In yaml, while creating this kind of service three ports are important node port(has range), target port(default as port) and service port(mandatory)

  • yaml file for reaching applications within a single pod on a single node using a service.

  • For multiple pods in a single node same cluster, there is no need for any configuration change in yaml because all have the same label and the service uses a random algorithm to reach pods

  • For multiple pods in multiple nodes in the same cluster, the service expands across all the nodes in the cluster and uses the same port number with the respective node ip.

LoadBalancer

  • A LoadBalancer service is based on the NodePort service and adds the ability to configure external load balancers in public and private clouds via native load balancers.

  • It exposes services running within the cluster by forwarding network traffic to cluster nodes.

ExternalName

  • An ExternalName service maps the service to a DNS name instead of a selector.

  • It returns a CNAME record matching the contents of the externalName field

Expose k8s workloads using service

Services match a set of Pods using labels and selectors, a grouping primitive that allows logical operation on objects in Kubernetes.

Creating a service for an application running in two pods

  • create a YAML file to create a new application deployment

      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: hello-world
      spec:
        selector:
          matchLabels:
            run: load-balancer-example
        replicas: 2
        template:
          metadata:
            labels:
              run: load-balancer-example
          spec:
            containers:
              - name: hello-world
                image: gcr.io/google-samples/node-hello:1.0
                ports:
                  - containerPort: 8080
                    protocol: TCP
    
  • Run the application in cluster

    kubectl apply -f https://k8s.io/examples/service/access/hello-application.yaml

    Create a deployment named: hello-world

    ReplicaSet : 2

    Pods: 2(application each)

  • Display information about the Deployment

    kubectl get deployments hello-world

    kubectl describe deployments hello-world

  • Display information about the rs

    kubectl get replicasets kubectl describe replicasets

Expose the deployment using service

  • This creates service object that exposes deployment

    kubectl expose deployment hello-world --type=NodePort --name=example-service

    Note : Here we use Nodeport to expose the node to outside world in cluster

  • Display info about the service

    kubectl describe services example-service

  • List pods that run Hello world application

    kubectl get pods --selector="run=load-balancer-example" --output=wide

  • check the application

    curl http://<clusterip>:<nodeport>

K8s Service discovery

To make a Pod reachable via external networks or other Kubernetes clusters without relying on any internal IPs, we need another layer of abstraction. Kubernetes offers that abstraction with a construct called a Service Deployment.

Service discovery is the actual process of figuring out how to connect to a service from pods.

For Cloud Native service discovery, k8s updates the EndpointSlices for a Service whenever the set of Pods in a Service changes.

For non-cloud native service discovery, Kubernetes supports 2 primary modes of finding a Service - environment variables and DNS.

Environment variables

When a Pod is run on a Node, the kubelet adds a set of environment variables for each active Service.

It adds {SVCNAME}_SERVICE_HOST and {SVCNAME}_SERVICE_PORT variables, where the Service name is upper-cased and dashes are converted to underscores.

For Example the Service redis-primary which exposes TCP port 6379 has following environmental variable

REDIS_PRIMARY_SERVICE_HOST=10.0.0.11
REDIS_PRIMARY_SERVICE_PORT=6379
REDIS_PRIMARY_PORT=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP_PROTO=tcp
REDIS_PRIMARY_PORT_6379_TCP_PORT=6379
REDIS_PRIMARY_PORT_6379_TCP_ADDR=10.0.0.11

Note: In this method, you must create the Service before the client Pods come into existence. Otherwise, those client Pods won't have their environment variables populated.

DNS-Based Service Discovery

Kubernetes provides a built-in DNS service that allows you to discover Services and Pods using their DNS names. The DNS service is automatically created when you create a cluster and acts as pods.

Kube-Dns or CoreDNS integrate with Kubernetes via the Kubernetes plugin, or with etcd with the etcd plugin. All major cloud providers have plugins too: Microsoft Azure DNS, GCP Cloud DNS and AWS Route53.

How do Pods Communicate

K8s implements DNS in clusters. Let’s say one pod i.e. test wants to communicate with another pod, db. So, we can do that by adding the db pods ip in test pod's /etc/hosts file*.*

But the same cannot be done when more pods need to communicate with each other in a cluster

To eliminate this, a centralized DNS server was introduced. It stores the pod's name and IP. So the pods reach the DNS server and connect with other pods in the cluster. But it is possible only when pods reach the DNS server, for that we need to mention the DNS server namespace and IP address in each pod's /etc/resolve.conf.

Every time a new pod gets created, k8s will create an entry of the new pod in the DNS server and also will update the /etc/resolv.conf file of the new pod with IP address of the DNS server.

CoreDNS in K8s

As CoreDns is the latest version here we will see about it. A cluster-aware DNS server, such as CoreDNS, watches the Kubernetes API for new Services/pods and creates a set of DNS records for each one.

CoreDNS has been available in K8s since v1.9. It is a fast and flexible DNS server.

How CoreDNS server record entries

As a continuation from the above topic, in the below picture, you can see that in the CoreDNS server there are two types of entries

  1. One for service - for service, it is mapped as name and IP address

  2. One for pods - for pods the name is noted by its IP address by replacing '.' with '-' and the IP address

By default,CoreDNS's pod entries will be disabled. Once it is enabled, throughout your cluster then all Pods should automatically be able to resolve Services by their DNS name.

The format of the DNS record would be: service-name.namespace.svc.cluster.local

Now the client pod can access the URL of the application deployed in pods using any of the below DNS names mentioned in the picture.

How is the CoreDNS setup in Cluster

CoreDNS is configured as pods as a namespace in the cluster. They are deployed as two pods as part of the redundancy of a replica set within a deployment.

It requires a configuration file /etc/coredns/CoreFile and in this number of plugins are configured (The one that is in orange).

Here Kubernetes plugin integrates CoreDNS and K8s, here we set the top-level domain name of the cluster as cluster.local.

Every record in the CoreDNS server falls under this domain.

As mentioned in the above topic pods insecure is the option that is disabled by default. We can enable this to record pod entries

Next comes the proxy, once pods try to reach an application URL it checks the CoreFile's proxy attribute. It is then forwarded to the nameserver specified in the CoreDNS pod's resolve.conf file.

It now uses the resolve.conf file to reach the DNS nameserver in K8s Node.

Note: This CoreFile is passed into Pod as a ConfigMap object. To edit this configuration we can use ConfigMap object

kubectl get configmap -n kube-system

Now we have the CodeDNS pods running using the CoreDNS plugin. It watches the K8s cluster whenever pods/ services are created and adds the entries to its database.

Let us think that we have multiple DNS servers, so what address do the pods use to reach the DNS server? Here comes the service that will be created by default when CoreDNS is installed. This Service is called kube-dns.

kubectl get service -n kube-system

This Service IP address is configured as nameserver IP in /etc/resolve.conf of pods. This configuration is done by K8s automatically when pods are created. Kubelet component is responsible for this process.

Below is the config file of kubelet is the IP address of the DNS server(service IP)

The pods are configured with the right nameserver. Now the pods can resolve other pods or services like below

If we check this with nslookup or host command it will return the FQDN of webservice

In the above picture, the DNS nameserver can resolve the services in any of these names.

However, it is not the case with pods and the FQDN name needs to be mentioned for it

Reference: https://medium.com/kubernetes-tutorials/kubernetes-dns-for-services-and-pods-664804211501

https://platform9.com/blog/kubernetes-service-discovery-principles-in-practice/

https://www.digitalocean.com/community/tutorials/an-introduction-to-the-kubernetes-dns-service

Summary

To understand these concepts, learning the basic DNS and routing concepts would be much helpful. Also, Service and Service Discovery are the crucial concepts of effective and efficient networking in K8s. They run the cloud native and native networking concept as a miniature within K8s. Still, it helps in forming and managing more complex infrastructure.

~~ Saraa

Thanks for reading my blog. Hope it gave some insights on DNS in K8s.

Suggestions are always welcomed.

Will see you in the next blog ........... :)