Background
We used to create serverless applications using AWS SAM (Serverless Application Model), but the serverless configuration information and the parameters required for it tended to be tightly coupled, making it difficult to create an application that can be used anywhere. Although there were functions such as Parameters that allowed you to embed values in definition files afterwards, there was a sense of being forced to use YAML and JSON, and there was a lot of wear and tear.
On the other hand, AWS CDK (Cloud Development Kit) allows you to write Constrcut, which is the equivalent of a definition file, in code and is quite flexible. I've created several serverless applications as distributable module, and I'll summarize my findings in an article.
Please see https://docs.aws.amazon.com/cdk/latest/guide/home.html for more detail of AWS CDK.
How to build
Create a new CDK project
Let's create a module named onigiri
, which is a normal CDK project. Let's start by creating a normal CDK project.
% mkdir onigiri
% cd onigiri
% cdk init --language typescript
Write CDK Construct
Construct is the template for the Stack that will eventually be deployed in CloudFormation. It can be written in the file lib/onigiri-stack.ts
, which will be generated when you init with the name onigiri
. In the following example, a Lambda Function and a DynamoDB are deployed in a construct where a Lambda Function is queried and its result is written to DynamoDB.
import * as cdk from "@aws-cdk/core";
import * as dynamodb from "@aws-cdk/aws-dynamodb";
import * as lambda from "@aws-cdk/aws-lambda";
import * as events from "@aws-cdk/aws-events";
import * as eventsTargets from "@aws-cdk/aws-events-targets";
import { NodejsFunction } from "@aws-cdk/aws-lambda-nodejs";
import * as path from "path";
export interface properties extends cdk.StackProps {
someParameter: string;
}
export class OnigiriStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props: properties) {
super(scope, id, props);
const cacheTable = new dynamodb.Table(this, "cacheTable", {
partitionKey: { name: "pk", type: dynamodb.AttributeType.STRING },
billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
});
const lambdaQuery = new NodejsFunction(this, "query", {
entry: path.join(__dirname, "lambda/query.js"),
handler: "main",
environment: {
TABLE_NAME: cacheTable.tableName,
SOME_PARAM: props.someParameter,
},
});
cacheTable.grantReadWriteData(lambdaQuery);
new events.Rule(this, "periodicQuery", {
schedule: events.Schedule.rate(cdk.Duration.minutes(10)),
targets: [new eventsTargets.LambdaFunction(lambdaQuery)],
});
}
}
Here are some tips for writing Construct
- As we will be using TypeScript to create the Lambda function, we will use the
NodejsFunction
in the@aws-cdk/aws-lambda-nodejs
package to deploy the modules undernode_modules
at the same time. We use theNodejsFunction
in the@aws-cdk/aws-lambda-nodejs
package to deploy the following modules at the same time. It's also possible to store anode_modules
in a separate layer. /node_modules` in another layer, but I'll spare you the trouble.- Although the
path
parameter ofNodejsFunction
can be relative to this file, the standard of relative path is changed when distributed as a module, so__dirname
is joined to specify absolute path. - The path is specified as a
.js
file. This is because it doesn't work well if you try to transcompile the module viaNodejsFunction
after the module is distributed.
- Although the
- When you add a CDK module such as
@aws-cdk/aws-lambda
, make sure to install it with the same version ofaws-cdk/core
added at init time. If the version is slightly out of alignment, thetsc
error occurs. - If you use it as a single CDK project, you should add CDK modules like
@aws-cdk/aws-lambda
asdevDependencies
asnpm i --save-dev @aws-cdk/aws-lambda
.
Also, as it's good to have variable parameters in the distribution construct, I've extended the cdk.StackProps
argument from the OnigiriStack
to create an interface called properties
. This allows you to pass in some environment variables and external resources as arguments.
Create a Lambda Function at the same time. In this case, I will create a Lambda Function file in . /onigiri/lib/lambda/query.ts
, where the Lambda Function file is located.
Edit package.json
Make the following changes to package.json
.
- Added
files: ["lib"]
: to place transcompiled code underlib
. - Add
"main": ". /Add
lib/onigiri-stack.js"`: Construct code can now be read from the distribution project. - Added
"type": ". /lib/onigiri-stack.d.ts"
added: Ibid. - Added
"prepare": "tsc"
in thescripts
: to make it possible to transcompile the package when it is published or installed from github.
You can also set the output of transcompilation to dist
and you can adjust it as you like. See https://github.com/m-mizutani/dnstrack/blob/master/package.json for a sample.
Deployment
The purpose of this article is to distribute your CDK construct as a module, but you may want to deploy it as a test. One way to do this is to make bin/onigiri.ts
deployable with environment variables. As an example, let's set up bin/onigiri.ts
as follows.
#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "@aws-cdk/core";
import { OnigiriStack } from "../lib/onigiri-stack";
const stackName = process.env.ONIGIRI_STACK_NAME || "onigiri";
const app = new cdk.App();
new OnigiriStack(app, stackName, {
someParameter: process.env.ONIGIRI_PARAM!,
});
By doing so, you can specify an environment variable to deploy directly from the developing repository, but avoid embedding parameters in the development environment.
% env ONIGIRI_STACK_NAME=onigiri-1 ONIGIRI_PARAM=nanika cdk deploy
Publish and distribute the pacakge
You can reuse your CDK constructs in external projects by publishing them with npm publish
and npm i onigiri
. Also you can install the pacakge from github by npm i your-github-account/onigiri
.
After that, create another construct to deploy the package.
#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "@aws-cdk/core";
import { OnigiriStack } from "onigiri";
const app = new cdk.App();
new OnigiriStack(app, "wagaya-no-onigiri", {
someParameter: "nanika-sugoi-guzai",
});
Sample
The following is a construct made according to the above method, for your reference.
- DNS track: https://github.com/m-mizutani/dnstrack
Top comments (0)