Get a blueprint for breaking your monolithic architecture into microservices using DDD, Dapr, Plogios, and HashiCorp Waypoint.
George Hantzaras, the director of cloud platform engineering at Citrix is thinking about new ways to move enterprises to microservice architectures and provide self-service operational platforms for developers to just code without having to focus on ops.
They've been experimenting with a lot of newer tools, including HashiCorp Waypoint, which they recently used to build a proof of concept higher level abstraction platform for builds, releases, and deployment called "WayPaaS".
In this talk, Hantzaras will talk about the trends in application modernization, microservices, and how to move through the migration process by using domain-driven design, Dapr for service networking, Plogios for Kubernetes management, and Waypoint for simplifying build, release, and deploy. Here's a description of each tool:
Ploigos: A new project (you can't find the code yet) that's part Kubernetes distro, part Kops/Kubicorn-esque management tool, and part operator marketplace/manager/deployment.
HashiCorp Waypoint: An application deployment tool that aims to deliver a PaaS-like experience for Kubernetes, Amazon ECS, and other platforms.
And check out the other talk Hantzaras gave this year: Building a PaaS with Waypoint.
Hi, everyone. Thank you for joining me in this presentation about how you can modernize applications, and how we can use specific tools and specific processes in order to do so. Probably by the third slide you're going to start thinking, why should we hear this guy? So, a little bit about myself.
I'm part of the Cloud Platform engineering team at Citrix. A big part of our job is to kind of move things through the cloud and enable teams to be able to move in the cloud faster. I'm an old HashiCorp user. I organize the Athens HashiCorp User Group, and I'm also — and this is kind of a spoiler, a disclaimer, more of a disclaimer — I'm also one of the creators of Ploigos, which we're going to go into a bit more details later.
I come all the way from Athens, Greece. The reason why that's important is, I'm going to start with a little story about mythology. Specifically, I don't know if you know the story of Ulysses, this guy, he was — after the war, he was trying to return home. And he had this endless journey of creatures, and things going wrong, and so on . That's pretty much how I ended up here today.
I uploaded my presentation, the demo videos, everything. Then I had a flight of 18 hours, and then all my files were corrupt, and then I had other problems, again, and again, and again. This is the long way of saying that you're not going to be seeing a demo at the end of this. But I'm going to do my best.
So, what we're going to see is, we're going to kind of set the common ground, what application modernization is, what does it mean, what the business value is, why do we do it, common patterns of how we should do it. Then we're also going to talk about how it's done, specific tools that are going to help us do so.
The key takeaways: first of all is why application modernization is important for long-term strategy. Common techniques, patterns, the seven R's of application modernization, and we're going to focus on three of the seven. They're all of tools like Waypoint, Ploigos, Dapr, in order to streamline the process.
We often talk about, when we speak about application modernization we talk about legacy systems. Modernization means moving away from legacy and being able to adopt new frameworks, being able to design our code base using new patterns. The thing with legacy is we usually treat it as we're talking about the system, we call it legacy when we want to say that it's trash. But usually, legacy is mission-critical and that's why it's more of a business decision if we're going to retain it, if we're going to transform it, throw it away, or just leave it as it is.
We usually modernize in order to reduce cost or better manage cost; to streamline deployments, make it easier for product engineers to deploy; build in more reliability, and be able to predict the reliability of our system in a better way; and be able to be more agile, to have a more iterative approach in development; and so on.Some common trends: probably the biggest trend around modernization is moving to the cloud, and nowadays we talk even for more complex setups like hybrid and multi-cloud environments. Also we're talking a lot about microservices, containers, Kubernetes, distributed systems, distribute architectures. Also, we're looking to automate everything. We're looking to reduce toil in our whole development life cycle.
The way to do this is the seven R's. So, starting from the top, the four first ones are usually business related. They're not things that we are going to usually care about. So, we either leave it as it is: retain. We either throw something away: retire. Replace it by something new, or repurchase when we, again, are going to buy something new. In this presentation we're going to focus about refactoring, replatforming, and rehosting.
Rehosting just means hosting it in different environments. It's kind of self-explanatory. Replatforming can mean a few different things. Changing the platform, it can mean moving from Java Enterprise to Java Community. And refactoring is the hardest one, but usually has the biggest value to return us. It has to do with us rewriting and rearchitecting our code.
A bit more details. As I said, rehosting, you can also hear that as relocating. We can just take something and move it in the cloud. Replatforming, we can change, probably — let's take an example of an application that's using a Postgres. Moving that on a Postgres database in Amazon RDS instead of having a postgres database, which is run in a VM — hat's replatforming. Repurchasing means to, again, move from a cell-hosted to a SaaS application or something like that. And finally, refactoring has to do with rearchitecting our whole application.
A few ways to do this is, first of all, moving something, rehosting something, we see a lot of times lifting and shifting. And that's a good way how not to do application modernization. One of the, probably the pattern we're going to talk most about today, is strangling the monolith. Essentially, we're trying to break down a monolith in small chunks, in microservices. And then finally, refactoring a monolith to microservices. And it might seem like a small difference between the second and the third. But it's a small difference that actually makes a lot of difference in the results. When we refactor the monolith to microservices, we usually do this in bigger chunks, which is less controlled and doesn't move around domain-driven design, which is essentially what microservices are built around.
Let's see step-by-step how we can modernize our application. And let's start with an example. We have an e-commerce setup, we have different types of users, different integrations, everyone is connecting through the same API into the single service. Single monolithic service has all the functionality inside, and this connects to a single database. And of course we know that here, if you want to scale the inventory, then you scale the whole thing and we all know what can go wrong.
We're going to focus in this step-by-step guide in three different phases. First, we're going to refactor the monolith and we're going to move to microservice architectures. We're going to do that by using the strangler pattern, which essentially helps us what we call strangle the monolith. So essentially, it's a structured approach on how we can take small chunks out of the monolith and create microservices. The second phase is going to be about deploying and managing a new type of infrastructure, which will host our application. It's going to be about how we can easily deploy and manage Kubernetes clusters in a multi-cloud environment. Finally, we have the new application, we have the new environment. In the third phase we're going to see how we can easily deploy this without adding an extra burden in the development teams.
The reason we do this is in order to move from the monolithic case where all capabilities, all business capabilities are different libraries or different modules. We want to move to the after, which were all different business capabilities or different microservices. We end up looking something like this. So we can independently scale different systems, we can update different systems independently and the reliability of those systems. We reduce the blast radius if something goes wrong.
The way we're going to be doing this is the first step, and probably the step that is most important and is being least done nowadays, is you have to use domain-driven design. And again, we have to remember that microservices are built around DDD, and we have to define the domain model, and we should identify what we call bounded context. And that's going to give us an idea what the bounded functionality of a microservice should look like.
Then we do an effort-impact analysis to choose the right context to start with. If we are deploying an application weekly, then probably that's going to have a big impact if we refactor that application and we are able to redeploy it independently. If we have an application that has scalability issues, then probably we want that to be independent. Then we're going to plan out the microservice architecture, the new architecture. At that point we're going to see that we're going to need to implement a gateway pattern. And then we're going to design and deploy the new hosting platform, and implement the new deployment automation for the new platform in the new application.
Let's start with the first step. How can we start strangling the monolith? How can we start carving out functionality from the monolith in order to create those microservices? We start by identifying the bounded contexts. Looking into a monolith, we can define different business contexts that are engraved in our monolith. We still don't think about microservices. We should stick to thinking about business context and define those business contexts without thinking technically at the moment.
At this point, this is where we have defined bounded contexts and that's where we start thinking technically. Each bounded context might end up being two or three microservices. We find one and we start carving out the functionality. At this point, we should start thinking about things like interprocess communication. This is going to be implemented by HTTP, GRPC, or asynchronous methods. In this example we start with authentication and authorization service. This is usually a good starting point because it's a service that many other microservices are going to rely on, and it has a straightforward design and implementation.
The last part of carving out the first microservice is that we should go back and think about 12-factor apps. We should have single-concern databases. Each service has its own database, and this is usually a hard part because we have to think about preferences, we have to think about all the stuff. Finally, we go back in iterate again and again until we reach the end state of having many microservices. The middle is really important here, because throughout this journey the monolith is going to coexist with microservices. We have to make sure that we route the traffic in the right way, we see the strangler façade, which we are going to implement a gateway pattern in there in order to route the traffic the right way, and those are some things we should have in mind. Also, it's really important to think how we're going to handle the database when the monolith and the microservices coexist.
Some things that are going to be a burden for the development team as we go through this journey. First of all, the team is going to have to start thinking in domains. They're going to have to start thinking about architecture in DDD. We're going to change how our services interact. We're going to have to think about that. Where do we need asynchronous, where can be pub/sub or it can be HTTP? And so on. Single-concern services, well bounded services, and single-owner databases, and then how do we handle state in microservices? Those are some concerns that we should be thinking as we rearchitect.
The first one is more of an architectural thing. It's more of a way of thinking, while the other three are things that Dapr addresses. And Dapr's a tool which helps us develop distributed applications, and it addresses the three points we talked about before. It handles interservice communication, so if we imagine a Kubernetes environment, in a pod we have Dapr deployed as a sidecar container with our container with our service. And then the service only communicates with a Dapr sidecar, and then Dapr handles everything else. So it can handle interservice communication, it can handle state store, pub/sub, and all the other resources you see at the top.
The reason this is good is because it makes this so easy for you to implement. Invoking a method from another service just becomes a line. Or, publishing events also is just a single line of code. And you don't have to care about implementing RabbitMQ clients or Redis clients for state management, and so on so. So this becomes real easy. The last part is also a nice way to manage secrets, and there's a Vault integration there which is in alpha at the moment, but that was really interesting. And the way it's deployed in Kubernetes, so Dapr has at the moment its own Kubernetes operator. They've implemented the operating pattern in Kubernetes. So managing Dapr is pretty easy. You just install the operator, you deploy it, it creates a namespace where the control plane of the pods you see at the top, those are deployed in the different namespace. And then when you deploy ... you don't have to change anything in your deployment. So your Helm charts stay as they are. And every time you deploy, you just have to add those annotations, and the Dapr control plane is responsible to deploy the sidecar next to your service.
This is how the e-commerce application ends up looking like. You have different services. Some communicate via pub/sub via the RabbitMQ, the basket service uses Redis to store state, and then we see all the services at the front communicate via, again, service invocation, the Dapr service invocation. This kind of helps us to take off some of the burdens of refactoring. So we don't really have to think about those things as we rearchitect and we refactor.
Now the next step is to rehost and deploy Kubernetes, deploy and manage a Kubernetes cluster. And this has been a pretty, pretty significant burden for many teams nowadays. So the first thing is that you have to start thinking in pods and containers instead of processes running, and that's already something big. That's already an architectural change in the way that we think. But we also have to change the way we do operations. We have to learn new tools. The Kubernetes landscape is pretty complex. We also have to deploy and manage a highly complex infrastructure. Finally, when we deploy Kubernetes, we still have to think building upon that to have enterprise-grade features, to have the right authentication, to integrate with enterprise authentication, have right observability, service mass, all the nice stuff we've been hearing.
That's where Ploigos comes to play. So, Ploigos is a platform kind of aimed to streamline all these aspects. So streamline the way a Kubernetes cluster is managed. Also managing federated Kubernetes clusters, so streamlining the multi-cloud and the hybrid cloud setups, but also providing enterprise features and enterprise-grade features, which are easily managed.
A little disclaimer there: Ploigos at the moment is a test project. The intention is for it to become open source soon, but if you look for it online, you're not going to find the code yet. The goal is it helps deploy and manage Kubernetes clusters; it deploys multi- and hybrid cloud infrastructure; it provides out-of-the-box enterprise-grade features; and simplifies operations on a number of add-on components. We're going to talk about this in a little more detail.
Ploigos is three different things. The core is a tool to help manage Kubernetes, so it's similar to kOps, to kubicorn, to all those other projects, the difference being that the focus here is on federated clusters in a multi-cloud environment. It's also an enterprise Kubernetes distribution, which is called PKD, Ploigos-Kubernetes distribution, and the idea there is it comes preinstalled with a few things like service mass, observability, out-of-the-box integration with enterprise authentication, and so on.
It's also a marketplace for operators, which can be integrated and deployed in any different cluster. So, if you're familiar with a Kubernetes operator pattern, it helps you streamline the deployment, but most importantly it helps you make Day Two operations much, much easier.
And this is pretty much how the architecture looks like. The four key components are the cluster API, where someone would talk to in order to manage the different clusters. And then the cluster management is what orchestrates the different clusters. The Kube API is essentially a gateway to the cluster scheduler, and that places different applications in the different clusters in a federated environment. So, the cluster scheduler's what manages the federation of the actual deployment through the actual applications.
The deployment of a cluster becomes really easy in that way. You just create a configuration file, you define the infrastructure you want. You can build upon AWS CC2 instances, you can build upon Azure VMs. You can even use EKS and AKS and GKE for your clusters. Also, you can add metadata so you can use affinity in your Kubernetes deployments, all those kinds of things. And then as you see on the right, as it deploys it also deploys operator manager, which is what's going to do the integration and the management of the Kubernetes operators later on.
In the background, it extends Kubicorn in order to deploy and manage clusters, and that makes it use Kubadm to bootstrap those clusters. Management clusters use a similar architecture with K3S, so it's really lightweight and it's really easy for them to be deployed. They spin up really fast. And the marketplace, the components in the marketplace are built around the operator pattern. So it's going to be really easy with one command to just install the Dapr operator or the Waypoint operator and so on. Then the distribution, as I said, provides some features built in. And observability and service management specifically are done through eBPF, extending Kubelet, so it's really easy to deploy right away.
We've refactored the application, we have a new application, we have the microservices, we have the new infrastructure, and now we want to deploy our new services and the new infrastructure. Again, the development team has a few difficulties there. You have to redesign how CI and build happens in the containers. The team has to start understanding how Docker works, how Dockerfile works, and they have to build the Dockerfiles, Helm charts, all those kind of things. This, again, adds an extra burden to the development team. In a world where we shift everything left, essentially all the burden falls onto the development team.
So, this is what Waypoint tries to do. Waypoint takes off some of this burden, and it helps you deploy and manage applications in a really easy way without having development teams understand what's happening underneath. This is how someone would define the whole build and deployment of a Node application. If you start at the build part using backpack means, using buildpacks, that mean, if you've used Heroku, it's a similar experience. That's where the project started. So build packs are going to get your code in a smart way. They understand what base image they should be using. You write no Docker file, and they just build you a Docker image. So without having to write the Docker file, you go through the build process. You have an image which is pushed at that repository, and then you just deploy in Kubernetes. And again, you don't have to write any Helm chart, any kubeconfig, nothing.
The way this is going to work step by step: you start, you deploy the cluster easily just by creating the cluster YAML file. You install the Dapr operator, you install the Waypoint operator, and then with that simple Waypoint file you just build and deploy. After the deployment, the Dapr control plane takes over, and it automatically, based on the annotation, it goes and deploys the sidecar in your services.
The things we saw today are the why and the how of application modernization. You also saw how the Mac keyboards do, the no fire, double fire issue with the E. Difficulties on what can slow you down in this journey, and how we can overcome those difficulties, and common patterns, common technologies that help you standardize and streamline the process of modernization.
Thank you for watching. I usually upload the slides in my Twitter account. So if you like this, feel free to reach out any questions or anything else. Thank you.