Monday 13 May 2024

Kubernetes ClusterIP Service

This article extends my notes from an Udemy course "Kubernetes for the Absolute Beginners - Hands-on". All course content rights belong to course creators. 

The previous article in the series was Kubernetes NodePort Service | My Public Notepad.


A full stack web application typically has different kinds of pods hosting different parts of an application, for example:
  • pods running a frontend web server; their IPs could be e.g. 10.244.0.2, 10.244.0.3, 10.244.0.4 
  • pods running a backend server with IPs 10.244.0.5, 10.244.0.6, 10.244.0.7
  • pods running a key-value store / in-memory DB (e.g. Redis) with IPs e.g. 10.244.0.8, 10.244.0.9, 10.244.0.10
  • pods running a persistent database (e.g. MySQL)
Frontend servers needs to communicate to the backend servers which in turn need to communicate to the database as well as the Redis services etc. How to establish connectivity, within the cluster, between these services or tiers of this web application?

The pods all have an IP address assigned to them, but these IPs are not static. These pods can go down any time and new pods are created all the time. We cannot rely on these IP addresses for internal communication between the application tiers.

Also, what if the first front end pod at 10.244.0.2 need to connect to a backend service? Which of the three would it go to and who makes that decision?

Kubernetes service named ClusterIP:
  • groups the pods together
  • provides a single interface to access the pods in a group
  • provides cross-service connectivity within the cluster
  • can't be used to expose services to the outside world

For example, a service created for the backend pods will help group all the backend pods together and provide a single interface for other parts to access this service. The requests are forwarded to one of the pods under the service randomly. Similarly, we can create additional service for Redis and allow the backend pods to access the Redis systems through this service. 

This enables us to easily and effectively deploy a microservices-based application on Kubernetes cluster. Each layer can now scale or move as required without impacting communication between the various services. 

Each service gets an IP and name assigned to it inside the cluster, and that is the name that should be used by other paths to access the service. Example: in https://github.com/BojanKomazec/kubernetes-demo/blob/main/minikube/php-fmp-nginx-demo/default.conf we have in Nginx configuration:

fastcgi_pass php-fpm:9000;

...where php-fpm is the name of the ClusterIP service we defined for PHP-FPM pod (PHP-FPM does not need to be exposed to external world, only Nginx running in another pod needs to have access to it).


How to create a ClusterIP service?


We need to provide a definition file:

clusterip-service-definition.yaml:

apiVersion: v1
kind: Service
metadata:
  name: back-end
spec:
  type: ClusterIP
  ports:
    - targetPort: 80
      port: 80
  selector:
    app: myapp
    type: back-end

Another example:

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  type: ClusterIP
  ports:
    - name: web
      port: 80
  selector:
    app: nginx


ClusterIP is the default type for Service kind. Even if we don't specify type, it will be assumed the type to be ClusterIP.

targetPort (optional) is the port where the backend is exposed. It's not required to declare targetPort in the Service definition. If we omit it, it defaults to the value we declared for port (which is the port of the Service itself).

port is where the service is exposed and it is the same one as targePort.

To link the service to a set of pods we use selector. We can check the pod definition file and copy the labels from it and move it under selector.


To create the service:

$ kubectl create -f ./minikube/service/clusterip-service-definition.yaml 
service/back-end created

To check its status:

$ kubectl get svc
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
back-end        ClusterIP   10.111.52.100    <none>        80/TCP         12s
kubernetes      ClusterIP   10.96.0.1        <none>        443/TCP        25d
myapp-service   NodePort    10.104.148.160   <none>        80:30008/TCP   37h


The service can be accessed by other parts using the cluster IP or the service name.

If we run the same command before any our custom service is created, we'll see that there is already one service created implicitly:

$ kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.43.0.1    <none>        443/TCP   14m

That is a default service created by Kubernetes at launch.

$ kubectl describe service kubernetes
Name:              kubernetes
Namespace:         default
Labels:            component=apiserver
                   provider=kubernetes
Annotations:       <none>
Selector:          <none>
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.43.0.1
IPs:               10.43.0.1
Port:              https  443/TCP
TargetPort:        6443/TCP
Endpoints:         192.40.199.6:6443
Session Affinity:  None
Events:            <none>

This is a ClusterIP service with one endpoint attached (one pod with IP 192.40.199.6). 

In general, if there were multiple pods that this service is directing traffic to, we'd have here multiple IP addresses, for each pod. Endpoints are basically pods that are filtered based on the selector's labels. 
---

No comments: