Oct 2022Full Stack Europe

Microservices in Your Monolith

Abstract

I gave a live-coding presentation on how to package, deploy, and execute small functions to serverless platforms, all from your existing monolithic application.

Transcript

Transcript

00:13

Okay. Well, like Frank said, I am, uh, from Texas, so I came a long way, and I'm still quite sleepy, but I am excited to be here to talk to you about putting microservices inside your monolith. Um, I am a developer educator at a database company called PlanetScale. We are a serverless MySQL platform.

00:34

Um, I'm a Laravel developer and dad to two seventeen-month-old babies. My wife and I have twins, so we stay very, very busy. Also, this is not the only shirt I own. I just happen to like it a lot, um, so I wore it again today. I need to thank my individual sponsor for allowing me to be here. My wife is at home with those two seventeen-month-old babies, um, and I would not be able to do stuff like this if it were not for her support.

01:02

So thanks to Jennifer for allowing me to be here. So I need to tell you what my biases are before we get started. I love monoliths. Um, so you can filter everything else you hear through that lens. I love monoliths. I'm a Laravel developer, so I primarily, um, love the Laravel monolith, but Rail, Symphony, Django, .NET, you know, whatever.

01:27

I love monoliths. I find them very easy to reason about. I like having everything in one place. I like knowing where to go to look for things. Um, I like having one CI/CD process. It's kind of a nightmare to set up CI, and so I only ever really wanna do it once. I wanna get it perfect one time. I don't wanna be deploying ten different things.

01:50

I also think a lot of our tooling is set up, um, and geared towards monoliths. So that's my bias. Um, so you can filter everything else through that, but I am going to try to convince you, um, that monoliths are great. But what happens when you need to step, like, just, like, a half step outside the monolith or one step outside of the monolith, and you need something that looks or feels like a microservice,

02:21

whatever a microservice is, right? So if you have something that you need to, like, you truly do need to scale independently from the rest of your application, that kinda feels like a microservice. If you have, um, a part of your application that requires a different language, so you-- maybe you have a PHP monolith and you need to pull in a Node library or a Python library or Ruby or .NET, so then that kinda feels like maybe this should be a separate service.

02:51

So we're starting to feel like there are some things that look and feel a little microservice-y, but I wanna stay in my safe monolith land. I feel that. Um, the stack complexity could go beyond just adding another language. It could be something like you need to add headless Chrome because you're doing PDF generation or scraping or screenshots or whatever.

03:14

And so now you have this thing. You have your monolith, but you have this other stupid little thing that you have to have, right? So what is a possible solution? Just use microservices, right? That's a possible solution. That word "just" is very small, and it's carrying a lot of weight in that sentence, right? So when you just use microservices, you inherit all of this complexity of like, okay, well now I had a monolith.

03:39

Now I'm in two different repos or a bunch of different repos. How do I deploy those in sync? But if they're not in sync, how do I version them? How do I communicate between these two services? If they communicate over the public internet, how do I secure that? How do I set up the client in my monolith and the auth layer in my microservice? And so you inherit or you adopt a lot of this complexity just because you needed that one extra little thing that you wanted to scale independently, right?

04:09

I am going to try to convince you to just put that microservice inside of your monolith. Um, like Frank said, this is using a package I wrote called Sidecar. Um, but I'm gonna try to convince you, one, that the package is great, obviously, but two, that the, the, the pattern is, is viable and it can potentially, um, bridge the gap between pure monolith and microservice, um, m-madness.

04:35

I guess that's my bias again. But pure monolith and, and a lot of microservices. So what am I talking about specifically today when I'm talking about monolith and microservice? This is not an all-encompassing definition of microservice, obviously, but specifically when I'm talking today, when I'm talking about a monolith, I'm saying one of our beloved frameworks, right?

04:56

We all know them, we love them, we love to use them. When I'm talking about a microservice, specifically today, I'm talking about putting a service on one of these serverless platforms. Now, you could put a microservice anywhere, but I'm talking about putting it on AWS Lambda, Vercel, Google Cloud Functions, Netlify, DigitalOcean functions, taking advantage of a serverless platform to have someone else manage that stack complexity.

05:23

So this is what we're talking about. This is as far as I've gotten. Um, I'll come back to this later because I need some help, but this is as far as my skills have gotten me. I've written the Laravel to Lambda, and the Laravel to Vercel is open as a pull request, uh, work in process. So we'll come back to this in a little bit, so if you're not a Laravel developer, don't tune out yet. You can tune out in a little bit, but don't tune out just because of this, okay?

05:47

So using Sidecar, if I'm setting up, if I'm setting up a dichotomy to having them separate, what does it mean to use a-- to have a microservice inside your monolith? With Sidecar, the microservices live in the same repo. So they are, they are actually in the same repo as your monolith. They are deployed alongside your monolith.

06:10

They're actually deployed from your monolith. And then this last one I think is maybe the most interesting, but it's also the biggest trade-off. They're executed directly from your monolith. So there is no publicly available interface. There is no API gateway to set up. There is no way to hit it except through your monolith. And this is a huge trade-off, and I think, um, I'll show you why in a second why I think this is valuable.

06:36

But to be intellectually honest, this is maybe the biggest trade-off. There's no public interface for it. You have to go through your monolith, which I like. If you are visually inclined, if you are a visual learner, this is what we're after. We want Laravel or your framework of choice, we want that to remain your main driver.

06:57

We want that to be in charge. And then attached to it, stuck on the side, you can have Node, Ruby, Chrome, your best little friend. You can have whatever you want in your sidecar, but it's still gonna be attached to your monolith. So this is what we're after. We're gonna do something risky, and we're gonna live code and live deploy to AWS on hotel Wi-Fi.

07:23

So, if you're running, um, NPM install, please stop. Um, if you're downloading shows for the flight home, please stop. Um, what we're gonna do, with any luck, is I have a Laravel app here. Um, it's just a standard Laravel application. I have required Hammerstone sidecar, which is the package I wrote.

07:46

Um, Hammerstone is that company that Frank was talking about. What we're gonna do here, with any luck, is inside this Laravel application, we're going to write a microservice, um, air quotes, deploy it to AWS, and execute it all from here. So let's see if we can make this work on this slanted podium here.

08:07

The first thing we need to do is we're gonna create a PHP class. We're just gonna call it basic function. This is just gonna be our little wrapper. We're going to extend Lambda function. Lambda function is an abstract class that ships with sidecar that has a lot of good stuff. Um, we'll add the method stubs. You see that there are two things required. The first is the handler, the second is the package.

08:31

We're gonna start with the package. Um, the package is the set of files that you want to ship to, in this case, Lambda. You could ship an entire node modules directory. Um, hotel Wi-Fi, so we're not gonna do that. You could ship whatever you want, a bunch of images, binaries. You can do anything. Here, we're gonna ship a single file.

08:53

So we're gonna ship sidecar basic.js, and we will come out here. This is a workout. Uh, basic.js. And what we're gonna put in here is what I call a JavaScript handler. So this is, this is the function that is going to handle the inbound invocation from Lambda.

09:14

It's, it's stupid simple. It just receives an event. We put it on the exports object, call it handler, that's it. Um, we are going to just say return hello world, hopefully. And that is going to be our microservice. A little bit uninspired, but it shows the point. Okay, so that's the entirety of our package.

09:34

That's pretty easy. Um, the handler is going to tell, in this case, Lambda, it's going to tell Lambda, "Inside this bundle of files, which file and which method do you want me to execute when an inbound invocation happens?" So in this case, we're going to return the path to the file, and then we're going to say the method name is handler.

09:58

I'm gonna double-check that. Handler. So basic JS handler, handler. I think we're good. Um, we're also going to explicitly declare what runtime this is. So we're gonna say that this is runtime node sixteen. I think the default's node fourteen, so we can explicitly declare what the runtime is, and this will tell Lambda use that stack and not the other one.

10:24

That's it. Um, that's basically like a... It's kinda like a meta wrapper that just kinda holds some information and a place to do some configuration. The final thing, the final thing we need to do is come to this configuration file and just register it. This just tells sidecar, "When I hit deploy, these are the functions I want you to deploy." Um, so we haven't messed up yet, which is good.

10:49

So now we're gonna come over here and we're gonna deploy this function to Lambda. AR is just PHP Artisan. So we're gonna say Artisan sidecar deploy, activate, and fingers crossed, we'll see what happens. Hopefully, this will say that it was deployed and activated.

11:09

All right. So we deployed to AWS. Um, all right. So what happened here is this said, "All right. I found that function that you registered. I'm gonna give it this hideous name, which is configurable. Um, I see that it's a node function. It already exists because I've been practicing this over and over. Um, but it was updated, and then it was activated." So we've done the package and deploy.

11:36

One of the things that I told you was interesting, um, and I'm hoping to prove that it is, is how you execute, um, these microservices, right? So we can only execute them through our monolith, but the trade-off there is that it becomes extremely easy to execute them. So if we jump to this, it's just a web route here. We don't...

11:59

I, I don't set up, as sidecar, I don't set up API gateway, um- Because we don't need it. So what we do is we say return basic function, um, execute. Now, what is gonna happen here is PHP is gonna use the AWS SDK and invoke that function, and it's gonna run on Lambda with any luck.

12:21

So if we hit this in the browser, "Hello, world." Hopefully. All right. That was the final i- that actually worked, so that's good. So what happened was the browser hit Laravel. Laravel used the AWS SDK to go out to Lambda, execute that function, send the response back, and then send it back to the browser. So there's no way to hit this Lambda just, like, from, um, from the URL.

12:45

You can't just go to this Lambda. You must execute it through Laravel, which is kinda nice. So if we move on, um, if we keep going, so let's go back to basic function. Node is cool, but what if you needed, for some reason, Python? So I think part of the power of this is when you get into, when you get into areas that aren't typically, um, covered.

13:09

Like, most web servers probably have Node, and you're probably fine running a Node process. What if you needed Ruby or you needed Python and you don't want to, um, manage that in the stack yourself? You could just, you could just use a Lambda for it. So if we look at this Python, I don't know anything about Python. I copied this off the internet. What this does is it says, "Hello from Python." You can see that part.

13:33

Um, and then it pulls an environment variable out of the Lambda container, um, just to kind of prove that, like, this is running on Lambda. This is not, like, a cheap trick. So what we're gonna do, so we've switched everything. Let me make sure we've switched it all in basic function, Python handler, python.py, Python 3.9.

13:54

We are going to deploy this again. And what happens is Sidecar sees that it's, um, Python 3.9 now. Says the function already exists, potentially updating, and it updates and activates. So that function that was Node is now Python, and it happened in, I don't know, a couple of seconds there.

14:16

So if we hit this again from the browser, "Hello from Python" with the environment variable. So now our microservice is running Python, right? So hopefully you're starting to see that this can be kind of an interesting way to do a low, maybe, DevOps microservice deal. Let's switch back to Node and change this up a little bit.

14:37

So we have been, um... Not that one. That one. We have been just returning a static string, which is not very interesting, um, I, I will admit. But what we can do is we can pull stuff off of this event. So let me make sure I did that right. That looks good. Let's deploy this again.

14:59

Switched it back to Node. New, um, function body. That's going out. So we have access to this event parameter, um, and so we can use that to start to drive a little bit of behavior from the monolith side without redeploying. And how do we pass that information in? We just use, um, execute.

15:24

So we can say name is Aaron. Let's make sure that deployed. That's out there. If we refresh again, we see "Hello, world, Aaron." So now we're driving behavior in a deployed microservice from the monolith side without redeploying. So we can ha- say, "Hello, Fullstack EU," right?

15:47

This is moderately interesting to change the name. I think what's really interesting is you control both sides, right? You control the Laravel side in this case, and you control the microservice side. It's all right there together. It's super tightly coupled. Like, that is the point. I'm aware of that. I, I did that on purpose.

16:08

And so it's easy to reason about, I think, and you can drive extremely, um, sophisticated behavior from the monolith side without redeploying the Lambda, right? So if you start to think, what if I'm doing, what if I'm doing, like, web scraping or screenshots or, um, PDF generation or something?

16:30

You can start to pass the URLs from your monolith side into the microservice, and that microservice handler is set up to expect the things that you're passing because you wrote them both. And so now you're able to drive all kinds of wacky behavior without redeploying, right? So one last thing I wanna show you in code here is let's say, um, you have a process that takes a long time.

16:58

So you have, you have some sort of like, um... Let's say you have a Python, like, data analytics process, right? And it takes, it could take 15 minutes. That's legal on Lambda. It can take up to 15 minutes. Um, let's say it takes two and a half seconds here, so we're just gonna fake it. We're gonna pretend that it takes two and a half seconds.

17:19

I should probably deploy this. So if this process takes two and a half seconds, um, and you wanna run a bunch of them, you don't wanna back up behind each other, right? So that's deployed. If we go back to web, and we say sidecar multi and format that.

17:40

This is a way to do it. This is a bad way to do it. So this is a way to do it. This is r- run one, run two, run three, and then return all the bodies back to the browser. If we run this, that was deployed, right? If we run this, this is gonna take about eight seconds, 'cause it's gonna take two and a half, two and a half, two and a half, a little bit of overhead, finally return it all to the browser, and it's going to take forever.

18:05

Should've made it shorter. Here we go. So- That's not ideal, right? 'Cause I told you that one of the promises of this Sidecar thing is that you can scale parts of your service independently. So it seems like we're still pretty constrained, right? So that's the bad way to do it. Um, if we change this to execute many, have a little bit of formatting to do here.

18:29

So we can pass through, um, we can pass through a couple of different payloads, and they'll be executed at the exact same time. So if we collect that, map over it, pluck the bodies out, return those to the browser. So now what we're gonna do is we're gonna say, instead of doing it the dumb way, one, two, three, do it the smart way, one, two, three, all at the same time, and then re-return those back to Laravel.

18:57

So this should take about two and a half seconds, and it's kinda hard to see because that wasn't counting. So let's prove that by three, six, nine, twelve, fifteen, whatever. So we know that it's not gonna take thirty seconds. It should take about two and a half seconds plus a little bit of overhead, right? So now we're doing, like, thirty-five, forty seconds worth of processing in two and a half seconds because we were able to fan that out to a bunch of different Lambdas.

19:26

Lambda is infinite or whatever, um, so you can fan it out as far as you wanna go. There are some people using this, fanning it out to fifty to five hundred different, um, consecutive invocations. They're doing, like, 3D model generation, and they're-- I don't really understand how they're doing it, but they're slicing part of it up, sending it off to Lambda, Python does some stuff, send it back, and they put it all together.

19:49

So that is how we would fan it out, um, horizontally, and that's where you start to get some of the benefit of using one of the serverless platforms 'cause they'll handle that for you. Um, if you wanna scale it up vertically, you can go into each different function. Um, each different function gives you a memory, right?

20:11

And so if you are doing, let's say, Chrome, you definitely need to be over ten twenty-four, but you can do four gigs of memory for your Chrome process, but not four gigs for some other process. Or if you wanna go crazy, you can give it ten gigs if you're doing some sort of Python data analysis. You'll pay through the nose for it, but it might make sense for your business, right?

20:32

So each function, depending on how it's invoked, can be scaled horizontally, and depending on how it's configured, can be scaled vertically. This will give you more processing power along with more RAM. So this is kind of the, um, this is kind of the scaling promise of Sidecar. Let's go back, and I'll talk about a little bit more.

20:55

Um, let me get a glass of water here. Um, we can skip this slide, fortunately. Um, so let's talk about, ooh, the deployment story for a second. So I showed you all the development, which I find very interesting, um, and I kinda breezed over the deployment.

21:18

I just showed you, like, AR Sidecar deploy activate. One of the things that I told you, um, is that these are deployed from your monolith, which is what we were doing. And one of the promises here is that, um, you don't have to worry about them getting out of sync, right? So you don't have to worry about, um, your monolith being a version or two ahead and the, uh, the microservice expecting some different format.

21:44

This is a screenshot of a Vapor YAML file. It doesn't really matter. What matters is this is, um, a Laravel deploy process, and they give you two different hooks in this one, and you can kinda translate this to your own deploy process. They give you a build hook that happens usually on, um, CI or CD. They give you a build hook that kinda, like, gets everything ready and sends it out to, in this case, Vapor.

22:10

And then they give you a deploy hook, which is like, "Okay, the app is about to be switched on live. Do you wanna do anything?" This is usually, like, run migrations, you know, do whatever. The important part is, with Sidecar, you can separate your deployment process just like every deployment process you already have, right?

22:31

So in the build step, you can say to Sidecar, "I want you to deploy my microservices." What this does is this sends them out to Lambda. It does all the packaging, which could take, you know, if you're bundling Node modules, could take forever. But it does all the packaging, which could take a long time, right? And then it sends it to, to S3 and then to Lambda, and then it just sits there.

22:53

It, it's not actually able to be used when you call deploy. It's able to be used when you activate it. So Lambda has this notion of, like, aliases. And so right before, right before your monolith goes live, where you would run migrations and, like, say, "All right, it's time to turn on the lights," you tell Sidecar, "Those functions that we just deployed, I want you to activate them now 'cause I'm ready to use them." And so your monolith stays in sync with these

23:24

microservices, and at the last second, both of them go live together. You can imagine this step is pretty quick 'cause all it's doing is it's sending an, uh, API request to AWS saying, "You already have the function. Add the alias." That's it. And so it's nice to remove that mental overhead and keep these things in sync just by adding these two lines to your existing deploy process.

23:48

I wanna talk about, um, a case study on syntax highlighting. I'm very familiar with this case study because it is about me, which I don't know if that's legal for case studies, but this is the reason that Sidecar exists. So I was writing some documentation for s- for another product, and the syntax highlighting was all wonky. It was, like, missing a bunch of stuff, and so I did what every developer does, and I stopped writing documentation and started building something new, right?

24:15

And that's where, that's where, um, Torchlight came from, and that's where Sidecar came from too. So- Torchlight is a syntax highlighting API. So you would install it in your app, um, the clients send me unhighlighted code blocks, I send them back highlighted code blocks. So it's a little different than every other syntax highlighter in that it runs on my server, not in your user's browser, right?

24:40

So that's the situation. Here's kind of how it was set up in the beginning, was I had... This was actually on Vercel at the time. Um, I had one endpoint that just received code blocks, and I was like, "This is great. I'm a genius." Well, it worked for a little while. It worked when people would send me, like, five or 10 code blocks in one request, right?

25:03

So I don't get them one by one, I get them as an entire chunk because it has to make a network round trip, so I wanna gather them all up. So I would get five or 10 code blocks, and I could highlight those in time and send them back and everybody was happy. People started sending me 50, 100, 200 code blocks at once, and I was like, "I can't, I can't do this.

25:24

I can't highlight these fast enough." So now I have this endpoint. This is, this is in my Laravel application. I have this endpoint, um, that does the highlighting. The first thing that happens is this partition, um, call. And so I take all of those code blocks, split them up by links, and then kind of, you know, put them together into reasonable units.

25:46

And then I use Sidecar's execute_many to fan those out to Lambdas so that if somebody sends me 100 code blocks, I may have 50 Lambdas running, each one doing two, and then I get all the results back, collate them, and send them back to the user. And so now this is, this is what the infrastructure looks like. I have this Laravel router that all it does is caching and partitioning, basically, and then the Lambdas do the rest.

26:12

And is this, like, the most sophisticated structure in the world? No. But as one person who loves Laravel and knows a little bit about JavaScript, this is great. I don't have to worry about this. And so this is the way that I use it. Um, a lot of people are using it to, uh, drive headless Chrome, so there's a lot of PDF and image generation going on with Sidecar because with Lambda, you can add, um, Lambda layers, and there are layers available that contain Google Chrome.

26:43

And so people are just slapping layers in there, and they don't have to worry about, um, compiling Chrome to fit into a Lambda. So people are using it for that. A lot of people are using it for server-side rendering. So if you have a full JavaScript SPA in the front and you, um, you wanna do server-side rendering, you need a node process running somewhere to do that usually.

27:06

And some people are just farming that out. If you can believe it, they're farming that out to Lambda, and they're running tens of millions of requests through that every month to do their server-side rendering. And so those are some of the things that people are doing. Every week or two, someone will tell me something that they're doing, and I'm like, "I don't-- I literally don't understand what you're doing, but I love that it's working for you." Um, so like I said, this is, this is where I'm at.

27:33

This is as far as my skills have gotten me. Um, the Laravel to Lambda is what I was showing you just now. Laravel to Vercel is open as a pull request that I haven't quite finished, but it's open on the repo. Um, this is what I would love to happen. So if you find this pattern compelling, if you think that this seems like a cool idea and could be fun for your community to have, um, and you want to work together on it, I would love that.

28:02

That would be so fun for me. If you just wanna take this and go do it yourself, that's also great. It's totally open source. Um, but I would be happy to help in any way that I can. So while I am here, I am hoping that someday we'll, we'll all be here, and this can be a nice pattern, um, to reach for when you don't wanna break out of the monolith, but you do need something shaped a little bit like a microservice.

28:28

So my last, um, my last charge to you is to consider this pattern. Consider putting a microservice in your monolith before, um, you bite the bullet and go full microservices. You can find, um, the documentation and, um, the GitHub links, everything at hammerstone.dev/sidecar, and thank you so much for having me.

Get the next one by email

I’ll send you some more. Not too many.