Microservices

Microservices contract and versioning

What is it? Why is it important?

This is a series of posts I’m doing about designing microservices. Keep posted I will link them up when they are all ready!

When you are designing a microservices architecture, whether you are using a REST or messaging approach for communication between microservices, you have to design the APIs/messages and how a microservice will interact with each other.

One of the most important aspects of the microservices architectures is the ability to work on and deploy a microservice, completely independent of each other. To achieve this, each microservice must provide a well-defined, versioned contract.

The microservices contract

A microservice contract is between the service and its clients. The main goal is that you can make changes to the service without affecting the clients, nevertheless if the clients are aware of the service changes or not.

Even if you spent a lot of time the first time, designing the initial contract, for certain, the API will need to change over time.

When the time comes to update an API, it is important to understand the difference between breaking and non-breaking changes, when a major release is required and when to dispose of an old version.

When the changes are small, for example, adding a new parameter to the API, if that parameter isn’t business-critical, the clients should be able to consume the API in the old way, without sending or expecting to receive that parameter, and the server should fill the blanks with default values.

However, if you are doing a major, backward-incompatible, change to the API, you will need to maintain the old version for some time because you as a service cannot force your clients to update immediately.
If you are using a REST approach, one way is to add a versioning number in the path, for example /app/v1/service, app/v2/service. This way you can have two or more versions of your microservice available.

That is key to understand, if you are not doing a breaking change, there is no need for a new version of your contract.

Resource management strategy for docker containers on Kubernetes (nodejs + express)

It seems simple, but it is not.

How to properly assign resources requirements and limits to a HTTP dockerized microservice running on kubernetes?

Good question, right?

Well, as you can imagine there is not a single answer to it. But there is a strategy you can follow. This is not a “book definition” what I mean with this is that there might be another way of sizing resource requirements, but, so far, I’ve been using this method successfully.

It is a delicate balance between the hardware size on which your containers will run (ie: k8s nodes) and how many requests the container itself can handle.

The idea is to find that delicate balance so our pods can be scheduled on the nodes, without wasting precious resources that can be used for other workloads.

So, how do we do that?

A simple method:

Let’s take a simple microservice as an example: a nodejs express REST API which talks to a MongoDB.
We will be doing: GET /example

Run the container on your local machine or a k8s cluster, measure the idle resource usage:

  • CPU: 1%
  • Memory: 80Mb


Using a load testing app like Apache JMeter, we fire up 100 concurrent requests in 1 second… measure the resource spike using docker stats or similar:

  • CPU: 140%
  • Memory: 145Mb

We exceeded one CPU core, and that is not good, so we reduce the number of parallel requests to 70

  • CPU: 93%
  • Memory: 135Mb

Now, we have something to work with. We can assume that a single instance of our app, can handle up to 70 requests per second.

Setting Kubernetes resources:

According to how resource limits and requests are set in Kubernetes, we can say that it is safe to consider that our resource requests can be:

  • CPU: 0.1
  • Memory: 100Mb

And we can limit the resources to:

  • CPU: 1
  • Memory: 150Mb

That will ensure that we can handle at least, 70 requests per second, per replica.

Disclaimer: as I said before, this is a simple approach, we are not considering several things, for example, node resources, MongoDB capacity…