Building and sustaining a microservices ecosystem - Part 2
Rahul Srivastava
Sr. Leadership | Business Strategy | Digital Transformation | Cybersecurity (CISSP) | Enterprise Architecture (TOGAF)
Introduction
This article is part 2 of a three-part series on?“Building and sustaining a microservices ecosystem”. In Part-1 we covered the various considerations w.r.t. Cloud, Infrastructure, Platform, etc.
In this article (Part-2), we will go through the various security and design considerations when designing microservices.
Design considerations
This reference architecture is focused on designing microservices-based applications and the recommended practices that will apply to workloads running on a Kubernetes platform.
1. Microservices
A microservice is a loosely coupled, independently deployable unit of code. Microservices typically communicate through well-defined APIs and are discoverable through some form of service discovery. The service should always be reachable, even when the pods move around.
Some of the salient features of a microservice are:
The Kubernetes Service object is a natural way to model microservices in the Kubernetes platform.
2. Cross-cutting concerns
Cross-cutting concerns are technical requirements that are commonly applicable for all the features of a given software, for instance - logging.
If there’s one area where Monolithic applications have an advantage of, it is the code reuse for cross-cutting concerns. They can be shared centrally in Monolithic through single instances of libraries or frameworks.
In Microservices architecture however, these cross-cutting concerns may be addressed differently in each microservice or the same may be duplicated across all microservices. This creates manageability and maintainability overhead. In some other Microservices-based applications folks try to create a separate service altogether to address the cross-cutting Concerns like logging, which is then called over the network via an API call. The result is unpleasant, crude implementation, latency overhead, and it introduces new forms of challenges. It is not always recommended to create separate standalone Microservice/s to address cross cutting concerns.
Solutions that address the cross-cutting concerns in fact doesn’t really apply to the Microservices Architecture as such; but that doesn’t mean we should ignore it in the Microservices Architecture & Design. Instead the cross-cutting concerns should be addressed uniformly across all Microservices. For disparate development Agile team setup, sets of standards should be followed to avoid different ways of implementations by different teams for the same generic concerns. During sprints, cross cutting concerns should be default task cards for every service.
Below are some primary cross-cutting concerns and guidelines to address them:
3. Event-driven microservices
An excellent way to add visibility to what is happening to your service is through event sourcing or event logging. The fundamental concept behind this pattern is that every change to the state of an application should be encapsulated in an event object and stored sequentially.
Event-driven architectures are ideal for improving agility and moving quickly. They’re commonly found in modern applications that use microservices, or any application that has decoupled components. When adopting an event-driven architecture, you may need to rethink the way you view your application design.
Benefits of event-driven microservices
4. Data storage
In a microservices architecture, services should not share data storage. Each service should own its own private data in a separate logical storage, to avoid hidden dependencies among services. The reason is to avoid unintentional coupling between services, which can happen when services share the same underlying data schemas. Also, when services manage their own data stores, they can use the right data store for their particular requirements.
Avoid storing persistent data in local cluster storage, because that creates host affinity and ties the data to the node. Instead:
5. I18N & L10N
The microservices should support internationalization and localization.
The art of making software independent of the underlying system defaults is called internationalization (L18N). Every system has defaults, such as encoding and locale. Any piece of software that unknowingly relies on these defaults may not work correctly when ported from one system to another because system defaults vary from one system to another; and thus, are not internationalized.
The art of making software adapt to the underlying locale is called localization (L10N). Software must rely on these defaults, to display the text messages, error messages, and other messages in the locale it is running on so that it makes sense for the person who is using that software.
A locale represents a language. Therefore, adapting to a locale means the software should be able to display or take inputs in a language specific manner. For example, the French language is a locale. But the French spoken in France differs from the French spoken in Canada, so we augment our definition of locale to say that a locale represents a specific language of a specific country. Again, a language spoken in a specific country can have variations. For example, the ancient traditional Chinese spoken in China vs the simplified Chinese spoken in China, so we augment our definition of locale again to say that a locale represents a specific language of a specific country with variants.
When we write software that adapts to the underlying locale this actually means addressing the community that will use this software. Therefore, locale-specific things such as text messages, exception messages, the date and time, currency, and other messages should be displayed in a way that allows the user of the software to understand what is displayed (the output of the software). There is a community of users that the localization tries to address, because we don’t want to re-write the entire software for every language, and every dialect of that language.
领英推荐
Resource constraints
Resource contention can affect the availability of a service. Define resource constraints for containers, so that a single container cannot overwhelm the cluster resources (memory and CPU). For non-container resources, such as threads or network connections, consider using the bulkhead pattern to isolate resources.
Use resource quotas to limit the total resources allowed for a namespace. That way, the front end can’t starve the backend services for resources or vice-versa.
When you specify a pod, you can specify how much of each resource a container needs. The most common resources to specify are:
Securing microservices
1. Role-based access control (RBAC)
Kubernetes has mechanisms for role-based access control (RBAC). Kubernetes RBAC controls permissions to the Kubernetes API. For example, creating pods and listing pods are actions that can be authorized (or denied) to a user through RBAC.
It’s good practice to scope Kubernetes RBAC permissions by namespace, using Roles and RoleBindings, rather than ClusterRoles and ClusterRoleBindings.
2. Managing secrets
Applications and services often need credentials that allow them to connect to external services such as a SQL database. The challenge is to keep these credentials safe and not leak them.
Kubernetes Secrets let you store and manage sensitive information, such as passwords, OAuth tokens, and SSH keys. Storing confidential information in a secret is safer and more flexible than putting it verbatim in a pod definition or in a container image.
3. Pod and container security
This list is not exhaustive, but here are some recommended practices for securing your pods and containers:
4. TLS/SSL encryption
In common implementations, the Ingress controller is used for SSL termination. So, as part of deploying the Ingress controller, you need to create a TLS certificate. Only use self-signed certificates for dev/test purposes.
For production workloads, get signed certificates from trusted certificate authorities (CA). You may also need to rotate your certificates as per the organization’s policies.?
Namespaces – organizing services
Use namespaces to organize services within the cluster. Every object in a Kubernetes cluster belongs to a namespace. By default, when you create a new object, it goes into the default namespace. But it’s a good practice to create namespaces that are more descriptive to help organize the resources in the cluster.
For a microservices architecture, consider organizing the microservices into bounded contexts, and creating namespaces for each bounded context. For example, all microservices related to the “Order Fulfillment” bounded context could go into the same namespace. Alternatively, create a namespace for each development team.
Place monitoring and utility services into their own separate namespace. For example, you might deploy ELK or Prometheus and Grafana for cluster monitoring, or Tiller for Helm, etc.
To be concluded in part 3 where we will talk about monitoring, upgrades and deployment considerations and briefly on the DevOps considerations for operating the microservices.
Stay Tuned!
Senior Director at Capgemini
2 年Vijendra Malhotra
Azure Cloud |Terraform | Automation | Sr. Infra. Architect at Cognizant
3 年Good one
Technical Project Manager at Capgemini India Pvt. ltd
3 年Yes, its a good informative article end to end.
JAVA - Open Source Capability Leader | Technical Architect | Passionate Leader | Mentor
3 年Covered the core aspects well ..looking forward to part 3 :)
Solution Architect
3 年Exceptional!!