Transcript
This transcript was autogenerated. To make changes, submit a PR.
Hi, my name is Daniel and I'm glad to introduce you my talk on how to
migrate from monolith to microservices, a guide to seamless transitions.
Today we're going to talk through five parts.
The first one is the architecture of the overview.
We're going to briefly describe, the benefits, of microservices
architecture and disadvantages of the monolith architecture.
We will talk on how to choose the best architecture for
your system, for your case.
Then we're going to discuss on how it is related to high loads and how
to connect microservices and high loads and how to split a large system
into separate smaller microservices.
Then, when we know how it is related to high loads and, we will going to
design to talk about design for failure.
we're going to discuss what it is and how it's useful for our purpose.
After that, we will go through main challenges and we'll also talk about the
solution for this, for these challenges.
We'll describe the problems that can occur during your transition and what
are the main approaches for them.
to handle them.
And in the end, we will take everything all together and, see how we can apply
our knowledge on real practical examples.
the first thing is, what is a software architecture?
you can go through many books and websites and find many different
definitions for software architecture.
But the one that I'd like to, share with others and stick with me is,
It is a method for documenting decisions made for the implementation
of an information system.
decisions are presented as a connection between several components.
And one of the most important thing is that architecture is always
considered from different perspectives.
You can, come up with different frameworks, for example, UML, when
you can, look or describe software architecture from different perspective.
For example, you can create sequence diagrams to describe the behavior of
your system, or you can create use case diagrams to show which, in which way
your system can be used and et cetera.
as a separate type of architecture, we can, name monolithic architecture.
And of course, there are many other types, but we're not going to, look
at them today because it's a very, large topic and, we're not going
to cover In our talk and, okay.
Talking about monolithic architecture.
as many of you already know, monolithic architecture is when
you have a large piece of software.
And of course, it's simpler for your development.
It's very easy to deploy on servers.
And usually it has great performance.
And, it's easy to manage from all perspectives.
But when I was working at the world's largest social network, my domain,
my main domain was the authentication and the authorization systems.
And when the, amount and when We have more than 500 developers.
Then it was almost impossible to, deploy some fixes, new code, and you
have, I had to wait for more than several days to have my code deployed.
So of course it presents different scalability issues.
You cannot like simply copy, one, large piece of software,
because if one component fails, then the whole system fails.
So it is tightly related to coupling between your components.
of course, deployment risks, as I've mentioned before, we had more than
500 developers and when one of them, had an error or some bug in, in his
code, then, the whole, we call them so called trains, with comets, the
whole train was a total failure.
So we had to roll back it.
Okay.
I'm going to a much better approach microservices architecture.
It divides your large piece of software into smaller and
loosely coupled microservices that are deployed independently.
And of course, when you talk about its benefits, you don't have such problem as,
complex deployment or, tightly coupled components, you have
other problems that come up when you use Microsoft's architecture.
For example, the whole system costs more for you.
It's, it is more complex.
So you have to find, more SRE engineers for your company, for your team to handle
the infrastructure for your microservices.
The software you use is more complex.
For example, in almost every microservices architecture that
I've worked with, we had Kubernetes.
And, it's if team is not ready for to work with such tools, it will be
a bit painful for you to migrate.
So we know the benefits and disadvantages of both types of architecture.
so let's.
describe the use cases when microservices architecture might be a better choice.
So when you have to scale your system, when your system is becoming
more complex and always evolving, as an example, a social network, it
is all, it always receives updates.
when you have to deploy and develop, some applications independently,
microservices is a great choice.
Also, it is tightly connected with the team structure and
ownership in monolith applications.
It is usually hard to, separate one component from another and to divide it.
for different teams to use the other use cases when you have a, when you
have technology diversity, for example, you have one microservices written
in Golang and the other in Java.
So microservices.
architecture handles this case great.
the other thing is the full tolerance and isolation.
when you want one part of the system to fail and not have
other part of the system to fail.
The other use cases when you have your application distributed across different
countries or different parts of the world.
the other use case is integration with third party services.
And when you have to speed up the development process or the
simplest case, when you just want to modernize your legacy system.
we also have to mention.
Use cases when microservices will not suit well for example when your rps is
are Not too big for example down two to four rps or When your application is
very small Or team expertise is limited Or maybe you have limited resources this
is a use case when you don't want to use microservices and better stick to
your old technologies and architecture.
Uh, as I mentioned in the beginning, we'll briefly discuss on how
microservices are related to high loads.
I would say that high load is almost impossible when you are creating, It's
using monolithic architecture, but I would not be 100 percent sincere with
you because the world's largest social networks, such as Facebook or pk.
com, they are, they, historically were built using monolithic architecture
and developers had to invent different solutions to handle this.
For example, to create transpilers to translate PHP code into C code or
developers had to create more, more performant perform the software, like
databases, with much better performance.
Because high load is where the bottleneck is the CPU, and the main
challenges are the volume of data, the quality of data, the high rate of
change, and high level of complexity.
There is no clear definition of high load.
We can only name some signs of it.
For example, the system can no longer handle the current load or
common approaches are insufficient or there's an urgent need to scale the
infrastructure or a single server is not enough to serve your customers.
Thank you.
And also when hardware can't cope with increased loads and existing
tools are, cannot solve your problems.
And when you are in such situation, when you have a very high RPSs, the
main questions that you should consider are reliability, two, scalability,
and three, ease of maintenance.
Thank you.
I want you, from the all, points that I will mention today, these three are the
key ones, so I want you to remember them.
Designed for failure.
Some of you may know such person as Edward Murphy.
And he said that anything that can go wrong will go wrong.
And this is the principle that you should keep in mind when developing microservice
architecture and high load systems.
Let's see a small example of it.
For example, you have an order service which stores its orders in a database.
and sends an email when it receives a creation request.
So everything is pretty simple, right?
Then let's say that your email provider unexpectedly fails, but you still have to
deliver your messages to the customers.
So the great choice for you would be to add a fallback email service that you're
going to use in case your main one fails.
In the case of a service failure, external or internal to the system,
it must be properly handled.
And another good option for handling degradation is to use a cache
service for your data storage.
For example, your database cannot handle the load and, Sorry.
But you still have to serve your customers with, many pages that
they can watch your, their orders.
And, of course, the, they're going to face some problems when
they want to create a new order.
But, for example, just to watch them or just to, list them, the cache is enough.
The other thing is, that's slightly patches microservices
patterns is transactional outbox.
when you have some messages in your system, when you have messages queue,
you want to implement delivery, grant some guaranteed deliveries.
And for that purpose, the transactional box pattern is the best.
And if you're working with very important data, maybe with finances or personal
infos, personal data, this is the kind of pattern that must be used, otherwise you
will face some unpleasant consequences.
And we are moving to challenges that you are going to face when
you are migrating your system from monolith to microservices.
And the first one is code complexity.
As you see on the screenshot, This is a real screenshot that I was
taking, when I was working on the code base of the social network.
And I've just clicked the button to find the usages of ID function.
And as you can see, we have more than 30, 000 results, which is mind blowing.
And apart from it, the code is tightly coupled.
It's usually hard to understand and you spend days to understand how one component
work And of course The development is slow and you're you cannot deploy
your code very fast when you have Many developers working on the same parts Of
course, it's limited in its flexibility.
It has high risks of bugs.
It's almost impossible to cover with tests, unit tests or integration tests,
because you have the problem when you do not have interfaces and basically
your test environment cannot use this.
production databases, so you cannot write your test code in such cases.
You have, firstly, you have to disconnect the data that is used
in production from the code.
The other thing that I think is the most important, it is a high entry barrier.
It is very hard to find new developers, especially if you use
some old technologies like PHP and maybe Perl or other stuff like this.
But what's more importantly is that even if you find one, it will
take more than a year, two years for them to adapt to the code, but
code base to understand processes.
And then developer works more than two years on such project.
it is very hard to, not lose him because, usually developer gets tired
working on such, bad quality code.
So that introduces recruitment challenges.
for listening.
The second thing is data management and decoupling.
we've mentioned this thing before, so we'll go through solutions.
first one is database per service pattern, when you use one, exactly
one database for your microservice.
And, You use event sourcing or change data capture to, let the other microservices
know that some data has changed.
You may also use data replication and shared databases as temporary solutions.
For example, you have module one, module two, and et cetera that, that
are working with the users table.
And the first module was working with username field.
The second module is working with a password field.
so how do we handle such situations?
We can create, let's say, two microservices and divide our
data into two separate tables.
The first one is for users and the second one is for addresses.
And if our personal data service needs some user info, any user
info, then it can just, Make a get request to the ALF service
The next thing is the multi layered caches It is a on the slide.
You can see a real example of how the caching Could be implemented.
I Would not say that it is a great approach, but it's how it is in big
systems that Handle a lot of requests.
You have your app, usually monolithic app, and you have your local caches,
maybe memory caches, and then you have a chain of caches, multi layered caches,
and one question could come to your mind, You can pause and try to answer
it by yourself, but the answer is, when you want to, simplify such systems, you
have to, go through all the complexity of the code that works with this
chain, and usually it's mind blowing.
microservices rely on communication between services, and it
introduces latency, failure points, and complex dependencies.
For such cases, you better use message queues, service discovery
tools, and circuit breakers, to not allow cascade failures.
And usually for this, you have different libraries in your programming languages.
we're going to talk about security.
Microservices, of course, they increase the numbers.
of communication endpoints exposing, the system to vulner vulnerabilities.
And, to handle this, you can use API gateways to centralize authentication,
authorization, and request validation.
for example, you can use Istio Gateway if you're using Kubernetes, or you can use.
Many proxies like NGINX or Envoy or Help Proxy.
The other thing that you can use is of two protocol and token based authentication
like JVTs for secure access control.
And if you have a separate team or specialist, which is, Which responsi
Which responsibilities are connected with information security, you can implement
penetration testing, and all that.
Of course, it is very hard to deploy decentralized systems.
Distributed systems, sorry.
So usually, you want to have a separate server.
team member, maybe DevOps engineer that will handle all these tasks.
and usually in his responsibilities, you can expect counterization and
orchestration tools, CI CD pipelines in GitLab or GitHub actions.
And of course, GitOps for infrastructure as a code, maybe, such things as, Argo
CD and other stuff, maybe Ansible.
To monitor a monolithic system, you can use maybe the things like,
Sentry or, things like that, but to monitor many small applications.
You'll have to use more complex approaches like logging, tools like ALK
stack, distributing tracing solutions.
You can also implement distribution tracing by yourself, but if you
are limited in your resources or doesn't want to, you can use
um, ready solutions like Zipkin.
Also, it's a great choice to use service meshes like Istio.
It's great for Kubernetes.
And it also helps to, for, to increase the observability of your system.
I've mentioned before that microservices introduce some, HR problems.
So teams.
and organizational resistance and skill gaps.
teams may resist, change due to lack of familiarity with microservices
or fear of increased workload.
And you probably will have to organize training programs and workshops and maybe
transition your responsibilities between team members and groups of developers.
And to demonstrate that microservices approach can work with your system,
you can create, let's go, an MVP for your system, for your microservice,
and then the team will understand the benefits, and you'll more likely
continue to migrate to microservices your other components of your system.
of course, working with legacy monoliths is hard.
and the parts that are going to help you are strangler, facets, adapters,
bridges, and, different others.
Also, you'll have to maintain some compatibility layers.
The main concern that I have faced with when I was working with the, when I was
migrating authentication part of the social network to microservices is that
authentication is under a very high load, hundreds of thousands of RPSs.
And, It was unacceptable to, increase latency even by one or two microseconds.
So I had to think about how to, increase performance of my microservices or of
my, networking of my infrastructure.
for example, I've used, for interconnection, I've used
gRPC protocol made by Google.
we've also used Kubernetes to scale horizontally our system.
And to measure the impact of the migration, we've used performance testing.
It was very hard in the beginning to manage dependencies, but, there are
great solutions for such problems, such as event driven architectures.
or API versioning with service contracts, a great tool Swagger and OpenAI are
a great choice for you to document your APIs and share it between teams.
So we've covered almost every significant problem, and we've mentioned the
solutions that can solve these problems.
So let's go to the practical example, and we will try to create a token
management service by moving, code parts from one monolithic data, code base.
And the system itself just generates and checks APIX tokens.
So we have our user, which provides session token like any string.
And we have our legacy monolith system.
We have our web server, where our users send requests to.
And our web server just sends.
user's token to parse session function, which returns ID, IP and
expires date, taken from database.
Everything is simple.
We want to migrate gradually.
So the first thing we do is we take, we create new microservices, let's call it.
token microservice and we just copy our parse function and we send two requests
simultaneously one to the monolith version of this function and the other two
microservice version of this function both of them are temporarily using sessions
database so they're sharing it and if we compare our ID and IP and other data.
And if it's equal, then we can gradually decrease the traffic from the monolithic
function to microservice function.
And we'll have something like this, where parse function is transferred
to token microservice But what if some other module uses Session's database?
We have to migrate it as well.
How do we do it?
And the problem could be even worse if we have different caches
between database and monolith.
here's A solution for this problem, we add a consumer to the monolith
and we add a producer to the microservice and use patterns that we
have mentioned earlier, CDC pattern.
So when our sessions database changes, then we capture changes and
we're producing message patterns.
into the message queue.
the consumer will take the message from the message queue and update
its local state in some other DB.
And our other module will use data for not from sessions
database, but from other database.
So we have decoupled two modules, one from another.
And to make everything more, more concise, to keep it simpler, to make
everything beautiful, we can transfer the logic from monolith to the second
microservice and let everything other, everything else untouched.
That was it for today.
In the end, I want to recommendate, give you some recommendations on books that you
can read and take useful information from.
So the first one is Designing Data Intensive Applications by Martin Klapman.
And the second one is the website called microsoft.
io These two will probably be enough to cover the main aspects of microservices.
But if you want to go deeper, you can use distributed systems.
It's free and could be found on the URL that I provided.
And if you are familiar with Go, you can go there.
take a look at the book by Nick Jackson.
So thank you for your attention and I'm looking forward for your feedback.
You can send me questions if you have, in telegram or you can write me an email.
So thank you once again and have a nice day.
Goodbye.