How do I call another kubernetes service inside the same cluster

Photo by Jay Gomez on Unsplash

How do I call another kubernetes service inside the same cluster

Let's say you have a nodejs backend deployed in Kubernetes, and wanted to call another service inside your Kubernetes cluster. How do you do it? (Skip to answer by scrolling down to the bottom) Read more about DNS in kubernetes here: https://basantakharel.com/dns-in-kubernetes-more-than-just-name-resolution

const axios = require('axios');
//DONOT use in production without error handling
const {data} = await axios.get('some-service');

Assuming you have some knowledge of Kubernetes(K8s), you know that you need a deployment and a service

To give basic info here is some description of deployment and service

Deployment

A Deployment provides declarative updates for Pods and ReplicaSets.

You describe a desired state in a Deployment, and the Deployment Controller changes the actual state to the desired state at a controlled rate. You can define Deployments to create new ReplicaSets or to remove existing Deployments and adopt all their resources with new Deployments.

You don't want to be creating pods manually or even replica sets but use deployment which handles rollout/rollback of your pods(not useful if you have only one replica as in the example below but good practice nonetheless)

Check out the K8s deployment official documentation for more info

And here is a sample YAML file for a deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: some-service-dpl
spec:
  replicas: 1
  selector:
    matchLabels:
      app: some-service-dpl
  template:
    metadata:
      labels:
        app: some-service-dpl
    spec:
      containers:
      - name: some-service-dpl
        image: yourdockerregistry/some-service-dpl-image
        ports:
        - containerPort: 8080

Service

A Kubernetes Service is an abstraction that defines a logical set of Pods running somewhere in your cluster, that all provide the same functionality. When created, each Service is assigned a unique IP address (also called clusterIP). This address is tied to the lifespan of the Service, and will not change while the Service is alive.

Service by default is of type ClusterIP which exposes the service on cluster-internal IP which will not change while the service is alive. But it will if it is killed and a new one spins up so an IP address is not a full proof way to talk to a service

Check out the K8s service official documentation for more info

Here is YAML file for a service that is exposing the previous deployment

kind: Service
apiVersion: v1
metadata:
  name: some-service-svc
spec:
  selector:
    app: some-service-svc
  ports:
    - name: some-service-api
      targetPort: 8080
      port: 8080
      protocol: TCP

So how do we talk to this service backend? The whole point of creating the service was to get a stable IP for the pods managed by replica sets managed by some-service-dpl. So we don't want to talk to the pods directly

We also don't want to talk to the service directly using an IP address which can be found by this simple kubectl command. Especially not if we are deploying both services at the same time and there is no way to know the IP address beforehand

kubectl get svc some-service-svc
NAME               TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)     AGE
some-service-svc   ClusterIP   10.0.15.149   <none>        8080/TCP    21s

What we do want to use is the name of the service itself and that's it!

This works because K8s uses DNS for services and pods and internally resolves some-service-svc:8080 to http://10.0.15.149:8080. Below you can see the fully qualified DNS name for a service

 {{service_name}}.{{namespace}}.svc.cluster.local
const axios = require('axios');
//DONOT use in production without error handling
const {data} = await axios.get('http://some-service-svc:8080');
//if the service was in some other namespace 
const {data} = await axios.get('http://some-service-svc.otherNamespace.svc.cluster.local:8080');

Just make sure that you keep track of the name of your service or add the service and port exposed by the service in your environment from config variables (from Helm values if you use Helm for example)