Kubernetes is the preferred system to deploy microservices. And when you have microservices, you usually want interaction between them.

Since each microservice runs within its own Kubernetes Pod, you need a way for one pod to find another pod within the cluster.

This is where the Kubernetes service resource plays a crucial role.

1 – Why the Need for Kubernetes Service?

In a non-Kubernetes world, a sysadmin would configure the client application to talk to the correct service host.

By configuration, I mean specifying the exact IP address of the server where the service is hosted.

That’s not possible in Kubernetes because of 3 reasons:

  • Kubernetes pods are ephemeral. They can get created or removed any time based on the decisions made by the control plane.
  • Pods get their IP address after it has been scheduled and just before it’s started. This means that you can’t know upfront about the IP address that will be assigned.
  • With the magic of horizontal scaling, multiple pods might run the same service and each of those pods will have its own IP address. It doesn’t make any sense for the client to know how many pods run a particular service and which one they should talk to.

The bottom line is that all pods should be accessible through a single IP address.

And to solve this challenge, Kubernetes provide a special resource type – Services.

2 – Big Picture View of Kubernetes Service

Let’s take a typical example of an application that has a frontend and a backend database server.

You may deploy the frontend web server on multiple pods for scalability reasons. However, the database server is deployed on a single pod.

There are two problems that we have to handle over here:

  • External clients need to connect to the frontend pods without worrying about the exact pod.
  • Frontend pods need to connect to the backend database. Though the database is deployed on a single pod, there are chances it may move around the cluster causing a change in the IP address. You don’t want to reconfigure the frontend pods every time that happens.

To solve these problems, you can create two Kubernetes services:

  • One service for the frontend pods that makes the application accessible from outside the cluster. It’s basically a constant IP address through which external clients can connect to the pods.
  • Similarly, a new service for the backend database pod that gives it a stable address that does not change when the pod is moved around.

Here’s what it looks like in practice:

kubernetes service
The Concept of Kubernetes Service

3 – Kubernetes Service Demo

Let’s now create a Kubernetes service and see it in action.

To make things easy, I have divided the whole process into steps.

STEP 1 – Creating a Docker Image

Right off the bat, you can create a Docker image to run on your Kubernetes pods.

Here’s the Dockerfile I’ve used.

#Specify a base image
FROM node:alpine

#Specify a working directory
WORKDIR /usr/app

#Copy the project
COPY ./ ./

#Install dependencies
RUN npm install 

#Default command
CMD ["npm","start"]

It’s a pretty basic image that runs a Node.js application using the node:alpine image.

Here’s the code for the Node.js application in case you are looking for it. Just be aware that it is designed to give 500 status code if you call it more than twice.

const express = require('express');

const app = express();

let requestCount = 0;

app.get('/', (req, res) => {
    requestCount++;
    if (requestCount > 2) {
        res.status(500).send("The app is not well. Please restart!")
    }
    res.send("Hello World from our Kubernetes Pod Demo")
})

app.listen(3000, () => {
    console.log("Listening to requests on Port 3000")
})

STEP 2 – Building a Docker Image

Next up, you need to build the image and provide an appropriate name.

$ docker build .
$ docker tag <image_id> progressivecoder/nodejs-demo

STEP 3 – Creating the Replication Controller

Next, you also create a replication controller or a replica set for running multiple pods. Either will do for this example. All we want is to have multiple pods running the same Node.js application.

Here’s the YAML file for a replication controller.

apiVersion: v1
kind: ReplicationController
metadata:
  name: basic-rc
spec:
  replicas: 3
  selector:
    app: hello-service
  template:
    metadata:
      labels:
        app: hello-service
    spec:
      containers:
      - name: hello-service
        image: progressivecoder/nodejs-demo
        imagePullPolicy: Never

The replicas attribute tells Kubernetes how many pods it has to create.

At the bottom, you have the containers section where you specify the image that should be deployed.

If interested, you can read more about Kubernetes replication controller.

STEP 4 – Create the Service

Finally, we get to the Kubernetes service.

Here’s the YAML for that:

apiVersion: v1
kind: Service
metadata:
  name: basic-service
spec:
  ports:
  - port: 80
    targetPort: 3000
  selector:
    app: hello-service

It is super-simple to understand.

The kind is “Service”.

Then, you have the spec section where we specify the port mapping. Basically, the configuration in the example states that you are routing incoming traffic on port 80 to port 3000 on the pod. Port 3000 is where our Node.js web server is listening.

The selector specifies the label value the service uses to find out the underlying pods it has to connect to. Here the label is app: hello-service.

STEP 5 – Testing the Service

Time to test our Kubernetes service.

Firstly, you need to get the pods up and running by applying the replication controller.

$ kubectl apply -f basic-pod-rc.yaml

Here, basic-pod-rc.yaml is the name of the YAML file.

Next, you need to create the service.

$ kubectl apply -f basic-service.yaml

You can now check the status of the service by running the command kubectl get svc.

saurabhdashora@Saurabhs-MacBook-Air basic-pod-demo % kubectl get svc                    
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
basic-service   ClusterIP   10.109.67.193    <none>        80/TCP           3s

The interesting property here is the CLUSTER-IP. This is the IP address assigned to the service within the cluster. You cannot access it from outside the cluster as yet, but pods within the cluster can access it.

To see that in action, we can use one of the pods that was created. Execute kubectl get pods to get the list of pods.

NAME             READY   STATUS    RESTARTS   AGE
basic-rc-jsm5p   1/1     Running   0          2m19s
basic-rc-vsn7f   1/1     Running   0          2m19s
basic-rc-wjf44   1/1     Running   0          2m19s

Next, execute a curl request from within one of the pods. You will get the response from the Node.js application.

$ kubectl exec basic-rc-jsm5p -- curl -s http://10.109.67.193
Hello World from our Kubernetes Pod Demo

You might face issue while executing curl because the node:alpine image does not contain curl by default.

However, you can install it using the below command.

$ kubectl exec basic-rc-jsm5p -- apk --no-cache add curl

Alternatively, you can also install the curl package within the Docker image itself but for demo purpose, I installed it directly within the pod.

Conclusion

That’s all for this post.

This post was meant to get you a basic understanding of services in Kubernetes and how you can get one up and running in hardly any time.

Now there’s a lot more to Kubernetes services. Here are a few other posts that might interest you:

If you have any queries or comments, please mention in the comments section below.

Categories: BlogKubernetes

Saurabh Dashora

Saurabh is a Software Architect with over 12 years of experience. He has worked on large-scale distributed systems across various domains and organizations. He is also a passionate Technical Writer and loves sharing knowledge in the community.

0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *