Site icon Vinsguru

Kubernetes Adapter Pattern

Overview:

In this tutorial, I would like to demonstrate the use of Kubernetes Adapter Pattern with a simple example.

Kubernetes Adapter Pattern:

Design Patterns are repeatable & reusable solutions for commonly occurring problems in the software/architectural design and they encourage the developers to design a highly cohesive and loosely coupled applications. Design patterns can be used for the infrastructure/deployment design as well in this modern Microservices era!

Lets consider an application in our Kubernetes cluster. One of the APIs of the application is not compatible with a client which is trying to consume the API.

Kubernetes Adapter Pattern allows us to run a sidecar container along with main application container to convert the API as the client expects without any application code change!

This adapter pattern is a special case of Kubernetes Sidecar Pattern which we had discussed here.

Sample Application:

Let’s consider a simple Microservice for cars in which we get information about a car model based on the given id. The application is successfully deployed and all the clients which are consuming this API are happy except one!

{
   "id":1,
   "make":"honda",
   "model": "civic",
   "topSpeed" : 60
}

The unhappy client realized that the topSpeed in the above response is in miles/hour. But the client expectation was to get that information in the kilometers/hour. The Microservice developers are afraid that they do not want to make a change in the code just for 1 client. [This is a simple example to explain the issue. But hopefully you get the idea].

Let’s see how we could make the client happy with Kubernetes Adapter Pattern.

Car Service – Application Container:

{
   "cars":[
      {
         "id":1,
         "make":"honda",
         "model": "civic",
         "topSpeed" : 60
      },
      {
         "id":2,
         "make":"honda",
         "model": "accord",
         "topSpeed" : 80
      },
      {
         "id":3,
         "make":"nissan",
         "model": "370z",
         "topSpeed" : 100
      }
   ]
}
kubectl create configmap carsdb --from-file=db.json
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: car-service
  name: car-service
spec:
  replicas: 1
  selector:
    matchLabels:
      app: car-service
  template:
    metadata:
      labels:
        app: car-service
    spec:
      containers:
      - image: clue/json-server
        name: car-service
        ports:
        - containerPort: 80
        volumeMounts:
        - name: db
          mountPath: /data/
      volumes:
      - name: db
        configMap:
          name: carsdb
apiVersion: v1
kind: Service
metadata:
  labels:
    app: car-service
  name: car-service
spec:
  ports:
  - name: 8080-80
    port: 8080
    protocol: TCP
    targetPort: 80
  selector:
    app: car-service
  type: LoadBalancer

Kubernetes Adapter Pattern Implementation:

Now let’s work on the 1 specific client requirements of converting the topSpeed to km/h unit without touching the main car-service app. We are going to achieve that by attaching nginx to the main car-service app as a sidecar.

upstream backend  {
  server localhost;
}

server {

    listen 8080;

    default_type application/json;

    location /cars_backend {
        internal;
        proxy_pass http://backend$request_uri;
        proxy_redirect off;
    }

    location /cars {

        content_by_lua_block {

            -- forward to backend
            local a = ngx.location.capture("/cars_backend")

            -- convert to diff unit
            local func = function (v)
                return '"topSpeed": ' .. v[1] * 1.6;
            end

            -- find a match and replace
            local newstr, n, err = ngx.re.sub(a.body, '"topSpeed": ([0-9]+)', func)

            -- final response
            ngx.say(newstr)

        }
    }

}
kubectl create configmap nginxconf --from-file=default.conf
spec:
      containers:
      - image: openresty/openresty:alpine
        name: car-adapter
        ports:
        - containerPort: 8080
        volumeMounts:
        - name: conf
          mountPath: /etc/nginx/conf.d/     
      - image: clue/json-server
        name: car-service
        ports:
        - containerPort: 80
        volumeMounts:
        - name: db
          mountPath: /data/
      volumes:
      - name: db
        configMap:
          name: carsdb
      - name: conf
        configMap:
          name: nginxconf
apiVersion: v1
kind: Service
metadata:
  labels:
    app: car-service
  name: car-service
spec:
  ports:
  - name: 8080-80
    port: 8080
    protocol: TCP
    targetPort: 80
  - name: 8081-80
    port: 8081
    protocol: TCP
    targetPort: 8080
  selector:
    app: car-service
  type: LoadBalancer

Now we have included a special client requirement as a separate sidecar without modifying the original application.

By using the same uri patterns and using different port, we are able to change the response based on the requirements.

Summary:

We were able to successfully demonstrate the use of Kubernetes Adapter Pattern. We could modify the application response behavior by adding additional container as a sidecar based on the consumer requirements.

Read more about Kubernetes Cloud Design Patterns.

The source code is available here.

Happy learning 🙂

 

Share This:

Exit mobile version