While a distributed microservices architecture isn't a fit for all enterprises or application types, it works well in many situations.
Microservices promote a modular application design, and facilitate flexibility. They provide organizations with the freedom to choose development languages, frameworks, code builds, integration processes and runtime infrastructure. However, such freedom leaves developers and DevOps teams with a daunting array of choices.
There is no prescriptive or universal action plan to deploy microservices in a production environment. However, major online service providers such as Google, Amazon and Netflix that operate hyperscale distributed architectures show that every DevOps team must address certain principles and issues to prepare for a microservices deployment. Here are six of those key lessons learned.
1. Use cloud services for production infrastructure
Simply stated, a microservices architecture is one in which applications consist of discrete, independently scalable components. Cloud services, which encapsulate infrastructure resources or enterprise apps as on-demand services, enable microservices -- they make it more cost-effective and convenient to disaggregate and run application functions as separate components. Rather than use traditional IT infrastructure, build and deploy microservices in the cloud.
While the decision to operate in the cloud still provides IT organizations with an array of choices for a microservices runtime environment, don't partition microservices into discrete VM instances, as containers and serverless functions are better suited to microservices deployments. VM instances might still be necessary, because container users often deploy compute instances to build a cluster rather than use a fully managed containers-as-a-service tool. However, it's better to manage these instances through a cloud Kubernetes service, such as Azure Kubernetes Service, AWS Elastic Kubernetes Service or Google Kubernetes Engine.
Given the novelty of microservices deployments, never judge the feasibility, cost and performance of cloud services based on your history with legacy applications and servers. Likewise, don't assume cost-performance tradeoffs in storage -- research the financial and performance differences between solid-state drives and hard disk drives, and you might determine SSDs are worth the incremental cost.
2. Design for failure
A microservices architecture is decoupled and distributed, with individual application components that have many dependencies. Nevertheless, such disaggregation enables components to operate, receive updates and scale independently without a base reliance on the availability or responsiveness of other microservices. The microservices infrastructure is not critically dependent on a single server -- without mutual independence, a microservices design becomes a convoluted monolithic system.
Organizations must design and deploy microservices to accommodate for failure. For example, microservices must treat servers, or container nodes, as stateless entities that can independently stop, restart, autoscale and patch. Applications must tolerate the failure of microservices components and recover gracefully from component-level failures. Chaos engineering tools, such as Gremlin and Netflix's Chaos Monkey, test the resilience of a microservices infrastructure.
3. Decentralize data management
Enterprise data centers often consolidate databases and storage volumes onto a few systems to improve operational efficiency, cut licensing costs and simplify application, policy and security management. This monolithic approach is anathema to microservices design, as the database features don't suit every situation. If several microservices application teams share the same database, this becomes a problem when someone needs to update the database structure that other microservices depend upon.
Software developers need the freedom to choose the best database for the job. This is easy to ensure with cloud platforms, such as AWS or Microsoft Azure, which offer a myriad of database choices. Developers also need the flexibility to change and update the database schema and procedures without the fear they might break existing functional code.
4. Distribute governance
Enterprise IT organizations traditionally operate in functional silos -- separate storage, networking, database, development, operations and server teams -- where each silo handles one piece of the overall application lifecycle. In this traditional model, development teams become part of an unwieldy project to implement an application or IT service. Teams often communicate through existing processes, such as help desk tickets or feature requests, which are slow and often highly restrictive. This inhibits creativity and rapid development of new ideas.
Microservices upends this structure. Cross-functional microservices teams with a DevOps culture and Agile development practices replace IT silos. Teams organize around the product -- the microservice or microservices-based application -- instead of a particular job role, with a manager responsible for its development and delivery.
To design and deploy microservices successfully, create a culture with minimal restrictive processes balanced with the responsibility to acknowledge and fix problems when they occur. Optimize for speed, rather than efficiency. The infrastructure team should be small, because the cloud service provider handles most operational tasks. Product-focused teams promote agility, tight communication and coordination. This team structure also eases the build process for necessary API interfaces that enable process automation and microservices integration.
5. Automate infrastructure deployment, embrace CI/CD processes
As mentioned above, microservices thrive in an agile DevOps environment, which requires fast, frictionless processes. To eliminate overhead and friction, automate processes so a particular team member's actions trigger the necessary responses -- updating a code module generates software builds and tests, for example. Process automation encourages rapid code development and an evolutionary design, and both are critical attributes of microservices.
Netflix offers two other tactical recommendations based on its microservices experience:
- Keep code at a similar level of maturity. Immutable infrastructure entails the creation of a new microservice when an enterprise needs to add features or fix bugs in an existing one. The technique keeps the old microservice operational while developers test, patch and debug the new one. When it's ready for deployment, infrastructure admins swap out the old microservice container for the new one.
- Use separate builds for each microservice. Various microservices might share files such as libraries and code functions, but they might require different versions. Because the microservices philosophy centers on minimizing dependencies, build each service independently to ensure that it pulls the correct version of a component file from a storage class memory. Independent builds also make it easier to prune outdated versions from the code base.
6. Monitor, log and troubleshoot from the start
The construction of applications from a set of microservice Legos considerably complicates how to monitor and troubleshoot systems and their performance. Various microservices often trigger a cascade of events that leads to an application failure. To minimize failures -- which aren't a maybe, but a reality -- incorporate monitoring and troubleshooting into microservices design.