Monolith vs Microservices
A monolithic architecture is a choice, and a valid one at that. It may not be the right choice in all circumstances, any more than microservices are—but it’s a choice nonetheless.
Let me start with an example. Imagine if we wanted to pick a machine among a list of machines that is best suited to run a CPU-heavy process. It is actually a very common problem you might find in your professional life.
First of all we would like to create a list of all the candidate machines. In true Unix fashion, we will write this list onto a file.
We can read this file line-by-line, maybe using a while loop, where each line will be one of the servers.
The command to find the load on a machine is uptime.
14:35:25 up 23:42, 0 users, load average: 0.07, 0.02, 0.00
We can run this command on all the machines and get their load. We can use ssh destination [command] for this purpose. From that we can pick the subset of machines where the load is minimum.
The whole thing will look something like this:
top_machine=$(while read machine; do load=$(ssh $machine uptime|sed -e 's/.*load average: \([^,]*\),.*/\1/'); done < list|sort|head -n 1)
ssh $top_machine my_command
In this subset of machines that are not under load, we want to pick the machine with the best CPU. We can use cat /proc/cpuinfo for this purpose. We can grep for the field that interests us the most, or we can take a more complex approach by assigning a score based on values of different fields or even take into account the RAM speed and the speed of the mounted disk etc., and then sort the score and pick the best.
This one-liner script stops at CPU but it demonstrates that using the power inherent in the philosophy of “do one thing and do it properly” behind Unix tools we can create a very tightly coupled thing that does what we want.
Now let us look at the maintainability of the above one-liner. Is it maintainable? Yes. It is cohesive, so it can be understood in its entirety. Because it is terse, it can be understood by a newbie. Small changes can also be accommodated easily without rewriting the whole thing. A little bit of familiarity with shell scripting and you are good to go.
It is reliable? No. This script will fail to get load of a machine that has a different format of output of uptime which may break the regex used by sed. Or it can be a machine which doesn’t have uptime. Or it can be a machine where you don’t have ssh access (anymore). It also depends on a file called list that should be manually populated with the hostname of all the candidate machines. In fact, maintaining that list of machines is probably the biggest problem we will have to face once we start scaling upwards.
So, it is imperative that we de-couple the different parts of the problem. For example, we can write a program that queries all the machines on our network for their load and writes it somewhere. We can run this problem periodically as a cronjob and then we can simply look at the best machine from the list whenever we want.
Also, instead of relying on ssh and uptime, we can write a simple server that publishes the load of the machine on some specific port.
If we have gone with the more complicated scoring approach, we can further decouple the scoring part into another program so that our querying program doesn’t get blocked if the scoring part becomes problematic such as requiring a lot of time to calculate the score or not being able to accurately calculate score when one of the metrics about the machine is unavailable.
At least 1 of the above-mentioned programs will be running on a remote machine, so we can safely say that what we are looking at is microservice architecture. REST has become kind of a standard for machines to talk over the network, but it is not mandatory.
We saw how reliability can be an issue in scalability and how microservice architecture can solve this problem. But wait – do we have another option?
It turns out, we do! We can simply let the machines do everything themselves including calculating the score for themselves and then have them store the scores in a file on an NFS-mounted directory. By moving the process of relying on standardization from REST to a machine that can mount NFS we avoid any direct communication between the “services” altogether. Neat!
So I would ask the reader to think if you need microservices – or are you just increasing your cost and complexity?
Further reading: MonolithFirst