Sticky sessions for the little ones [Часть 2]… Or how to understand kubernetes and be filled with your knowledge

Sticky sessions (Sticky-session) Is a special kind of load balancing in which traffic goes to one specific server in a group. Typically, a load balancer (Nginx, HAProxy), which sets the rules for distributing traffic to available servers.

In the first part of the series, we have already covered how to create sticky sessions using Nginx… In the second part, we will analyze the creation of such a balancing by means Kubernetes

Since the articles are mainly aimed at beginners, you will have to touch on the basics kubernetes… Yes, yes, I know the Internet is full of material for studying the cube. But there will be a minimum of stuffy theory and a maximum of practice. It’s better to deploy a test application to the cluster once and understand the basics than to read a ton of boring tutorials.

Who just wants to know about the implementation of a sticky session in a cube – poke it here.

Minimum a cube cluster consists of two nodes (Node) – master and worker. Nodes in a cluster are the machines (virtual machines, physical servers) that run your applications. Master the node is the brain of the kubernetes, where the entire cluster is managed. Applications, as a rule, are not deployed on the master node (this can of course be done, but this is a special case and is beyond the scope of this article). But on worker application nodes and are deployed. For educational purposes, it is not very convenient to have two cars and put a cube there. Therefore, they came up with minikube

Minikube is a single-node cluster, which is both a master and a worker node and all this on the local machine. It has some simplifications compared to a real cube, such as installing an ingress-controller or launching a board. The tool is needed mainly for local development and training.

All examples will be for macOS. For installation for other systems, see the official documentation

brew install minikube

To start minikube run the command:

minikube start

You can work with the cube through the command line, for this you need to install kubectl.

minikube kubectl --get po -A

This utility put and to interact with a real cube. To view a dashboard with graphs and pictures, run:

minikube dashboard

Just keep in mind that the command will block your console, so it’s better to open another window.

This completes the installation of the tutorial cluster. As for me, to understand the basics kubernetes only 4 resources need to be parsed:

  1. Pod

  2. Deployment

  3. Service

  4. Ingress

In this order, we will consider each resource as the application is launched in the cluster.

Preparing the application

We will use the code from the previous article.

from fastapi import FastAPI
from uuid import uuid4

app = FastAPI()
uuid = uuid4()


@app.get("https://habr.com/")
async def root():
    return {'uuid': uuid}

Here variable uuid initialized along with FastAPI application. The variable will live as long as the server is running. Actually, by the value of this variable, we will know for sure that we got to the same instance of the application.

Putting together the image:

docker build -t mopckou/sticky-session:0.0.5 .

After building, I pushed the image to my public docker hub repository:

docker login
docker push mopckou/sticky-session:0.0.5

That’s it, we are ready to deploy the application. To do this, you need to specify a set of rules for the cube that will be followed when starting your application. These rules are called deployment configuration (Deployments).

Usually any resource in Kubernetes described in yaml files. In our case, there are not many deployment rules, so we’ll get by with a short command to create an object deployment

kubectl create deployment sticky-d --image=mopckou/sticky-session:0.0.5

IN -–Image you can specify your application, which has been uploaded to the docker hub public repository.

When creating a deployment resource, a Pod is also created on the node. A pod is the smallest cube unit in which a container or multiple docker containers are run. Deployment monitors the status and health of the pod. If one fails, then it will promptly launch a new one.

Let’s check that two resources have been created deployment and pod

kubectl get deployment,pods -l app=sticky-d
About “kubectl get”

kubctl pretty user friendly, for example in the command get you can ask for both pod and pods, deployment or deployments, etc. You can separate it with commas (without space) list the resources we want to get.

We scale our application to two services.

kubectl scale deployment sticky-d --replicas=2

And check the status again pods and deployment, the output should be something like this:

The new pod started to launch.

experimenting with deployment

As soon as the two pods are launched, you can visually demonstrate the work deployment

We will remove the bottom by hand pod / sticky-d-57444787d8-rdn6q:

Again, ask for the current state of the pods and deployment:

Didn’t have time to go * –rdn6q retire, a new one has come in its place * -6gjmv

Now let’s send requests for deployed applications. It will not be possible to do this directly, for this they came up with a service (Service).

Service (Service) in Kubernetes is an abstract object (resource) that provides access to running pods. Although each pod has a unique IP address, these IP addresses are not available outside the cluster without using the service. Services allow applications to receive traffic and are a standard pod load balancer.

Services can be opened differently, depending on the specified type field:

  • ClusterIP (default) – provides access to the service using the internal IP address in the cluster. This type makes the service available only within the cluster.

  • NodePort – Opens a service on the same port of every selected node in the cluster using NAT. Makes a service available outside the cluster using NodeIP: NodePort.

  • LoadBalancer – Creates an external load balancer in the current cloud (if supported) and assigns a fixed external IP address for the service.

  • ExternalName – opens access to the service with the specified name (defined in the externalName field in the specification) and returns a CNAME record. No proxy is used. This type requires kube-dns version 1.7 or higher.

Let’s create a service with the type Loadbalancer… Balancing occurs according to the principle random balancing (you can read about this in this article, which links here).

kubectl expose deployment sticky-d --type=LoadBalancer --port=8080
kubectl get services sticky-d

You can start the service with the command:

minikube service sticky-d

The output will be something like this:

We will send requests for the service and make sure that traffic goes to different pods:

The balancer doesn’t work? Or did we achieve our goal and got sticky sessions? Nearly!

We got a sticky smoker session. The point is, as long as a TCP connection to the server is open, the cube directs traffic to only one specific pod. But if there is a break in the connection or we create a new connection, then we can get to another pod. For more on this, see this excellent article. Gotham needs less sticky sessions now. To bypass this feature, you must uncheck the box. keep-alive at Postman.

Let’s see what happens:

Hooray! We made sure that the traffic is balanced.

Resource service a rather poor load balancer and knows how to distribute traffic for a specific deployment. Here comes ingresswhich greatly expands the balancing capabilities in kubernetes.

If you just create an ingress resource, nothing happens. We need to create an ingress-controller. To activate ingress-controller run:

minikube addons enable ingress
problems activating ingress-controller?

For macOS and Windows, at the time of this writing, an error may occur, to fix this, you need to run the following commands:

minikube config set vm-driver hyperkit
minikube delete
minikube start
minikube addons enable ingress

This will erase all deployments and pods. Go back and redeploy the application and start the service.

If the Ingress-controller is successfully launched, a special pod will start, run the command:

kubectl get pods -n kube-system

The result should be something like this:

Details on simple examples why you need ingress

Ingress allows you to balance traffic based on hostname or path in the request. Let’s look at a simple example. Let’s say we have two applications app1 and app2… After we bought a domain name, let it be awesome-company.com And we want our services to be available at:

  • awesome-company.com/app1

  • awesome-company.com/app2

Or even like this:

  • app1awesome-company.com

  • app2awesome-company.com

You can also do a combination of the two.

Resource ingress just allows you to do this. Ingress balances traffic to different services, being such an add-on over them.

In a nutshell, the above scheme is performed as follows. When creating an ingress resource, an external load balancer (if you are using GKE or AWS) issues an external IP address to the resource (let’s say 1.1.1.1). In this case, Ingress becomes the entry point to the cluster on ports 80 and 443. Resolving domain names app1awesome-company.com and app2awesome-company.com on the ip – 1.1.1.1. In the resource itself ingress we indicate that requests with the host app1awesome-company.com proxied on the service app1, and, accordingly, requests with the host app2awesome-company.com proxied on the service app2.

Now let’s create a resource Ingress… Make a new file ingress.yml and paste the following text:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
spec:
  rules:
    - host: sticky.info
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: sticky-d
                port:
                  number: 8080

Pay attention to the line host: sticky.info Need locally to define domain name and assign it the ip address of the ingress resource. Before defining the local domain name, let’s start the ingress resource.

kubectl apply -f ingress.yml

Let’s check if it has appeared:

kubectl get ingresses

Pay attention to the resource address if you make a request for 192.168.64.3:80 there will be a mistake 404

“Resolve” the local domain name. Open the file / etc / hosts and in the last line write down the ip address separated by a space ingress resource and domain name of the service.

And now let’s try to send a request to the address sticky.info:

It should be noted that balancing works despite the enabled checkbox. connection

small hack

You can cheat and not define a local domain name. Just specify the host in the request header:

Now let’s finally make our traffic sticky. Add to Ingress.yml annotations with 4 parameters:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
    nginx.ingress.kubernetes.io/affinity: "cookie"
    nginx.ingress.kubernetes.io/affinity-mode: "balanced"
    nginx.ingress.kubernetes.io/session-cookie-name: "key"
    nginx.ingress.kubernetes.io/session-cookie-max-age: 60
spec:
  rules:
    - host: sticky.info
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: sticky-d
                port:
                  number: 8080

Let’s consider each parameter in more detail:

  • affinity: set the value “cookie” to enable sticky sessions;

  • affinity-mode: sticking mode. Two modes are supported – persistent and balanced… If the value is balanced, traffic will stick to a specific pod only for a certain period of time. Specify persistent for permanent adhesion;

  • session-cookie-name: the name of the cookie, by the value of which the ingress-controller will associate traffic with a specific pod;

  • session-cookie-max-age: time for which traffic sticks to the pod, in seconds (setting is needed if affinity-mode = balanced). After the specified time has elapsed, the ingress-controller will generate a new value for the session-cookie-name and insert it into our cookie.

Let’s apply the changes:

kubectl apply -f ingress.yml

And let’s check the work of sticky sessions in a cube:

Responses will only come from one specific instance of the application. note that ingress-controller he himself substituted a value for us in the cookie for key and added a sticking shelf life. After 60 seconds nginx-controller will generate the value again for key, and the traffic will already stick to another pod (but not the fact that to another).

Now let’s see how it looks in the code:

import asyncio
from aiohttp import ClientSession
from asyncio import sleep


async def foo():
    session = ClientSession()

    while 1:
        response = await session.get('http://sticky.info')
        uuid = (await response.json())['uuid']

        print(f"answer: {uuid} /// cookie: {response.cookies}")

        await sleep(1)


lp = asyncio.get_event_loop()
lp.run_until_complete(foo())
lp.close()

In a loop, we make a request to the application and display the request result and the cookie object.

Ingress-controller generated key and substituted an object for us in the cookie Set-Cookie… Traffic stuck for 60 seconds on a specific pod.

Done! We have successfully implemented sticky sessions in Nginx and Kubernetes

And finally, the results of the social. a survey among IT companies about the need for Kubernetes.

Similar Posts

Leave a Reply

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