|
| 1 | +# 3. Deploy to AWS Lambda |
| 2 | + |
| 3 | +In Lesson 1/2 I created a fully working setup for comfortable development on my local machine. (No database yet – that comes in the next lesson.) |
| 4 | + |
| 5 | +Let's deploy everything to AWS Lambda now! |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## Off-topic |
| 10 | + |
| 11 | +But first let me talk a little bit about my current setup. |
| 12 | + |
| 13 | +As you noticed, I do not use the default Rust/Cargo bin/lib project entry points. Instead, I define my own custom main source files. This introduces a sort of duplication in the `Cargo.toml` files, which isn’t that bad; however, some may prefer to stick with Rust/Cargo defaults. So I will create an additional example function called `function_four` to illustrate an alternative approach. |
| 14 | + |
| 15 | +```bash |
| 16 | +cd app/functions/ |
| 17 | +cargo new function_four |
| 18 | +``` |
| 19 | + |
| 20 | +`app/functions/function_four/Cargo.toml` |
| 21 | + |
| 22 | +```toml |
| 23 | +[package] |
| 24 | +name = "function_four" |
| 25 | +version = "0.1.0" |
| 26 | +edition = "2021" |
| 27 | + |
| 28 | +[dependencies] |
| 29 | +tokio = { workspace = true } |
| 30 | +serde = { workspace = true } |
| 31 | + |
| 32 | +# internal dependencies |
| 33 | +lambda_http_wrapper = { path = "../../libraries/lambda_http_wrapper"} |
| 34 | +``` |
| 35 | + |
| 36 | +`app/functions/function_four/src/main.rs` |
| 37 | + |
| 38 | +```rust |
| 39 | +use lambda_http_wrapper::Error; |
| 40 | + |
| 41 | +use lambda_http_wrapper::run; |
| 42 | + |
| 43 | +mod controller; |
| 44 | +use controller::handle; |
| 45 | +mod types; |
| 46 | + |
| 47 | +#[tokio::main] |
| 48 | +async fn main() -> Result<(), Error> { |
| 49 | + run(handle).await |
| 50 | +} |
| 51 | +``` |
| 52 | + |
| 53 | +`app/functions/function_four/src/types.rs` |
| 54 | + |
| 55 | +```rust |
| 56 | +//! This module defines the “API types” that your controllers use. |
| 57 | + |
| 58 | +use serde::{Deserialize, Serialize}; |
| 59 | + |
| 60 | +/// Your application’s Request – you can add more fields as needed (e.g. headers, body). |
| 61 | +#[derive(Debug, Serialize, Deserialize)] |
| 62 | +pub struct Request { |
| 63 | + /// For simplicity we extract just the query parameters. |
| 64 | + pub name: String, |
| 65 | +} |
| 66 | + |
| 67 | +/// Your successful Response. |
| 68 | +#[derive(Debug, Serialize, Deserialize)] |
| 69 | +pub struct Response { |
| 70 | + pub message: String, |
| 71 | +} |
| 72 | + |
| 73 | +/// Your Error Response. |
| 74 | +#[derive(Debug, Serialize, Deserialize)] |
| 75 | +pub struct ErrorResponse { |
| 76 | + pub error: String, |
| 77 | +} |
| 78 | +``` |
| 79 | + |
| 80 | +`app/functions/function_four/src/controller.rs` |
| 81 | + |
| 82 | +```rust |
| 83 | +use crate::types::*; |
| 84 | + |
| 85 | +/// This is “business logic” controller |
| 86 | +pub(crate) async fn handle(req: Request) -> Result<Response, ErrorResponse> { |
| 87 | + // For example, get a "name" from the query string (defaulting to "world") |
| 88 | + let name = req.name; |
| 89 | + |
| 90 | + // Here you would call into your service layer, etc. |
| 91 | + Ok(Response { |
| 92 | + message: format!("[Function_4] Hello {}, this is an AWS Lambda HTTP request using controller wrapper to avoid lots of boilerplate", name), |
| 93 | + }) |
| 94 | +} |
| 95 | +``` |
| 96 | + |
| 97 | +This approach has its benefits: next time you want to add another Lambda function to your project, you simply copy an existing function’s directory, change the package name in its `Cargo.toml`, add it to the top-level workspace `Cargo.toml`, and (optionally) modify its request/response types and controller logic. You can choose between using the default approach or my fully custom approach — the choice is yours. |
| 98 | + |
| 99 | +## Deploying to AWS Lambda using cargo-lambda |
| 100 | + |
| 101 | +In project root: |
| 102 | + |
| 103 | +```bash |
| 104 | +$ cargo lambda build --release |
| 105 | + |
| 106 | +$ cargo lambda deploy function_one |
| 107 | +✅ function deployed successfully 🎉 |
| 108 | +🛠️ binary last compiled a minute ago |
| 109 | +🔍 arn: arn:aws:lambda:eu-central-1:014498641106:function:function_one |
| 110 | +🎭 version: 1 |
| 111 | + |
| 112 | +$ cargo lambda deploy function_three |
| 113 | +✅ function deployed successfully 🎉 |
| 114 | +🛠️ binary last compiled 12 minutes ago |
| 115 | +🔍 arn: arn:aws:lambda:eu-central-1:014498641106:function:function_three |
| 116 | +🎭 version: 1 |
| 117 | +``` |
| 118 | + |
| 119 | +Perfect. The deployment tool takes care of building and uploading my functions to AWS Lambda. |
| 120 | + |
| 121 | +Function cold startup time is about 35–50ms, while execution durations are around 1ms (typically billed as 2ms when rounded up). I can see the functions in the AWS Console and test them using the `apigateway-http-api-proxy` template. Note that when testing a Lambda function in the AWS Console, you must supply all the required internal details. For example, the API Gateway proxy template supplies a base64‑encoded body (which you can decode with [base64encode.org](https://www.base64encode.org/)): |
| 122 | + |
| 123 | +```json |
| 124 | +... |
| 125 | + "body": "eyJuYW1lIjoiZnVuY3Rpb25fb25lIn0=", |
| 126 | +... |
| 127 | +``` |
| 128 | + |
| 129 | +Testing in the AWS Console yields a response like this: |
| 130 | + |
| 131 | +```json |
| 132 | +{ |
| 133 | + "statusCode": 200, |
| 134 | + "headers": { |
| 135 | + "content-type": "application/json" |
| 136 | + }, |
| 137 | + "multiValueHeaders": {}, |
| 138 | + "body": "{\"message\":\"[Function_1] Hello function_one, this is an AWS Lambda HTTP request using controller wrapper to avoid lots of boilerplate\"}", |
| 139 | + "isBase64Encoded": false, |
| 140 | + "cookies": [] |
| 141 | +} |
| 142 | +``` |
| 143 | + |
| 144 | +This is also how it appears on the AWS Lambda Dashboard: |
| 145 | + |
| 146 | + |
| 147 | + |
| 148 | +## Configure AWS locally |
| 149 | + |
| 150 | +For me, deploying to AWS works right away because I’ve used the AWS CLI many times and it’s already configured on my machine. If you’re setting this up for the first time and you don’t have the AWS CLI installed or configured yet, follow these steps: |
| 151 | + |
| 152 | +### 1. Install AWS CLI |
| 153 | + |
| 154 | +**macOS:** |
| 155 | + |
| 156 | +If you have Homebrew installed, run: |
| 157 | + |
| 158 | +```bash |
| 159 | +brew update && brew install awscli |
| 160 | +``` |
| 161 | + |
| 162 | +**Windows:** |
| 163 | + |
| 164 | +Download the AWS CLI MSI installer from the [AWS CLI Installation page](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) and run the installer. |
| 165 | + |
| 166 | +**Linux:** |
| 167 | + |
| 168 | +Download and install using the following commands: |
| 169 | + |
| 170 | +```bash |
| 171 | +curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" |
| 172 | +unzip awscliv2.zip |
| 173 | +sudo ./aws/install |
| 174 | +``` |
| 175 | + |
| 176 | +After installation, verify the installation by running: |
| 177 | + |
| 178 | +```bash |
| 179 | +aws --version |
| 180 | +``` |
| 181 | + |
| 182 | +You should see output similar to `aws-cli/2.x.x Python/3.x.x Linux/...`. |
| 183 | + |
| 184 | +### 2. Obtain Your AWS Access Keys |
| 185 | + |
| 186 | +1. Log In to the AWS Management Console: |
| 187 | + Go to **AWS Console** and log in. |
| 188 | + |
| 189 | +1. Navigate to IAM: |
| 190 | + In the **AWS Console**, search for and open **IAM** (Identity and Access Management). |
| 191 | + |
| 192 | +1. Create a New User (if needed): |
| 193 | + |
| 194 | + - In the **IAM** dashboard, click on Users in the sidebar. |
| 195 | + - Click **Add user**. |
| 196 | + - Enter a `username` (e.g., aws-cli-user). |
| 197 | + - Select `Programmatic access` as the access type. |
| 198 | + - Click **Next: Permissions**. |
| 199 | + - Attach a policy such as `AdministratorAccess` for testing (for production, consider a policy with only the required permissions). |
| 200 | + - Click through to create the user. |
| 201 | + |
| 202 | +1. Save Your Access Keys: |
| 203 | + After the user is created, you’ll see an `Access key ID` and a `Secret access key`. Save these credentials securely (you won’t be able to see the secret access key again). |
| 204 | + |
| 205 | +### 3. Configure AWS CLI |
| 206 | + |
| 207 | +Open your terminal and run: |
| 208 | + |
| 209 | +```bash |
| 210 | +aws configure |
| 211 | +``` |
| 212 | + |
| 213 | +When prompted, enter the following: |
| 214 | + |
| 215 | +- `AWS Access Key ID`: (your access key ID) |
| 216 | +- `AWS Secret Access Key`: (your secret access key) |
| 217 | +- `Default region name`: (e.g., eu-central-1 or the region you use) |
| 218 | +- `Default output format`: (e.g., `json`) |
| 219 | + |
| 220 | +These settings are saved in `~/.aws/credentials` and `~/.aws/config`. |
| 221 | + |
| 222 | +### 4. Test Your Configuration |
| 223 | + |
| 224 | +Run a simple command to confirm that your AWS CLI is properly configured: |
| 225 | + |
| 226 | +```bash |
| 227 | +aws sts get-caller-identity |
| 228 | +``` |
| 229 | + |
| 230 | +You should receive a JSON output with details about your AWS account. |
| 231 | + |
| 232 | +Note: Keep your AWS credentials secure and never expose them publicly or commit them to version control. |
| 233 | + |
| 234 | +Now you’re ready to deploy your Lambda functions using `cargo lambda deploy...` — the AWS CLI will supply the necessary credentials for authentication. |
0 commit comments