111 lines
No EOL
18 KiB
HTML
111 lines
No EOL
18 KiB
HTML
<!DOCTYPE html><html><head><meta content="text/html; charset=utf-8" http-equiv="Content-Type" /><meta content="width=device-width, initial-scale=1" name="viewport" /><!--replace-start-0--><!--replace-start-5--><!--replace-start-8--><title>AWS SAM - My Zettelkasten</title><!--replace-end-8--><!--replace-end-5--><!--replace-end-0--><link href="https://cdn.jsdelivr.net/npm/fomantic-ui@2.8.7/dist/semantic.min.css" rel="stylesheet" /><link href="https://fonts.googleapis.com/css?family=Merriweather|Libre+Franklin|Roboto+Mono&display=swap" rel="stylesheet" /><!--replace-start-1--><!--replace-start-4--><!--replace-start-7--><link href="https://raw.githubusercontent.com/srid/neuron/master/assets/neuron.svg" rel="icon" /><meta content="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, API gateway and databas" name="description" /><meta content="AWS SAM" property="og:title" /><meta content="My Zettelkasten" property="og:site_name" /><meta content="article" property="og:type" /><meta content="SAM" property="neuron:zettel-id" /><meta content="SAM" property="neuron:zettel-slug" /><meta content="AWS" property="neuron:zettel-tag" /><script type="application/ld+json">[]</script><style type="text/css">body{background-color:#eeeeee !important;font-family:"Libre Franklin", serif !important}body .ui.container{font-family:"Libre Franklin", serif !important}body h1, h2, h3, h4, h5, h6, .ui.header, .headerFont{font-family:"Merriweather", sans-serif !important}body code, pre, tt, .monoFont{font-family:"Roboto Mono","SFMono-Regular","Menlo","Monaco","Consolas","Liberation Mono","Courier New", monospace !important}body div.z-index p.info{color:#808080}body div.z-index ul{list-style-type:square;padding-left:1.5em}body div.z-index .uplinks{margin-left:0.29999em}body .zettel-content h1#title-h1{background-color:rgba(33,133,208,0.1)}body nav.bottomPane{background-color:rgba(33,133,208,2.0e-2)}body div#footnotes{border-top-color:#2185d0}body p{line-height:150%}body img{max-width:100%}body .deemphasized{font-size:0.94999em}body .deemphasized:hover{opacity:1}body .deemphasized:not(:hover){opacity:0.69999}body .deemphasized:not(:hover) a{color:#808080 !important}body div.container.universe{padding-top:1em}body div.zettel-view ul{padding-left:1.5em;list-style-type:square}body div.zettel-view .pandoc .highlight{background-color:#ffff00}body div.zettel-view .pandoc .ui.disabled.fitted.checkbox{margin-right:0.29999em;vertical-align:middle}body div.zettel-view .zettel-content .metadata{margin-top:1em}body div.zettel-view .zettel-content .metadata div.date{text-align:center;color:#808080}body div.zettel-view .zettel-content h1{padding-top:0.2em;padding-bottom:0.2em;text-align:center}body div.zettel-view .zettel-content h2{border-bottom:solid 1px #4682b4;margin-bottom:0.5em}body div.zettel-view .zettel-content h3{margin:0px 0px 0.4em 0px}body div.zettel-view .zettel-content h4{opacity:0.8}body div.zettel-view .zettel-content div#footnotes{margin-top:4em;border-top-style:groove;border-top-width:2px;font-size:0.9em}body div.zettel-view .zettel-content div#footnotes ol > li > p:only-of-type{display:inline;margin-right:0.5em}body div.zettel-view .zettel-content aside.footnote-inline{width:30%;padding-left:15px;margin-left:15px;float:right;background-color:#d3d3d3}body div.zettel-view .zettel-content .overflows{overflow:auto}body div.zettel-view .zettel-content code{margin:auto auto auto auto;font-size:100%}body div.zettel-view .zettel-content p code, li code, ol code{padding:0.2em 0.2em 0.2em 0.2em;background-color:#f5f2f0}body div.zettel-view .zettel-content pre{overflow:auto}body div.zettel-view .zettel-content dl dt{font-weight:bold}body div.zettel-view .zettel-content blockquote{background-color:#f9f9f9;border-left:solid 10px #cccccc;margin:1.5em 0px 1.5em 0px;padding:0.5em 10px 0.5em 10px}body div.zettel-view .zettel-content.raw{background-color:#dddddd}body .ui.label.zettel-tag{color:#000000}body .ui.label.zettel-tag a{color:#000000}body nav.bottomPane ul.backlinks > li{padding-bottom:0.4em;list-style-type:disc}body nav.bottomPane ul.context-list > li{list-style-type:lower-roman}body .footer-version img{-webkit-filter:grayscale(100%);-moz-filter:grayscale(100%);-ms-filter:grayscale(100%);-o-filter:grayscale(100%);filter:grayscale(100%)}body .footer-version img:hover{-webkit-filter:grayscale(0%);-moz-filter:grayscale(0%);-ms-filter:grayscale(0%);-o-filter:grayscale(0%);filter:grayscale(0%)}body .footer-version, .footer-version a, .footer-version a:visited{color:#808080}body .footer-version a{font-weight:bold}body .footer-version{margin-top:1em !important;font-size:0.69999em}@media only screen and (max-width: 768px){body div#zettel-container{margin-left:0.4em !important;margin-right:0.4em !important}}body span.zettel-link-container span.zettel-link a{color:#2185d0;font-weight:bold;text-decoration:none}body span.zettel-link-container span.zettel-link a:hover{background-color:rgba(33,133,208,0.1)}body span.zettel-link-container span.extra{color:auto}body span.zettel-link-container.errors{border:solid 1px #ff0000}body span.zettel-link-container.errors span.zettel-link a:hover{text-decoration:none !important;cursor:not-allowed}body [data-tooltip]:after{font-size:0.69999em}body div.tag-tree div.node{font-weight:bold}body div.tag-tree div.node a.inactive{color:#555555}body .tree.flipped{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}body .tree{overflow:auto}body .tree ul.root{padding-top:0px;margin-top:0px}body .tree ul{position:relative;padding:1em 0px 0px 0px;white-space:nowrap;margin:0px auto 0px auto;text-align:center}body .tree ul::after{content:"";display:table;clear:both}body .tree ul:last-child{padding-bottom:0.1em}body .tree li{display:inline-block;vertical-align:top;text-align:center;list-style-type:none;position:relative;padding:1em 0.5em 0em 0.5em}body .tree li::before{content:"";position:absolute;top:0px;right:50%;border-top:solid 2px #cccccc;width:50%;height:1.19999em}body .tree li::after{content:"";position:absolute;top:0px;right:50%;border-top:solid 2px #cccccc;width:50%;height:1.19999em}body .tree li::after{right:auto;left:50%;border-left:solid 2px #cccccc}body .tree li:only-child{padding-top:0em}body .tree li:only-child::after{display:none}body .tree li:only-child::before{display:none}body .tree li:first-child::before{border-style:none;border-width:0px}body .tree li:first-child::after{border-radius:5px 0px 0px 0px}body .tree li:last-child::after{border-style:none;border-width:0px}body .tree li:last-child::before{border-right:solid 2px #cccccc;border-radius:0px 5px 0px 0px}body .tree ul ul::before{content:"";position:absolute;top:0px;left:50%;border-left:solid 2px #cccccc;width:0px;height:1.19999em}body .tree li div.forest-link{border:solid 2px #cccccc;padding:0.2em 0.29999em 0.2em 0.29999em;text-decoration:none;display:inline-block;border-radius:5px 5px 5px 5px;color:#333333;position:relative;top:2px}body .tree.flipped li div.forest-link{-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}</style><script
|
||
async=""
|
||
id="MathJax-script"
|
||
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"
|
||
></script>
|
||
<link
|
||
href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/themes/prism.min.css"
|
||
rel="stylesheet"
|
||
/><link rel="preconnect" href="https://fonts.googleapis.com" /><link
|
||
rel="preconnect"
|
||
href="https://fonts.gstatic.com"
|
||
crossorigin
|
||
/><link
|
||
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=IBM+Plex+Sans+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=IBM+Plex+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=IBM+Plex+Serif:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap"
|
||
rel="stylesheet"
|
||
/>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/components/prism-core.min.js"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/plugins/autoloader/prism-autoloader.min.js"></script>
|
||
<style>
|
||
body .ui.container,
|
||
body ul {
|
||
font-family: "IBM Plex Sans" !important;
|
||
}
|
||
body blockquote {
|
||
border-left-width: 3px !important;
|
||
font-style: italic;
|
||
}
|
||
.headerFont,
|
||
.ui.header,
|
||
body h1,
|
||
h2,
|
||
h3,
|
||
h4,
|
||
h5,
|
||
h6 {
|
||
font-family: "IBM Plex Sans Condensed" !important;
|
||
}
|
||
body p {
|
||
line-height: 1.4;
|
||
}
|
||
.monoFont,
|
||
body code,
|
||
pre,
|
||
tt {
|
||
font-family: "IBM Plex Mono" !important;
|
||
font-size: 12px !important;
|
||
line-height: 1.4 !important;
|
||
}
|
||
</style>
|
||
<!--replace-end-7--><!--replace-end-4--><!--replace-end-1--></head><body><div class="ui fluid container universe"><!--replace-start-2--><!--replace-start-3--><!--replace-start-6--><div class="ui text container" id="zettel-container" style="position: relative"><div class="zettel-view"><article class="ui raised attached segment zettel-content"><div class="pandoc"><h1 id="title-h1">AWS SAM</h1><p>SAM stands for <strong>serverless application model</strong>. 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 <a href="zk/Lambda_programming_model.md">Lambda functions</a>, <a href="/zk/AWS_API_Gateway.md">API gateway</a> and database tables.</p><p>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).</p><h2 id="key-features-of-sam">Key features of SAM</h2><ul><li>Single deployment configuration</li><li>Integration with development tools</li><li>Local testing and debugging</li><li>Built on AWS CloudFormation</li></ul><h2 id="main-technologies-required">Main technologies required</h2><h3 id="docker">Docker</h3><p>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.</p><h3 id="aws-cli">AWS CLI</h3><p>This is installed using Python and allows you to interact directly with AWS via the command-line.</p><h3 id="aws-sam-cli">AWS SAM CLI</h3><p>See <a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html"><a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html">https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html</a></a></p><h2 id="setting-up-credentials-for-the-aws-cli">Setting up credentials for the AWS CLI</h2><p>You require an access key for the given <a href="zk/AWS_User_management_and_roles.md#iam">IAM user</a>. You should create an IAM account specific to the project with bounded permissions.</p><pre><code class="language-none">aws configure
|
||
AWS Access Key ID [None]: AK*******
|
||
AWS Secret Access Key [None]: ukp******
|
||
Default region name [None]:
|
||
Default output format [None]:</code></pre><p>This information can be found in the Security Credentials section of the given <a href="zk/AWS_User_management_and_roles.md#iam">IAM</a> user:</p><p><img src="/static/access-key-aws.png" /></p><h3 id="switching-between-credentials">Switching between credentials</h3><p>You should set up a different IAM user for each project.</p><p>You can do this with:</p><pre><code class="sh language-sh">aws configure --profile <profile-name></code></pre><p>This will then ask you to add the credentials for the user.</p><p>You can switch between different credentials for the user as follows:</p><pre><code class="sh language-sh">AWS_PROFILE=<profile-name> sam build
|
||
</code></pre><h2 id="starting-a-sam-project">Starting a SAM project</h2><p>First create a directory for your project which will serve as the repository:</p><pre><code class="sh language-sh">mkdir aws-sam-learning
|
||
cd aws-sam-learning</code></pre><p>Then we can use the <code>sam</code> cli to bootstrap the project:</p><pre><code class="sh language-sh">sam init --runtime nodejs16.x</code></pre><p>We can just click through and accept the basic HelloWorld Lambda.</p><p>This will create the Lambda as well as an API Gateway trigger URL.</p><h3 id="templateyaml"><code>template.yaml</code></h3><p>This is autogenerated and details the main constituents of the project. There are lots of fields but the most important are the following:</p><pre><code class="yaml language-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</code></pre><p>This details the location of the <a href="/Lambda_handler_function.md">handler function</a> which is contained at the path <code>hello-world/app.js</code>:</p><pre><code class="js language-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;
|
||
};</code></pre><p>It also lists the <code>get</code> event that we can use to call API Gateway and trigger the Lambda.</p><p>The full template is below:</p><p><img src="/static/sam-template-yaml.png" /></p><h2 id="adding-our-own-code">Adding our own code</h2><p>We will create our own function and API Gateway trigger.</p><p>We will place our function after the existing <code>HelloWorldFunction</code></p><pre><code class="yaml language-yaml">ClockFunction:
|
||
Type: AWS::Serverless::Function
|
||
Properties:
|
||
CodeUri: clock/
|
||
Handler: handler.clock
|
||
Runtime: nodejs16.x
|
||
Events:
|
||
ClockApi:
|
||
Type: Api
|
||
Properties:
|
||
Path: /clock
|
||
Method: get</code></pre><p>We can test the syntax with:</p><pre><code class="sh language-sh">sam validate</code></pre><p>Just like with <code>HelloWorld</code>, we will create a directory for this function: <code>clock</code> and we will initialise it as an <code>npm</code> project.</p><pre><code class="sh language-sh">mkdir clock
|
||
cd clock
|
||
npm init</code></pre><p>We will use <code>handler.js</code> as our root, handler function.</p><p>We have said in the template file that our <code>Handler: handler.clock</code>, therefore the main function in the <code>handler</code> module should be <code>clock</code>:</p><pre><code class="js language-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;
|
||
};</code></pre><p>The directory structure is as follows:</p><p><img src="/static/sam-directory.png" /></p><p>When we call the API Gateway path <code>/clock</code> with <code>GET</code>, our function will be triggered.</p><h2 id="deploying-the-project">Deploying the project</h2><p>We will now deploy our project to AWS from the local environment.</p><p>The process is as follows:</p><ol><li>Build</li><li>Package</li><li>Deploy</li></ol><h3 id="build">Build</h3><p>We need to install the runtime dependencies for the function. We do this by running <code>sam build</code>. This ignores test files and development dependencies and installs the project dependencies and source files to a temporary subdirectory.</p><p><img src="/static/sam-build.png" /></p><p>The build directory is <code>.aws-sam/build/</code>. There will be a subdirectory for each of our files.</p><h3 id="package">Package</h3><p>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.</p><p>The packaging proces will first archive all of the project artefacts into a zip file and then upload that to <a href="zk/AWS_S3.md">S3</a>. A reference to this S3 entity is then provided to CloudFormation.</p><p><img src="/static/s3-package-again.svg" /></p><p>The command is as follows:</p><pre><code class="sh language-sh">sam package
|
||
--template-file template.yaml
|
||
--output-template-file pkg.yml
|
||
--region eu-west-1</code></pre><p>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.)</p><h3 id="local-development-with-docker">Local development with Docker</h3><p>In order to work with your application locally without actually sending requests to AWS and using credit, you can run a local instance.</p><p>See <a href="zk/Local_AWS_development_with_SAM.md">Local AWS Development with SAM</a>.</p><h3 id="deploy">Deploy</h3><p>Once you have packaged the app you can deploy with <code>sam deploy --guided</code>. This will talk you through the defaults and will deploy the package to CloudFormation. In CloudFormation each individual project is called a <strong>stack</strong>.</p><p>If we then go to Cloud Formation we will see the deployed application.</p><p><img src="/static/cloud-formation-stack.png" /></p><h2 id="call-the-endpoint">Call the endpoint</h2><p>If we now go to the Lambda console, we will see our function listed, and the API Gateway endpoint under <code>triggers</code>:</p><p><img src="/static/gateway-trigger.png" /></p><p>We can then call this from Postman to check everything is working as it should:</p><p><img src="/static/postman-aws-output.png" /></p></div></article><nav class="ui attached segment deemphasized bottomPane" id="neuron-tags-pane"><div><span class="ui basic label zettel-tag" title="Tag">AWS</span></div></nav><nav class="ui bottom attached icon compact inverted menu blue" id="neuron-nav-bar"><!--replace-start-9--><!--replace-end-9--><a class="right item" href="impulse.html" title="Open Impulse"><i class="wave square icon"></i></a></nav></div></div><!--replace-end-6--><!--replace-end-3--><!--replace-end-2--><div class="ui center aligned container footer-version"><div class="ui tiny image"><a href="https://neuron.zettel.page"><img alt="logo" src="https://raw.githubusercontent.com/srid/neuron/master/assets/neuron.svg" title="Generated by Neuron 1.9.35.3" /></a></div></div></div></body></html> |