A microservices architecture is a pattern of designing software by taking a modular approach. This is in contrast to traditional architectures which treat software as one large monolithic unit. Microservices are among the most common development patterns for cloud native applications.
In a microservices architecture, an application is developed as a set of services specifically scoped for a given purpose, which can be managed and managed, deployed, and provisioned independently. Microservices are built independently, typically by different teams, and can be upgraded or replaced individually, integrated through defined APIs. A service is a process that uses a software-agnostic protocol to communicate across the network and is implemented by one or more microservices.
Microservices can be custom-built to provide specific business capabilities. Their compact size and decentralized architecture offer greater flexibility and ease of integration with various hardware, software, and programming languages.
In this article:
Microservices Architecture: Pros and Cons
Microservices advantages include:
- Agility—microservices are independently provisioned, making it easier to manage new version releases and bug fixes. They can be updated or rolled back without having to redeploy the whole application.
- Limited focus—development teams can focus on specific microservices, making the process of building, testing, and deploying features more agile. Larger teams with a more generalized focus can often move slower and be harder to manage.
- Small code base—avoids the tendency of monolithic applications to have tangled code dependencies, so you can add new features more easily, without risking potential conflicts with other parts of the application.
- Fault isolation—a fault in one microservice has a greater chance of avoiding disruption to the rest of the application in the event that there is a functional or security-related issue.
- Data isolation—schema updates are easier to perform because they only affect one microservice. In monolithic applications, because data is often shared by different components, altering the schema can introduce risks or delays during development and testing.
Microservices challenges include:
- Complexity—there are many moving parts in a microservices application and, while each service is relatively simple, the system as a whole can be complex.
- Security—microservices can be more difficult to secure, because of the aforementioned complexity. Each additional microservice represents a new potential attack surface, and a vector for additional attacks if one microservice is compromised. Additionally, many traditional security tools cannot operate in a microservices environment. Read our guide to microservices security ›
- New approach to development and testing—teams often need to adopt a different approach as compared to a traditional application development and testing, in order to write multiple small services with different dependencies. This may also require new tools, integrations, and workflows.
- Governance and management—building decentralized microservices offers more flexibility. However, it can require multiple frameworks and languages that are more difficult to manage with a uniform set of standards. You may choose to establish new, yet consistent, standards and APIs across the project, or to have unique standards per microservice.
- Latency—using many specialized services requires extensive interservice communications, which can congest the network and result in latency. Long chains of service dependencies can also contribute to this latency.
- Data consistency—each microservices can use its own database, tailored to its specific requirements. While this can help scalability, these databases can vary in structure and persistence, and can introduce challenges for data management.
Microservices vs Monolithic Architecture: 5 Key Differences
As technologies evolve, they change how we develop our application architectures so we can benefit from greater scalability, performance, and more. Microservices architecture is different from monolithic architecture in many ways, including:
Monolithic applications involve setting the deployment once, with the possibility of adjusting it over time, but the single point-of-failure means that the whole project can be affected by one fault.
While microservices require more work initially, they offer isolation so if one microservice is broken, it does not need to affect the rest of the project. It is also relatively easy to roll back a microservice, as compared to a monolithic app.
The main difference, in terms of security, is the number of potential attack surfaces—monolithic applications have a single attack surface, while microservices architectures have multiple attack surfaces. The attack surface of a monolithic app, however, is much larger than the attack surfaces of an individual microservice.
Traditionally, a web application firewall (WAF) is one security measure used to protect an entire application. Microservice security requires that teams consider the security of each connection to each microservice, often secured with an adaptable firewall. Microservices can also have their own separate firewalls.
In some cases, microservices can require more authentication and authorization steps to execute transactions. Furthermore, because each microservice needs to be secured independently, this can increase the complexity of security management, which must be engineered efficiently to avoid added latency during these transactions. It can also be harder to implement automated monitoring with common or unified metrics across multiple services.
Microservices architecture can provide significantly higher reliability, as compared to monolithic architectures, given the separation of services. If a microservice is broken, only the client using it is affected, but the rest of the project is shielded.
Microservices can support faster release cycles for new features, requiring less time to build and test them. The internal dependencies of monolithic apps cannot be broken up, so new commits may depend on an unfinished change from another development team or individual contributor.
Costs are difficult to compare between architectures. In some cases, a monolithic architecture may be cheaper, but in others, a microservices architecture is more cost-effective. Microservices are especially useful for scalability, as you only pay for the resources you use, and new services can be spun-up as necessary to perform their function at scale.
Larger monolithic apps can be more expensive to run and maintain because instances can’t be shared across multiple, cheaper hosts, and scale means multiplying the entire application rather than individual parts of it.
Best Practices for an Effective Microservices Architecture
An important design decision for microservices architectures is the way services communicate and share data. This is especially important if the microservices each have their own data storage. For the application to be effective, the separate services need to be able to work together.
The most common (and easiest) type of inter-microservice communication is through a synchronous REST API. This is not, however, a solution for the long term, because latencies can build up over time with long chains of service calls. Additionally, failures can cascade from one service to another.
For these reasons, you can consider implementing asynchronous communication. Microservices can communicate asynchronously through various mechanisms, including via message queues and asynchronous REST.
To develop a microservices application, you need to split the entire application into small and autonomous services that can be independently deployed. Domain-driven design is a way to do this in a way that creates loosely coupled microservices. Otherwise, your application will inherit the typical disadvantages of a monolithic architecture.
The majority of software developers use open source and third-party dependencies as a way to accelerate development and to build on the innovation of the broader developer community. There may be so many dependencies that it becomes impossible to track all of them manually, or known dependencies may have their own dependencies of which developers who select the primary dependency are unaware.
Dependencies may contain security vulnerabilities, and so tracking open source and third-party components is critical to maintaining security and health. Tracking these components can help facilitate early risk detection, which can facilitate timely remediation of security vulnerabilities.
Proxy Microservice Requests Through an API Gateway
API authentication, throttling, and request/response logging are important. These functions should not, however, be performed by all microservices. Instead, you should configure an API gateway to do these tasks.
Once you set this up, clients that need to call a microservice can connect to the API gateway instead of directly calling the service. This configuration provides the following advantages:
- There are no redundant calls from microservices
- The internal URLs of services remain hidden
You can then redirect traffic from the API gateway to a newer version of the service when one is available. This setup is critical when a third party attempts to access the service. It enables you to throttle the incoming traffic and reject all unauthorized requests directly from the API gateway before the request can reach the microservice. To increase security, you can also set up a separate API gateway to accept traffic from external networks.
Leverage REST APIs
A Representational State Transfer (REST) API is highly useful when developing a microservices application. REST APIs offer ease of use and flexibility, because there is no need to install additional software or libraries, and the data is not tied to any specific method or resource.
This type of setup enables the application to handle different types of calls and return a wide range of data formats. It also allows the application to adjust the structure of responses by using the correct hypermedia implementation.
Microservices Security with Aqua
Aqua Security extends application-level security tools like WAFs and API gateways, with automated networking controls and service-level authorization policies. Since microservices networking is more complex and dynamic, Aqua provides a purpose-built solution that is more granular, policy-driven and agile than traditional network security tools.
By enforcing network-layer connections and defined nano-segments based on the microservices context to control the communications between your workloads in your cloud native environment, Aqua limits the “blast radius” of compromises. Aqua automatically discovers container network topology, both within a host and across hosts/pods, and applies context-based firewalls based on the resulting analysis.
This analysis serves as the basis to automatically generate recommended firewall rules that can be used to define permitted microservices connections, which in turn serve as policy baseline for alerting and blocking unauthorized communication flows with no service downtime.
These network traffic rules can be scoped down to a specific process within a container or host, and can be defined on both cloud native and network parameters – orchestrator concepts (pod name, namespaces), IP/CIDR addresses, and DNS.
In addition, through Kubernetes Assurance Policies based on Open Policy Agent rules and role-based access, Aqua can define and enforce admission controller policies for microservices ingress authorization as well as limit developer and administrator access to microservices functional components based on their responsibilities.
Aqua Security enables enterprises to radically improve application security as they transition to container-based, microservices approach to application development and deployment.
To learn more about how Aqua can help to modernise your application delivery and seamlessly secure microservices architecture, schedule a demo or dig deeper on our capabilities.