This is part one of a two-part series that aims to shed some light and provide practical exposure on key topics in the modern software industry, namely cloud native applications. This post covers micro-services, containers and serverless applications. Here we’ll discuss practical advantages and disadvantages of the technologies covered.
The micro-services architecture has gained reputation as a powerful approach to architect modern software applications. So what are micro-services? Micro-services can be described as simply the idea of separating the functionality required from a software application into multiple independent small software services or “micro-services.” Each micro-service is responsible for an individual focused task. In order for microservices to collaborate together to form a large scalable application, they communicate and exchange data.
Micro-services were born out of the need to tame the complexity, and inflexibility of monolithic applications. A monolithic application is a type of application, where all required functionality is coded together into the same service. For example, here is a diagram representing a monolithic events (like concerts, shows, etc.) booking application that takes care of the booking payment processing and event reservation:
The application can be used by a customer to book a concert or a show. A user interface will be needed. Furthermore, we will also need a search functionality to look for events, a bookings handler to process the user booking then save it, and an events handler to help find the event, ensure it has seats available, then link it to the booking. In a production level application, more tasks will be needed like payment processing for example, but for now let’s focus on the four tasks outlined in the above figure.
This monolithic application will work well with small to medium load. It will run on a single server, connect to a single database and will be written probably in the same programming language.
Now, what will happen if the business grows exponentially and hundreds of thousands or millions of users need to be handled and processed? Initially, the short term solution would be to ensure that the server where the application runs, has powerful hardware specifications to withstand higher loads, and if not then add more memory, storage, and processing power to the server. This is called vertical scaling, which is the act of increasing the power of the hardware like RAM and hard drive capacity to run heavy applications.However, this is typically not sustainable in the long run as the load on the application continues to grow.
The micro-services version of the events application will take the below form:
This application will be capable of scaling among multiple servers, a practice known as horizontal scaling. Each service can be deployed on a different server with dedicated resources or in separate containers (more on that later). The different services can be written in different programming languages enabling greater flexibility, and different dedicated teams can focus on different services achieving more overall quality for the application.
Another notable advantage of using micro-services is the ease of continuous delivery, which is the ability to deploy software often, and at any time. The reason why micro-services make continuous delivery easier is because a new feature deployed to one micro-services is less likely to affect other micro-services compared to monolithic applications.
Issues with micro-services
One notable drawback of relying heavily on micro-services is the fact that they can become too complicated to manage in the long run as they grow in numbers and scope. There are approaches to mitigate this by utilizing monitoring tools such as Prometheus to detect problems, container technologies such as Docker to avoid polluting the host environments and avoiding over designing the services. However, these approaches take effort and time.
Cloud native applications
Micro-service architectures are a natural fit for cloud native applications. A cloud native application is simply defined as an application built from the ground up for cloud computing architectures. This simply means that our application is cloud native, if we design it as if it is expected to be deployed on a distributed, and scalable infrastructure.
For example, building an application with a redundant micro-services architecture -we’ll see an example shortly- makes the application cloud native, since this architecture allows our application to be deployed in a distributed manner that allows it to be scalable and almost always available. A cloud native application does not need to always be deployed to a public cloud like AWS, we can deploy it to our own distributed cloud-like infrastructure instead if we have one.
In fact, what makes an application fully cloud native is beyond just using micro-services. Your application should employ continuous delivery, which is your ability to continuously deliver updates to your production applications without disruptions. Your application should also make use of services like message queues and technologies like containers, and serverless (containers and serverless are important topics for modern software architectures, so we’ll be discussing them in the next few sections).
Cloud native applications assume access to numerous server nodes, having access to pre-deployed software services like message queues or load balancers, ease of integration with continuous delivery services, among other things.
If you deploy your cloud native application to a commercial cloud like AWS or Azure, your application gets the option to utilize cloud only software services. For example, DynamoDB is a powerful database engine that can only be used on Amazon Web Services for production applications. Another example is the DocumentDB database in Azure. There are also cloud only message queues such as Amazon Simple Queue Service (SQS), which can be used to allow communication between micro-services in the Amazon Web Services cloud.
As mentioned earlier, cloud native micro-services should be designed to allow redundancy between services. If we take the events booking application as an example, the application will look like this:
Multiple server nodes would be allocated per micro-service, allowing a redundant micro-services architecture to be deployed. If the primary node or service fails for any reason, the secondary can take over ensuring lasting reliability and availability for cloud native applications. This availability is vital for fault intolerant applications such as e-commerce platforms, where downtime translates into large amounts of lost revenue.
Cloud native applications provide great value for developers, enterprises, and startups.
A notable tool worth mentioning in the world of micro-services and cloud computing is Prometheus. Prometheus is an open source system monitoring and alerting tool that can be used to monitor complex micro-services architectures and alert when an action needs to be taken. Prometheus was originally created by SoundCloud to monitor their systems, but then grew to become an independent project. The project is now a part of the cloud native computing foundation, which is a foundation tasked with building a sustainable ecosystem for cloud native applications.
Cloud native limitations
For cloud native applications, you’ll face some challenges if the need arises to migrate some or all of the applications. That is due to multiple reasons, depending on where your application is deployed.
For example,if your cloud native application is deployed on a public cloud like AWS, cloud native APIs are not cross cloud platform. So, a DynamoDB database API utilized in an application will only work on AWS but not on Azure, since DynamoDB belongs exclusively to AWS. The API will also never work in a local environment because DynamoDB can only be utilized in AWS in production.
Another reason is because there are some assumptions made when some cloud native applications are built, like the fact that there will be virtually unlimited number of server nodes to utilize when needed and that a new server node can be made available very quickly. These assumptions are sometimes hard to guarantee in a local data center environment, where real servers, networking hardware, and wiring need to be purchased.
This brings us to the end of Part 1 of this whitepaper. Check out Part 2 tomorrow to learn about Containers and Serverless applications along with their practical advantages and limitations.
About the author
This is part one of a two-part post by Mina Andrawos, an experienced engineer who has developed deep experience in the Go language, and modern software architectures. He regularly writes articles and tutorials about the Go language, and also shares open source projects. Andrawos is the author of Cloud Native programming with Golang, which provides practical techniques, code examples, and architectural patterns required to build cloud native microservices in the Go language. He’s also the author of the Mastering Go Programming, and the Modern Golang Programming video courses.