285 lines
6.8 KiB
Markdown
285 lines
6.8 KiB
Markdown
---
|
|
tags: [AWS]
|
|
---
|
|
|
|
# AWS SAM
|
|
|
|
SAM stands for **serverless application model**. It is a framework developed by
|
|
AWS to simplify the process of building, deploying and managing serverless
|
|
applications. It provides a concise syntax for defining the components of a
|
|
serverless application, such as
|
|
[Lambda functions](Lambda_programming_model.md),
|
|
[API gateway](AWS_API_Gateway.md) and database tables.
|
|
|
|
The SAM infrastructure is defined in a YAML file which is then deployed to AWS.
|
|
SAM syntax gets transformed into CloudFormation during the deployment process.
|
|
(CloudFormation is a broader and more robust AWS tool for large, highly
|
|
scaleable infrastructures).
|
|
|
|
## Key features of SAM
|
|
|
|
- Single deployment configuration
|
|
- Integration with development tools
|
|
- Local testing and debugging
|
|
- Built on AWS CloudFormation
|
|
|
|
## Main technologies required
|
|
|
|
### Docker
|
|
|
|
Whilst SAM can be used to create a deployable file for AWS it can also be run as
|
|
a container for local development with Docker.
|
|
|
|
### AWS CLI
|
|
|
|
This is installed using Python and allows you to interact directly with AWS via
|
|
the command-line.
|
|
|
|
### AWS SAM CLI
|
|
|
|
See
|
|
[https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html](static/install-sam-cli.html)
|
|
|
|
## Setting up credentials for the AWS CLI
|
|
|
|
You require an access key for the given
|
|
[IAM user](AWS_User_management_and_roles.md#iam). You should create an IAM
|
|
account specific to the project with bounded permissions.
|
|
|
|
```
|
|
aws configure
|
|
AWS Access Key ID [None]: AK*******
|
|
AWS Secret Access Key [None]: ukp******
|
|
Default region name [None]:
|
|
Default output format [None]:
|
|
```
|
|
|
|
This information can be found in the Security Credentials section of the given
|
|
[IAM](AWS_User_management_and_roles.md#iam) user:
|
|
|
|

|
|
|
|
### Switching between credentials
|
|
|
|
You should set up a different IAM user for each project.
|
|
|
|
You can do this with:
|
|
|
|
```sh
|
|
aws configure --profile <profile-name>
|
|
```
|
|
|
|
This will then ask you to add the credentials for the user.
|
|
|
|
You can switch between different credentials for the user as follows:
|
|
|
|
```sh
|
|
AWS_PROFILE=<profile-name> sam build
|
|
|
|
```
|
|
|
|
## Starting a SAM project
|
|
|
|
First create a directory for your project which will serve as the repository:
|
|
|
|
```sh
|
|
mkdir aws-sam-learning
|
|
cd aws-sam-learning
|
|
```
|
|
|
|
Then we can use the `sam` cli to bootstrap the project:
|
|
|
|
```sh
|
|
sam init --runtime nodejs16.x
|
|
```
|
|
|
|
We can just click through and accept the basic HelloWorld Lambda.
|
|
|
|
This will create the Lambda as well as an API Gateway trigger URL.
|
|
|
|
### `template.yaml`
|
|
|
|
This is autogenerated and details the main constituents of the project. There
|
|
are lots of fields but the most important are the following:
|
|
|
|
```yaml
|
|
HelloWorldFunction:
|
|
Type: AWS::Serverless::Function
|
|
Properties:
|
|
CodeUri: hello-world/
|
|
Handler: app.lambdaHandler
|
|
Runtime: nodejs16.x
|
|
Architectures:
|
|
- x86_64
|
|
Events:
|
|
HelloWorld:
|
|
Type: Api
|
|
Properties:
|
|
Path: /hello
|
|
Method: get
|
|
```
|
|
|
|
This details the location of the [handler function](Lambda_handler_function.md)
|
|
which is contained at the path `hello-world/app.js`:
|
|
|
|
```js
|
|
exports.lambdaHandler = async (event, context) => {
|
|
try {
|
|
// const ret = await axios(url);
|
|
response = {
|
|
statusCode: 200,
|
|
body: JSON.stringify({
|
|
message: "hello world",
|
|
// location: ret.data.trim()
|
|
}),
|
|
};
|
|
} catch (err) {
|
|
console.log(err);
|
|
return err;
|
|
}
|
|
|
|
return response;
|
|
};
|
|
```
|
|
|
|
It also lists the `get` event that we can use to call API Gateway and trigger
|
|
the Lambda.
|
|
|
|
The full template is below:
|
|
|
|

|
|
|
|
## Adding our own code
|
|
|
|
We will create our own function and API Gateway trigger.
|
|
|
|
We will place our function after the existing `HelloWorldFunction`
|
|
|
|
```yaml
|
|
ClockFunction:
|
|
Type: AWS::Serverless::Function
|
|
Properties:
|
|
CodeUri: clock/
|
|
Handler: handler.clock
|
|
Runtime: nodejs16.x
|
|
Events:
|
|
ClockApi:
|
|
Type: Api
|
|
Properties:
|
|
Path: /clock
|
|
Method: get
|
|
```
|
|
|
|
We can test the syntax with:
|
|
|
|
```sh
|
|
sam validate
|
|
```
|
|
|
|
Just like with `HelloWorld`, we will create a directory for this function:
|
|
`clock` and we will initialise it as an `npm` project.
|
|
|
|
```sh
|
|
mkdir clock
|
|
cd clock
|
|
npm init
|
|
```
|
|
|
|
We will use `handler.js` as our root, handler function.
|
|
|
|
We have said in the template file that our `Handler: handler.clock`, therefore
|
|
the main function in the `handler` module should be `clock`:
|
|
|
|
```js
|
|
const moment = require("moment");
|
|
|
|
exports.clock = async (event) => {
|
|
console.log("Clock function run");
|
|
const message = moment().format();
|
|
const response = {
|
|
statusCode: 200,
|
|
body: JSON.stringify(message),
|
|
};
|
|
return response;
|
|
};
|
|
```
|
|
|
|
The directory structure is as follows:
|
|
|
|

|
|
|
|
When we call the API Gateway path `/clock` with `GET`, our function will be
|
|
triggered.
|
|
|
|
## Deploying the project
|
|
|
|
We will now deploy our project to AWS from the local environment.
|
|
|
|
The process is as follows:
|
|
|
|
1. Build
|
|
2. Package
|
|
3. Deploy
|
|
|
|
### Build
|
|
|
|
We need to install the runtime dependencies for the function. We do this by
|
|
running `sam build`. This ignores test files and development dependencies and
|
|
installs the project dependencies and source files to a temporary subdirectory.
|
|
|
|

|
|
|
|
The build directory is `.aws-sam/build/`. There will be a subdirectory for each
|
|
of our files.
|
|
|
|
### Package
|
|
|
|
As noted, CloudFront handles the deployment of the application. It can only
|
|
receive one file as an input. The packaging process consists in creating that
|
|
single file.
|
|
|
|
The packaging proces will first archive all of the project artefacts into a zip
|
|
file and then upload that to [S3](AWS_S3.md). A reference to this S3 entity
|
|
is then provided to CloudFormation.
|
|
|
|

|
|
|
|
The command is as follows:
|
|
|
|
```sh
|
|
sam package
|
|
--template-file template.yaml
|
|
--output-template-file pkg.yml
|
|
--region eu-west-1
|
|
```
|
|
|
|
This will automatically create a hashed bucket name for you in S3 (I have tried
|
|
to add my own naming but it doesn't comply.)
|
|
|
|
### Local development with Docker
|
|
|
|
In order to work with your application locally without actually sending requests
|
|
to AWS and using credit, you can run a local instance.
|
|
|
|
See [Local AWS Development with SAM](Local_AWS_development_with_SAM.md).
|
|
|
|
### Deploy
|
|
|
|
Once you have packaged the app you can deploy with `sam deploy --guided`. This
|
|
will talk you through the defaults and will deploy the package to
|
|
CloudFormation. In CloudFormation each individual project is called a **stack**.
|
|
|
|
If we then go to Cloud Formation we will see the deployed application.
|
|
|
|

|
|
|
|
## Call the endpoint
|
|
|
|
If we now go to the Lambda console, we will see our function listed, and the API
|
|
Gateway endpoint under `triggers`:
|
|
|
|

|
|
|
|
We can then call this from Postman to check everything is working as it should:
|
|
|
|

|