AWS re:Invent 2020: Serverless, the Swift way

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Hi there, happy re:Invent wave two. My name is Nicki, I'm a senior software engineer here at AWS. I work on the Mobile SDK team and amplify. So all things Mobile is usually my domain. But I love serverless. And I have been using Lambda since it was released back in, I think, 2014, 2015, that timeframe. And I also love Swift, which, coincidentally got released around the same time. So it's really nice to see my favorite two things come together. So I'm really excited to be giving this talk to you today, about how to create serverless applications in Swift or serverless functions. So, you know, without further ado, let's dive in. Let's get started. All right, so what are we going to do today? Well, the first thing we're going to do is we're going to talk about the new Swift AWS Lambda runtime. The Lambda runtime is a project that took a combined effort from the folks at Apple and AWS, and it is awesome. And I'm hopefully going to show you everything that you need to know about it today. I'm going to show you how to use it, and then we're going to do a demo of how to create a Lambda, and deploy it to AWS. And then, you know, we're going to talk a little bit about performance in the end. And that should be it. Hopefully, you guys will get started with building your own Swift Lambdas after you watch this talk. All right, so what is it? So the Swift Lambda runtime is a Swift package. And it's basically built to help you put out custom runtime Lambdas in Swift. So let's talk about what custom Lambda runtimes really are. So you can implement AWS Lambda runtime in any programming language, right. And what is a runtime? A runtime is essentially a program that runs a Lambda functions handler when the function is invoked. And the way that you distribute a runtime, or the way that you give Lambda a runtime is through an executable, called bootstrap. So if you look at this picture here, I, you know, dissected a Lambda that I zipped up that I deployed. And you can see that there is an executable here named bootstrap. The runtime is essentially responsible for setting up the function setup code, and communicating back and forth between the Lambda platform and your functions handlers. So delivering any events to the handler and then receiving those responses and delivering them back to the Lambda platform. And so that's basically what it does. So the Swift Lambda runtime is essentially a library that implements the Lambda runtime API, and essentially does the work for you of creating a custom Lambda runtime in obviously the language of our choice, Swift. So it makes it super easy to create Swift Lambdas. And we are going to see exactly how that works. So they wrote several different APIs for you to write a Lambda. So you can see here, if you look at the code, we're actually importing the AWS Lambda runtime, and the Lambda runtime is easily incorporated into your application via a Swift package. So you're going to create a Swift package, right, and then you're going to, in your package manifest, use the Swift Lambda runtime as a dependency. And so in my Swift code, I can then import this module. And then, you know, you see here, I'm just creating an input. And I'm conforming it to codable as well, for easy serialization and deserialization from and to JSON. same thing with the output. And then all I do is called Lambda.run. And I essentially in that closure, I provide my user defined function or my code, essentially, and then I give the output that I told Lambda that I was going to give, you know, in the form of result, so .success, right to the callback. And that's essentially it. That's the whole thing. It's really, really simple and really easy to write Lambda functions. So what about events. So, you know, you might write a Lambda function that receives an event from somewhere else inside AWS. Maybe you write a Lambda function that receives an event from an SQSQ, maybe you write a Lambda function that receives an event from API gateway, to make your, you know, endpoint available to the outside world. And so who wants to write all of those inputs as conforming decodable and exactly what they're? You don't have to. They wrote another module for you called AWS Lambda events, where you can just directly use it and you get all of those essentially struct that already conformed to codable for you right there, and you just use them just like any other object. So, you know, in this case, in this example, I am receiving an SQS message. And so all I had to do was essentially import it, and then write the type and that's it, then I can just use it. And so, you know, I did mention that even API gateway events and, you know, this is super handy now because you can write your own back end, especially if you're an iOS developer, in the language that you know and love. Just by putting Lambda and API gateway together, you can easily create a REST endpoint. And, you know, I have examples for how to do that. And I will show you in my demo, exactly how I used Lambda and API gateway in a real life use case in an iOS application. All right, so what about performance? It's a good question. So, you know, in this first API that I showed you, you know, where you call Lambda.run and you just provide the closure that is going to be the higher level abstraction API. And what I mean by that is, if you choose to use that API to write your Lambda code, that Lambda is actually doing thread switching. And so it's going to run your user defined code on a separate thread than the one that's actually interacting with the Lambda platform. So there's a network processing thread happening, where the runtime engine is interacting with the Lambda platform, and then your code is running on a separate thread. Now, because it's running on a separate thread, there is context switching between the threads, which means that there is a bit of a performance hit, not a huge one, but there is a performance it. So if you have a performance sensitive function, maybe you're really, really, you know, you really want your Lambda to be extremely performant for whatever reason, you might choose to use the event loop Lambda handler API. And so the way that you would actually implement this API is, it's a protocol. And you would just implement it in a struct, and then inside handle, you would write your code. Now, in this example, in this code that you're looking at, the code inside handle is going to run on the same thread that is, again, interacting with the Lambda platform. So you need to take caution and care to not write, you know, any blocking code, essentially, that would block the thread from communicating with the Lambda platform. So if you're going to use this API, it's obviously more performant. But you need to be very sensitive and cautious in the way you approach it, to make sure you're not writing any blocking code for any reason. And then again, obviously, all you do is you just pass your implemented protocol to Lambda.run, and that's it. And that's how you use this API. So let's talk a little bit about what's under the hood. So in the higher level, like abstraction, you are essentially using the Lambda handler, which implements event loop Lambda handler for you. And as you can see, there is an offloadQueue here, because as I mentioned, it's going to run your code in a separate thread from the thread that's actually communicating with the Lambda platform. And that's why you see this offloadQueue here. Now, again, you do take a slight performance hit when you do that. But if you're just writing regular Lambdas and you're not really worried about, you know, performance or cold start necessarily, which in a lot of cases is not necessarily that important, you can absolutely use this API. And let me just also say, I have written several Swift Lambdas now and I've used just the regular run of the mill API, and I haven't really needed to go beyond that. So I just want to emphasize that, but I did want to include it in this talk. Because if you did have something advanced, or extremely performance sensitive, you would definitely want to take advantage of the other API. Okay, so the other API, let's take a deeper look at it here. So it is event loop Lambda handler, it conforms to another protocol called byte buffer Lambda handler. And, you know, nothing essentially is happening here. There's no offloadQueue, because again, all the code here is running on the exact SAMe thread that is communicating with the Lambda platform. And, you know, it's just doing encoding and decoding from byte buffers to you, I mean, for you. And that's essentially that's it, that's all that this one's doing. And then the byte buffer Lambda handler, is essentially there to power those higher level protocols. And, you know, it's interacting at the byte buffer level. So there's really not a whole lot of use cases where you might use this one, but it is available for use if you needed to do some kind of special serialization. Or you, you know, you had a Lambda that you needed to interact with the byte buffer level for whatever reason. So I don't see this one being, you know, frequently used, but I did put it here, just so that you know about it, you're aware of it. And you have another tool in your toolbox in case you needed it. So yeah, essentially, that's the entire runtime library under the hood. I mean, there's a lot of other moving parts, but that's the base of it. And so, you know, you've written your Lambda, and you're like, well, I want to locally test my Lambda before I deploy it. Good question. How do you do that? It's actually way simpler than you could think of. They made it super easy for you. All you have to do is add an environment variable to your Xcode scheme. And I show you how to do that in the demo as well. And, you know, you set the value to true and that's it, and you can actually just click Play inside Xcode, and it will run your Lambda locally. I think it defaults to Port 7000, if I'm not missing taken, on an endpoint. And you can call it locally and test out your code before you ever deploy it to Lambda. All right, you can also fine tune with other environment variables. Log level is typically the one that people want to know about. The logging level is using Swift log. So if you ever used Swift log, you might be familiar with the API already. It's going to be set to info by default. And, you know, there's several other environment variables that you can use to fine tune your Lambda function. And you can check those out. All right. So you're like, I have my Lambda, have locally tested it, I've fine-tuned it, I chose the correct, you know, performance API to write my Lambda, how do I actually package and send it to AWS? Well, there's several ways that you can do this. The first way is, SAM, this is my favorite way. This is going to be the best way and the easiest way to consistently and repeatedly deploy your Lambda as you're developing it. The second is, you know, you can manually create a zip and upload it. And then I just want to emphasize that, you know, all these different ways, I will provide resources at the end so that you can choose the way that is works for you. I don't want to tell you how to do things or what way you should choose, you know, you find a way that you like. The third way is new, you know, just released last month at re:Invent. You can essentially give Lambda a container image directly. And then the last way is not technically a real thing yet, but I'm hoping by putting in here, you'll all go out and vote. So there's an RFC out in the Amplify CLI library. And Amplify CLI, if you're not familiar, is a really easy way to create AWS resources without having a lot of AWS knowledge actually. It's a very, very simple, easy to use CLI tool. And, you know, you can do something as simple as Amplify add API, and it essentially will create a Lambda and API gateway project for you. And wouldn't it be so nice if you could say Amplify add API, choose, you know, a REST endpoint, it creates the API gateway Lambda, and then you could choose Swift, and it will deploy the whole thing for you as simple as amplify push. So that doesn't exist yet. That's hypothetically what it would look like in my head. I don't know actual implementation. But ideally, but if you all go vote, hopefully, we can take it up as something that we actually do. So I'm trying to get the votes out there, you know, please vote for this RC. There will be a link to it at the end in my resource page. So hopefully you all vote, and we have a fourth way to distribute Swift Lambdas, which essentially is going to be great if you're an iOS developer, because you might already be using Amplify. Okay, let's see a demo. Let's get into the demo. Okay, so for this demo, we're going to do two things today. The first thing I'm going to do is I'm going to show you how to get up and running and build a Swift Lambda superfast and deploy it to AWS. And then, for the second thing, I actually built a full on demonstration of an iOS app with a Swift Lambda, to show you an actual use case for writing a Swift Lambda, maybe in your iOS development if you're an iOS developer. So let's get started. So the first thing we need to do is we need to create a new Swift package. So we need to create a new folder. And we're going to go into our folder. And then let's actually just run the command to create a new Swift package, fairly simple command, Swift package in it, and then you give the type, and then we can actually open this in Xcode. So here we have our Swift package with our sources. And we're going to actually edit the package now to take dependency on the Swift Lambda runtime. So I have this little handy gist that I wrote, so I can just steal my own code, don't have to write it again, and replace it here. And then we're going to steal the products that we want also, in the interest of time, obviously. And then I also want to run it locally, so I'm going to give it a platform. Okay, so then we click Save, and we should see our dependency on the left. Check it out, there it is. It's already been pulled down for us. So we should be able to just go to main and use it. So we can say import AWS Lambda runtime, there it is. Okay, so we need to create a struct as our input for our Lambda and conform it to codable so that it can easily be, you know, deserialized from JSON. So let's write our struct. And our struct is going to have, you know, one property in it of type string, and it's going to be named. This Lambda is going to be super basic, it's just going to return an output. And this output is going to have a message of type string. Okay, so now we can use the runtime to write our actual Lambdas. We're going to call Lambda.run and we're going to use this guy. So it's going to take a context. And then our input is going to be of type input. And we have a closure. We had to replace this because our output is called output. And then we need to name this parameter. Bingo. So now we can actually just call this with success, instantiate our output. And, you know, we want our message to basically be hi to whoever was given to us for the name. Okay, so now we've written our cool little simple Lambda, we need to actually test it. So it's actually really easy to do in Xcode. You can just add an environment variable called local_lambda, wow if I can type, server_enabled, and set it to true. And then we should be able to just click the play button, and it should run it locally. There it is, running locally on port 7000. And we can basically call /invoke. So we can actually test this locally, I usually just use Postman. We can literally have the test already written for us here. We can call /invoke, right, on port 7000. Name, we can give it a name for re:Invent 20. And then if we hit it, we should get our message back of hi re:Invent 2020. Cool, it works. Okay, so, now you probably want to deploy your Lambda. So we need a few things, we're not able to cross compile with Swift package manager yet. Obviously, there's always a hope that one day, so we need a Docker image so that we can run, you know, Swift builds for release inside a Linux Docker image with Swift to actually create the binary that we need to give to Lambda. I'm going to use SAM. As I mentioned, you know, before, there are multiple ways you can deploy to AWS. I created this handy little resource page for somebody to link to you at the end, of all the different ways that you could deploy with instructions for each way, whatever your preference is. I like to use SAM. My friend, Eric, created this nice little repo for us with a make file, a Docker file and a SAM template in it. And so I stole some pieces of this. And so we're actually going to use SAM to deploy it. So let's create a Docker file. And we need an image. So luckily, the Swift team over at Apple, they write a bunch of images. So let's just steal this one for Amazon Linux 2. And let's create our Docker file. Bingo, now we have an image. And then we're going to create a make file. And this make file all it's going to do, we're going to steal this lovely gist that I made. So I actually took the make file that my friend Eric created, and I edited it slightly to use static linking. And all that means is I added these two flags to basically build with static linking, you know, to improve performance. And then because I statically linked, I do not need to copy the dependencies, which is my make file does, but I do need to create a symbolic link. So I'm doing that for you here. So you can use my make file for static linking and his for dynamic. Okay, so every time I paste this, it never works, I always have to end up opening it in VS code. So let's go ahead and actually do that. VS code obviously is more handy for, you know, syntax with the make file and the Docker file. Xcode doesn't help me out there. So yeah, let's do tab size, so maybe it'll work. Okay, so now we need a template.emo file. So let's create one of those. And let's steal his template and edit it slightly. So he wrote this great little template, right. It's just a very simple SAM template. And this SAM template is actually setting up two functions. He has two functions. Here one function is setting up an API endpoint with a path for a get method, and the other one is just creating a function. For the purposes of this demonstration, we're just going to create a function, so we don't need this one. And let's see edit this to be re:Invent Lambda. And then you can see how we're actually telling SAM that we want the build method to be make file, which is true. We do not need an output because we are not spitting out an API endpoint. And that's basically it. There's our SAM template. So now what we should be able to do is we should be able to call SAM build. And, of course, there is a problem. Huh? Am I in the right directory? Maybe. Okay, different error. All right. So it looks like there's something wrong with our make file. So let's just see what happens when we just run the make file into… oh, here we go. I didn't edit this. This is my bad. So you need to edit this with the name of your Lambda, which ours is re:Invent Lambda. Okay, and let's just call it just to make sure that it works. Okay, I told you that it always has a formatting problem. Make files very specific with their tabs, you have to have 4, 1, 2, 3, 4. Okay, let's see. Bingo, we're in business. Okay, so we actually could have just called SAM build, we didn't need to individually build with make. But, you know, we're just testing and seeing if it works. So once this is done, we're going to call SAM build, to build our SAM template and use our make file, essentially. And if you look at our make file, our make file is actually just calling Docker run on our image. And then it's calling this command inside the image, Swift build product release with our static linking flags, and then creating that symbolic link. And that's all it's doing. There's nothing complicated about the make file. So, looks like it's almost done compiling here. And then once we've called SAM build, we can call SAM deploy. And we can deploy this thing to Lambda. SAM, in my opinion, is the easiest way to deploy your custom runtime Lambda to Lambda. But, you know, everybody's got a preference. So I did provide multiple ways to deploy as a Lambda, with instructions, depending on your preference. I'm definitely a SAM kid. All right. Come on. Come on Swift. Finish it up. Let's go, 24 out of 24. Any day now. Real life development for you. Waiting. I hope it didn't error. I feel like I can hear my computer like, struggling. Oh, we're getting somewhere. Oh, and there's apparently more steps. All right, slow. Normally, if this were live, I would use this opportunity to interact with you guys. Fortunately, we are all stuck at home. All right. Let's see. So hopefully this thing finishes pretty quickly here. And then we can call SAM build and SAM deploy. Bingo. All right. Whoops, don't want to type there. That was a mistake. SAM build. Okay, so SAM build is essentially using the make file. So it's going to run the same steps that we just ran. It's pretty pointless to just, yeah, there we go. It succeeded. So now we can call SAM deploy. And we can actually use this handy parameter called guided to guide us to help us to run SAM deploy. Otherwise, you have to pass the parameters individually. So I love this. So stack name, re:Invent. What should I call this? Swift Lambda stack. How about that? And then the region I like is US-West-2. So this is asking if I want to confirm changes before deploying. So the next time I deploy, should it ask me to confirm or not? For the purpose of this demo, no, doesn't matter. Yes, I do want to allow IAM role creation. Yes, I want to save my arguments to a config file. That's a perfectly fine configuration file name. And the environment could be default. I mean, you might name it prod or dev or whatever. But this is just a demo, so doesn't need to be anything beyond that. I'm not going to use this Lambda anywhere in production. Okay, so now it's basically deploying the stack to cloud formation. Looks like it's pretty quick, it's almost done. It's creating the roles necessary, which is so nice. I love SAM that does all this for you. And then we should be able to see our Lambda inside our console, and we can actually run it there, too. And it should work, hypothetically, always hypothetically. Cool. It's successfully created. So let's go to Lambda. And let's search for our Lambda. There it is. And we can test it using our handy JSON objects that we know. Get rid of all this. Bam. And run our test, boom, succeeded. There it is, there's our output. That's it. That's basically how to quickly create a Swift Lambda and get it up on AWS. So now I'm going to show you the actual demo that I built. So I built this app. And all this app is, is it's an app that loads my in app purchases that I have created in my App Store Connect account. So I have two consumable things that somebody can purchase. And so it's just listing them out. So I actually have my phone here for you, and unlock it. And this is what the app looks like. So it's displaying both of these products. And this is actually, what it's doing is it's actually calling a Lambda function to validate the receipt and displaying a checkmark if there's a valid receipt for that product, so if that product was purchased. And if it wasn't purchased, it's just going to display a Buy button and let you buy it. But I bought both of these products, bought both of these products before this demo. And so you see how this actually displaying the checkmark. So the receipt manager is calling a Swift Lambda to validate the receipt, and it's a Lambda that I actually deployed. And I'll show you the Lambda really quickly. But all it is, is just making the call using URL session to my endpoint. And so if we take a look at my Swift Lambda, my Swift Lambda is using this awesome package written by my friend Fabian, it's now been transferred to another person's repo. But it's still available, it's open source. And you can you can look for it App Store receipt validation, I will also provide a link to it as well. And all it's doing is it's basically instantiating their client. You need to provide a secret if you have auto renewable subscription products. And so an easy way to do that is to just create an environment variable in your template. So I actually did this for you in my little templates so that you could come in and replace it if you have auto renewable subscriptions that you're going to use. And you can actually pull using this get environment variable, I wrote this little function for you as well. So you can literally just come in here and uncomment this line if you need a secret and pass it in. But I have a consumable product, so I don't use secrets, so I just pass nil. And then I'm calling their validate receipt endpoint with my receipt. So my input is an API gateway event, because I set up, obviously, API gateway with my template. And then, you know, I extract my actual input from there, which just has a receipt on it. I call validate receipt. And then for subscriptions, you know, if you're validating subscriptions, you want to compare expiration dates. But for consumable purchase, you know, you just want to make sure that when the receipt is return, there's something inside in app. So I can say valid is true, if there's something in there and spit out my output, encode it and send it back to API gateway, which sends it back to my app. And then amazingly, displays that checkmark for you. So that that's basically it working live. And luck again, for you in action. So that's pretty awesome. That's the demo I built. It's going to be up for you and linked for you in the resource page. And yeah, let's get back to the slides. All right, so now that you've seen a demo of how to easily create a Swift Lambda and deploy it to AWS using SAM, and you saw my cool demonstration of it actually working inside of an iOS app with a real live use case, let's talk about other performance tweaks that you could make. So the first one… first of all, I want to preface this with, I don't want you to take this to heart and really think heavily about this. Because I know that if you're an iOS developer, you heavily depend on the use of foundation. But if you have a performance sensitive Lambda, something that you could do is remove all use of foundation. So removing it from your user defined function, as well as not even using the regular AWS Lambda runtime module, but using runtime core instead. The regular AWS Lambda runtime module does make use of foundation for, I believe, encoding and decoding. And if you just use core and you implement the APIs yourself, you could eliminate all use of foundation, which would essentially improve performance. And the second thing you can do, I actually did in my demo, you can use static linking now in Swift, 5.3.1. So I showed you, if you remember, in the make file, there were two flags I added to actually build with static linking. And it's pretty easy to do. I also will provide a link on the static linking at the launch of it and a forum post from the folks at Apple in my resource page at the end. So you'll have that as well. But that is the image that you need to take advantage of if you'd like to use static linking, and then you obviously need to use the flags that I provided, a compile time, it's fairly easy to do so. Just so you get an understanding of how big the difference is, when you use static linking, I actually wanted to put this here for you. So you look at the cold start times, there's a significant difference between when you're using static linking and when you're using dynamic linking. Warm start doesn't affect it that much, it's pretty much the same, no matter which you choose. But it definitely affects cold start time. And so if you're sensitive to that, I would definitely compile with static linking. Dynamic linking, obviously, you don't really have to do anything. You just, you know, Swift build. It's fairly easy to do so. But you have to copy all the dependencies, as I mentioned before, into your package. So yeah, there's examples on how to do both. It's up to you, it's your preference, I definitely would prefer static linking, because I love that little performance boost. And then you know how to Swift stack up to all the other programming languages. If you know if you're going to take it on, and you've been a serverless developer for a long time, and you're like, oh, I'm really interested, I want to get started. But how does it compare? Well, it falls pretty much in the middle here. But they've been doing some serious improvements. So you see here that static linking is significantly higher than dynamic linking. And it's also now beating Go. So they have made that improvement since their release in June. So they are consistently and continuously iterating on the Lambda runtime and improving its performance. So who knows, maybe next year, I'll be talking about how it beats Node. By the way, the runtime library is open source. So if you would like to contribute to this project, I highly encourage you to do so, especially if you know anything about how to improve performance on Lambda, please contribute. Because I would definitely love to see Swift make it, you know, to the top of this list, because it's my favorite, obviously. All right, so that's basically it. That's everything you need to know about Swift Lambdas in a nutshell. That link is, again, linked to my resource page, which I created for you. It has links to all the repos of the Lambdas that I've built to date with the different ways to deploy them. And links to other videos of talks that I've done before on this topic, as well as all the resources that I've used and found online to essentially create Lambdas and deploy them to AWS. That's all I have for you folks. Thank you so much for listening, and I hope you have a great rest of your re:Invent wave two. And again, my name is Nicki, and have a great day.
Info
Channel: AWS Events
Views: 505
Rating: 5 out of 5
Keywords: re:Invent 2020, Amazon, AWS re:Invent, SVS205, Serverless, AWS Lambda
Id: tOwBaO0JAMs
Channel Id: undefined
Length: 33min 46sec (2026 seconds)
Published: Fri Feb 05 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.