If you are here, you must have seen that penguin before. You know what I’m talking about. The one that is mostly known by computer nerds and professionals. But in fact, it’s used by many many more.
* Say hi to Tux :)
So that little penguin is really in our life isn’t it. Our smartphones have that penguin, some of our cars have that penguin, most of the everyday applications and services that we use are run by that little penguin. Thus it shouldn’t be surprised if I told you some of the traffic junctions that you passed by might have that penguin too ;)
If something is used within that much variety of ways and places, it needs to be flexible and customizable, if not it wouldn’t fit in each and every one of our hugely diverse needs for solving real-life problems. Therefore the penguin, a.k.a. Linux provides exactly that.
Now let’s examine a scenario where we started a project about collecting temperature and humidity data from a road to alert the drivers in case of dangerous combinations of high humidity and low temperature. Initially, all we need is a couple of sensors and a board to run them on. The goal is acquiring data from those sensors. At this stage when we are done, our application looks something like this:
Fig.1 Initial Project
But over time we decide to add new features to this application like:
Analyzing the data that the sensors gathered with some machine learning algorithms trying to figure out if there would be ice on the road.
Displaying some information about the system with a user interface. Like current temperature and humidity in real-time. Also maybe our algorithm’s status about the possibility of road icing in a calendar.
Sending commands to the system over that user interface for adjusting the sensor thresholds and sampling rates.
A power-saving feature based on defined thresholds for sensors. Such as: if the temperature goes higher than 5 °C reduce the sampling rate automatically. Or even shutting down the whole operation when unnecessary.
Hence, we end up with something really big and complex. Which can be depicted like the following:
Fig.2 Enhanced Project
At this point, we start to look for some maintenance methods for this huge application because we cannot even properly debug what’s going on with this complex project of ours. Back then, following what is what and where we are at during the runtime, wasn’t that much of a problem. Because we only did one thing. But now we added multiple capabilities to the system, each one having its own identity but they are monolithic. We need to separate them up. So exactly at this moment, that little penguin we mentioned before comes to our aid with a lot of tools and features that we can benefit from. Such as systemd for service configuration and management operations, UNIX sockets and dbus for inter-process communication, etc. That means, we can run our features as separate services using these tools. An example systemd service file looks like this:
[Unit] Description=About Roads Server ConditionPathIsDirectory=/etc/about-roads/binaries[Service] Type=dbus BusName=org.about.roads.httpserver ExecStart=/etc/about-roads/binaries/httpserver Restart=on-failure LimitNPROC=1 ProtectHome=true ProtectSystem=full[Install] WantedBy=multi-user.target Alias=org.about.roads.httpserver.service
After all those adventures, now we can build our system based on
micro-services. The end product that we deploy to our Linux ecosystem should be constructed by multiple pieces of software. Which are interconnected in a way but they should operate as separately as possible. If we wanted to describe this type of architecture, the following diagram can be helpful:
Why we create a way more complicated system than a simple and monolithic application? Why is the need of that complexity? What are the advantages? Firstly we get rid of those never-ending debug sessions. If this complexity on design level wasn’t introduced, the penalty for debugging errors would be cumbersome. Because, highly coupled series of instructions begin executed most probably on different threads can cause you unimaginable headaches. Even if you spotted the error quickly, testing your fix might take too long due to other possible break downs on somewhere else in your code. Secondly, our loosely coupled services can now work without caring about the state of each other. For example; if our server stopped sending data due to a runtime error and crashed, our sensors can still collect information regardless of the server’s state. Or the user interface can continue to display error messages even if there are inactive applications. Lastly, we end up with a system that is up and running with multiple micro-services and relying on the popular Unix philosophy of the output of an application, being the input of another one.
* Tux was originally made by Larry Ewing in the Gimp, re-illustrated in vector by Garrett LeSage, using Inkscape and refined and cleaned up by IFo Hancroft.
About Egemen Türkyılmaz
Egemen is a tech junkie who enjoys creating, learning about newer technologies, and experience them if possible. Besides him being a borderline audiophile, he is really interested in cinema, basketball, and anything related to middle-earth :)