Incognito has been a hot topic in the past few years (Appendix 1). Here’s an example of what microservices are like:
Netflix is notoriously good at DevOps. Netfix can do microservices. So: If I do microservices, I’ll be pretty good at DevOps, too.
In many cases, we blindly invest huge efforts into the micro-services model, but often rarely consider whether the costs and benefits of access can effectively solve the pain points we are currently facing.
Below, I’ll describe in detail what microservices are, why the model is so attractive, and finally talk about some of the key challenges that some microservices are facing.
If you’re wondering if microservices are right for you, can they help you solve a problem you’re currently facing? Read on, and I’ll help you out with a series of simple questions. This series of “questions” is at the end of the article.
What are microservices and why are they so popular?
Come on, old driver take you from the basics. An example: The following diagram shows a hypothetical video-sharing platform implemented as a “whole service” on the left and as multiple microservices on the right:
The difference between the two system designs is that the left side is a whole large and complete service. To the right is a small, much smaller set of services, each of which is a specific service, each with a specific role.
When diagrammed at the system detail level, it’s easy to see the many potential benefits of microservices in a few simple ways:
Independent development: Small independent components can be built by small independent teams. A team can change the ‘Upload’ service without interfering with the ‘Transcode’ service or even knowing about it. The time to learn about components is greatly reduced and it is easier to develop new features.
Standalone deployment: Each individual component can be deployed independently. This allows new features to be released faster and with less risk. For example, fixes or features of the “Streaming” component can be deployed without the need to deploy other components.
Independent scalability: Each component can scale independently of each other. When there are multiple concurrent demands and a new release is required, the “Download” component can be enlarged to handle the increased load without having to expand each component, making elastic scaling more feasible and reducing costs.
Reusability: Components implement a small, specific function. This means they can be more easily adapted to other systems, services, or products. The “Transcode” component can be used by other lines of business, or even become a new business, or provide Transcode services to other groups.
At the level of detail, the benefits of the microservice model over the whole model are obvious. But the question, if that’s the case – why has microservices become so popular in recent years? I was walking my whole life, and it just showed up?
If the microservice is so good, why hasn’t it been done before?
There are two answers to this question. One is that it relies heavily on our best technological capabilities, and the other is that recent technological advances have enabled us to take it to new heights.
When I started writing advertorials to answer this question, I found that this would be a long description, so from a practical point of view, I will split it into two articles and publish 2 later. In the first article, I’ll skip things like the process of going from single application to multiple applications, esBs and service-oriented architectures, component design and limited context, and so on.
Those interested can read more about Journey later. Although, in many ways we’ve been doing this for some time, the recent popularity of container technologies (especially Docker) and choreography technologies (Kubernetes, Mesos, Consul, etc.) has made the microservices model more viable from a technical perspective.
Therefore, if we want to implement microservices, we’d better think very carefully about whether we really need them. We have seen the great “theoretical benefits”, but it is worth mentioning that what are the unknown challenges?
What’s wrong with microservices?
With microservices so powerful and perfect, what challenges can there be? This is the biggest problem I’ve seen so far.
Development complexity increases
Things get harder for developers. In cases where developers want to work remotely, or perhaps across the functionality of many services, developers must run them on their machines, or connect to them. This is often more complicated than simply running a single program.
This challenge can be partially mitigated by tools (Appendix 3), but as the number of services that make up the system increases, so does the challenge developers face as they run the entire system.
The complexity of operation and maintenance increases
The potential complexity is a huge challenge for teams that do not develop services but maintain them. Instead of managing a few running services, they manage dozens, hundreds, or thousands of running services. The more services, the more communication, the more potential for failure.
DevOps has increased in complexity
Reading the above two points, you might realize that operations and development are handled separately, especially given the popularity of DevOps as a practice (I’m a big fan of DevOps). Can’t DevOps alleviate this pain point?
The challenge is that many organizations still rely on independent development and operations teams to run them — and some organizations are more inclined to adopt microservices.
This is still difficult for organizations that have adopted DevOps. Being both a developer and an operator is tough enough (but building good software is critical), but understanding the nuances of a container choreography system, especially a rapidly evolving one, can be difficult. Which brings me to my next point.
No expertise? Don’t use microservices
When a lot of things are done by experts, the end result is also fantastic. But imagine that an institution or organization using a single holistic system doesn’t always work well. So what can be done to improve and make these things better? By increasing the number of system services? But it also adds complexity to the operation.
Admittedly, all of this can be improved with effective automation, monitoring, choreography, etc. But the challenge is rarely the technology itself — the real challenge is finding people who can use it effectively. Precisely because these skills are in high demand right now, it may be difficult to find someone who meets your needs.
Real-world systems are often ill-defined
In all of the examples we’ve used to describe the benefits of microservices, we’ve talked about stand-alone components. But in many cases, components are not completely independent. Some areas may seem limited, but when you get bogged down in tedious detail, you’ll find they’re more challenging than you expected.
This is where things get very complicated. In fact, what happens if your boundaries are not clearly defined? Even if services could theoretically be deployed separately, you would find that because of the interdependencies between services, you would have to deploy a series of microservices as a group of services.
This means that you need to manage versions of services that work together, which are validated and tested in conjunction, and you don’t really have a system that can be deployed independently, because to deploy new functionality, you need to carefully orchestrate many services at the same time.
The complexity of states is often overlooked
In the previous example, I mentioned that a functional deployment might require the simultaneous deployment of many services in multiple versions. Assume that proper deployment techniques will mitigate this, such as blue/green deployment (which is rarely supported natively by most service choreography platforms), or running multiple versions of services in parallel and deciding which version of the consumption channel to use.
If the service is stateless, these techniques can mitigate a number of challenges. But the borderless service is straightforward and easy to deal with. In fact, if you have stateless services, THEN I would be inclined to consider skipping microservices and consider using a serverless model.
In reality, many services need to be managed. An example of our video sharing platform might be a subscription service. A new version of the subscription service can store data in a subscription database in different shapes. If you run both services at the same time, run both modes of the system at once. If you have a blue/green deployment and other services rely on data in the new shape, you must update that data at the same time, and you may also need to use cascading rollback if the subscription service deployment fails and rolls back.
Again, you might say that in a NoSQL database, these architectural issues go away, but that’s not the case. Databases that do not enforce schemas cannot connect to schemaless systems — meaning that schemas are often managed at the application level rather than the database level. The fundamental problems of understanding data structures and how they flow cannot be eliminated.
The complexity of communication is often overlooked
When you build a large network of interdependent services, there can be a lot of interservice traffic. This has led to some challenges. First, there are many things that can fail. We must assume that network call is likely to fail, which means that when one service calls another, it should need to retry at least a few times. Now we end up with a more complex situation when a single service may call many services.
A user uploads a video to the video sharing service. We might need to run an Upload service to pass data to the Transcode service, update subscriptions, update suggestions, and so on. All of these calls require a degree of coordination, and if any part of the process fails, we need to retry.
This retry logic can be difficult to manage. Trying to do things synchronously often leads to untenable failure points. In this case, a more reliable solution is to use asynchronous mode to handle the communication. The challenge here is that asynchronous mode itself tends to make the system stateful. As mentioned earlier, distributed and stateful systems are difficult to deal with.
When a microserver uses message queues for intra-service communication, you basically need a large database (message queues or brokers) to group these services together. Again, while it may not seem like a challenge at first, you get the idea — you have to pay it back sooner or later. The X version of the service may write messages in some format, and when the sending service changes the details of the message sent, the service that depends on that message will also need to be updated.
Of course, there can be many different message processing services in different formats, but this is difficult to manage. Now, when deploying a new version of the service, you might have two different versions of the service trying to process messages from the same queue, or even messages sent by different versions of the sending service. This can lead to complex edge cases. To avoid these edge situations, it may be easier to only allow messages of a particular version, which means you need to deploy versions of a set of services as a whole to ensure that messages of previous versions are properly masked.
This again highlights that the idea of standalone deployment may not go as well as expected.
Versioning can be difficult
To mitigate the aforementioned challenges, version control needs to be managed very carefully. Again, there seems to be a tendency to assume that following a standard like Semver[4] might solve this problem. However, this is not entirely true. While Semver is a reasonable usage convention, you still need to keep track of the versions of the services and apis that work together.
This can make things very challenging, and many times it can be confusing which versions of services work together.
It is very difficult to manage dependencies in software systems, whether node modules, Java modules, C libraries, or others. The challenge of consuming conflicts between separate components when an entity is difficult to deal with.
These challenges are difficult to deal with when dependencies are static. While you can patch, update, edit, and so on, if the dependencies themselves are real-time services, you may not be able to update them at all — you may need to run many versions (challenges described above), or until the entire system is fixed.
Distributed transaction
Microservices can be painful in situations where cross-operation transaction integrity is required. Distributed states are difficult to handle, and many small units may be difficult to orchestrate transactions.
It may sound tempting to try to avoid this problem by making operations idempotent, providing retry mechanisms, and so on, and it may actually work in many cases. But there may be scenarios where you just need a transaction to fail or succeed and don’t want it to be in the middle. The cost of solving this problem or implementing it in a microservice model can be very high.
Microservices can be behemoths in disguise
Obviously, individual services and components may be deployed in isolation, but in most cases you will have to run some sort of orchestration platform, such as Kubernetes. If you’re using a hosted service, such as Google’s GKE 5 or Amazon’s EKS 6, it handles a lot of the complexity of managing your cluster for you.
However, if you manage the cluster yourself, you are managing a large and complex mission-critical system. While a single service may have all the benefits described above, you need to manage clusters very carefully. The system may be difficult to deploy, difficult to update, difficult to fail over, and so on.
In many cases, the overall benefits are still there, but it is important not to trivialize or underestimate the additional complexity of managing another large and complex system. Hosted services can help, but in many cases these services are new and volatile (Amazon EKS, for example, didn’t announce them until late 2017) — you never know.
Death of micro-service madness!
Only through careful consideration can we avoid microservice madness for microservice’s sake. To help with that, I thought of some questions you might want to ask yourself, and possible answers:
You can download the PDF copy here: https://github.com/dwmkerr/blog/raw/master/2018/microservice-madness/images/microservice-questions.pdf
One final thought: Don’t confuse microservices with architecture
I deliberately avoided the “A” word in this article. However, my friend Zoltan made a good point when proofreading this article.
There is no microservice architecture. Microservices are just another pattern or implementation of components, nothing else. Whether or not it exists in the system does not mean that the architecture of the system is resolved.
Microserver is in many ways related to the technical process of packaging and operation rather than the inherent design of the system. The proper boundaries of components remain one of the most important challenges in engineering systems.
Whether your services are in a Docker container or not, you always need to think carefully about how you put your systems together. There is no single answer, only more options.
I hope you found this article interesting! As always, if you have any questions or thoughts, please feel free to comment below.
Appendix: Further reading
The following links may be useful:
- Martin Fowler — Bounded Context — A great article by Martin. I highly recommend this one.
- Martin Fowler — Microservices — This article focuses on the model of microservices.
- Microservices – Good or bad? — read this article to find out what BjornFrantzen has to say about microservices.
- When not to Microserve – Great post from Christian Posta’s topic
- Dave Kerr – Microservices Overall Architecture — Practical tips for CI/CD and DevOps in the microservices world — a recent DevOps conference presentation on microservices.
If you feel like it, please share anything you think is worth reading or watching!
reference
- https://trends.google.com/tren… rvice
- If you don’t want to miss this post, you can subscribe to the RSS feed or follow me on LinkedIn or Twitter.
- Docker Compose is a good solution, and Fuge is smart enough to also have the option of running choreography locally in the case of MiniKube.
- https://semver.org/
- Google Kubernetes engine, a born from Google cloud platform Kubernetes hosting service: https://cloud.google.com/kubernetes-engine/
- Amazon elastic container service (has support Kubernetes), a born from amazon Web services Kubernetes hosting service: https://aws.amazon.com/eks/