For quite a while now, cloud-native has been one of the hottest topics in software development. Some developers just call it hype that will lose traction and disappear after some time. For others, it’s the future of software development.
Whatever the future will bring, cloud-native is currently one of the biggest trends in the software industry. It has already changed the way we think about developing, deploying and operating software products.
But what exactly is “cloud native”?
Cloud native is a lot more than just signing up with a cloud provider and using it to run your existing applications. It affects the design, implementation, deployment, and operation of your application.
Pivotal, the software company that offers the popular Spring framework and a cloud platform, describes cloud native as:
“Cloud native is an approach to building and running applications that fully exploit the advantages of the cloud computing model.”
The Cloud Native Computing Foundation, an organization that aims to create and drive the adoption of the cloud-native programming paradigm, defines cloud-native as:
“Cloud native computing uses an open source software stack to be:
- Containerized. Each part (applications, processes, etc) is packaged in its own container. This facilitates reproducibility, transparency, and resource isolation.
- Dynamically orchestrated. Containers are actively scheduled and managed to optimize resource utilization.
- Microservices-oriented. Applications are segmented into microservices. This significantly increases the overall agility and maintainability of applications.”
Both definitions are similar, but look at the topic from a slightly different perspective. You could summarize the definitions as:
“An approach that builds software applications as microservices and runs them on a containerized and dynamically orchestrated platform to utilize the advantages of the cloud computing model.”
Let’s be honest: “Utilize the advantages of the cloud computing model” sounds great but if you’re new to cloud-native computing, you’re probably still wondering what this is all about and how it affects the way you implement your software. That was at least how I felt when I read about cloud-native computing for the first time.
Let’s take a look at the different parts.
The basic idea of containers is to package your software with everything you need to execute it into one executable package, e.g., a Java VM, an application server, and the application itself. You then run this container in a virtualized environment and isolate the contained application from its environment.
The main benefit of this approach is that the application becomes independent of the environment and that the container is highly portable. You can easily run the same container on your development, test or production system. And if your application design supports horizontal scaling, you can start or stop multiple instances of a container to add or remove instances of your application based on the current user demand.
The Docker project is currently the most popular container implementation. It’s so popular that the terms Docker and container are often used interchangeably. But keep in mind that the Docker project is just one implementation of the container concept and could be replaced in the future.
If you want to give Docker a try, you should start with the free community edition. You can install it on your local desktop so that you can start building your own container definitions and deploy your first application into a container. And when you’re done, you can handover the container to a coworker who does quality assurance and deploys it to production afterward.
You no longer need to worry if your application will work in the test or production environment, or if you need to update some dependencies. The container contains everything your application needs, and you just need to start it.
The official documentation shows you how to run Docker on your system and provides a good getting started guide.
Deploying your application with all dependencies into a container is just the first step. It solves the deployment problems you had previously, but if you want to benefit from a cloud platform fully, you’re experiencing new challenges.
Starting additional or shutting down running application nodes based on the current load of your system isn’t that easy. You need to
- monitor your system,
- trigger the startup or shutdown of a container,
- make sure that all required configuration parameters are in place,
- balance the load between the active application instances
- share authentication secrets between your containers.
Doing all of that manually requires a lot of effort and is too slow to react to unexpected changes in system load. You need to have the right tools in place that automatically do all of this. This is what the different orchestration solutions are built for. A few popular ones are Docker Swarm, Kubernetes, Apache Mesos and Amazon’s ECS.
Now that we have all the infrastructure and management in place, it’s time to talk about the changes that cloud-native introduces to the architecture of your system. Cloud native applications are built as a system of microservices. I’m sure you’ve heard about that architectural approach already, and I also wrote a series of posts about it here on the blog.
The general idea of this architectural style is to implement a system of multiple, relatively small applications. These are called microservices. They work together to provide the overall functionality of your system. Each microservice realizes exactly one functionality, has a well-defined boundary and API, and gets developed and operated by a relatively small team.
This approach provides several benefits.
First of all, it’s a lot easier to implement and understand a smaller application that provides one functionality, instead of building a large application that does everything. That speeds up development and makes it a lot easier to adapt the service to changed or new requirements. You need to worry a lot less about unexpected side effects of a seemingly small change, and you can focus on the development task at hand.
It also allows you to scale more efficiently. When I talked about containers, I said that you could simply start another container to handle an increase in user requests. This is called horizontal scaling. You can basically do that with every stateless application, independent of its size. As long as the application doesn’t keep any state, you can send the next request of a user to any application instance available.
Even so, you can do that with a monolithic application or a system of microservices; it’s often a lot cheaper to scale a system of microservices. You just need to scale the microservice that gets a lot of load. As long as the rest of the system can handle the current load, you don’t need to add any additional instances of the other services.
You can’t do that with a monolith. If you need to increase the capacity of one feature, you need to start a new instance of the complete monolith. That might not seem like a big deal, but in a cloud environment, you pay for the usage of hardware resources. And even if you only use a small part of the monolith, you still need to acquire additional resources for the other, unused parts.
As you can see, microservices let you use the cloud resources more efficiently and reduce the monthly bill of your cloud provider.
As always, you don’t get the benefits of an architectural style for free. Microservices remove some complexity from the services themselves and provide better scalability, but you’re now building a distributed system. That adds a lot more complexity on the system level.
To keep this additional complexity as low as possible, you should try to avoid any dependencies between your microservices. If that’s not possible, you need to make sure that dependent services find each other, and implement their communication efficiently. You also need to handle slow or unavailable services so that they don’t affect the complete system. I got into more details about the communication between microservices in Communication Between Microservices: How to Avoid Common Problems.
The distributed nature of your system also makes it a lot harder to monitor and manage your system in production. Instead of a few monoliths, you now need to monitor a system of microservices, and for each service, there might be several instances that run in parallel. You need to monitor much more application instances as you did in the past. You can use tools like Retrace to collect the information from all systems.
You don’t need to use a specific framework or technology stack to build a microservice. But it makes it a lot easier. They provide you with lots of ready-to-use features that are well tested and have been used in production environments before.
As you might guess from its name, Spring Boot integrates the well-known Spring framework with several other frameworks and libraries to handle the additional challenges of the microservice architecture.
Eclipse Microprofile follows the same idea, but uses Java EE. Multiple Java EE application server vendors work together to provide a set of specifications and multiple, interchangeable implementations.
The ideas and concepts of cloud-native computing introduced a new way to implement complex, scalable systems. Even if you’re not hosting your application on a cloud platform, these new ideas will influence how you develop applications in the future.
Containers make it a lot easier to distribute an application. You can use them during your development process to share applications between team members, or to run them in different environments. And after all tests have been executed, you can easily deploy the same container to production.
Microservices provide a new way to structure your system. They introduce new challenges, but they also shift the attention to the design of each component. That improves encapsulation and allows you to implement maintainable components that you can quickly adapt to new requirements.
And if you decide to use containers to run a system of microservices in production, you need an orchestration solution that helps you to manage the system.
- SOLID Design Principles Explained: The Single Responsibility Principle - April 1, 2020
- 11 Simple Java Performance Tuning Tips - August 13, 2019
- Java Logs: 4 Types of Logs You Need to Know - November 15, 2018
- Java Logging Frameworks: log4j vs logback vs log4j2 - October 30, 2018
- Design Patterns Explained – Dependency Injection with Code Examples - June 19, 2018