概要
AWS CDKでのビルドに苦労したので備忘録。
やること
- AWS CDKを使ったトランスパイル
- SAMを使ったローカル実行
- 設定によってどんなファイルが出るかの確認
やらないこと
- デプロイ
環境
- Windows Home
- docker desktop 3.2.1
- wsl2 ubuntu20
- SAM CLI, version 1.20.0
- aws-cli/2.1.28 Python/3.8.8 Windows/10 exe/AMD64 prompt/off
前提知識
- CDK Workshopにざっくり目を通していること
- cdk コマンドの機能を 実際に叩いて理解するの表をざっくり見ていること
- AWS CDK Toolkit (cdk command)をみてaws cdkをインストールできること
-
Windows での AWS SAM CLI のインストールをできること
- dockerはローカル実行をするなら必須
トランスパイル
-
"esbuild": "^0.9.0"
をnpm installすれば、Dockerがなくてもトランスパイル可能
package.json
"bundle": "cdk synth && cdk synth > cdk.out/template.yaml",
-
cdk synth
でデプロイする予定の成果物をローカルに出力できる。- template.yamlはローカル実行用
ローカル実行
- cdkで出力したtemplage.yamlを使って、
sam
コマンドでローカル実行-
helloHOGEHOGE
はteamplateファイル内から取得すること.Type: AWS::Lambda::Function
の上。(*) - template.yamlは改行コードlfの必要がある。gitbashならlfだが、powershellなどだと
> template.yaml
でcrlfになるので注意 - gitbashだと
sam
にパスが通っていないので、sam.cmd
を利用している
-
package.json
"localinvoke": "cd cdk.out && sam.cmd local invoke hello26396490--no-event",
ビルド成果物検討
- トランスパイルされたjsファイルを見ていく。
Stackの定義は長いので折り畳み
lib/sample-stack.ts
import * as cdk from '@aws-cdk/core';
import { NodejsFunction } from '@aws-cdk/aws-lambda-nodejs';
import * as lambda from '@aws-cdk/aws-lambda';
export class SampleStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
new NodejsFunction(this, 'test', {
runtime: lambda.Runtime.NODEJS_14_X,
entry: 'src/lambda/handlers/test.ts',
functionName: 'kotatest'
});
new NodejsFunction(this, 'test2', {
runtime: lambda.Runtime.NODEJS_14_X,
entry: 'src/lambda/handlers/test.ts',
functionName: 'kotatest',
bundling: {
externalModules: [
'aws-sdk', // Use the 'aws-sdk' available in the Lambda runtime
'date-fns', // Layrerに入れておきたいモジュール
],
define: { // Replace strings during build time
'process.env.API_KEY': JSON.stringify(JSON.stringify('"xxx-xxx"')), // バグってそう
},
},
});
new NodejsFunction(this, 'hello', {
runtime: lambda.Runtime.NODEJS_14_X,
entry: 'src/lambda/handlers/hello.ts',
functionName: 'kotahello',
handler: 'lambdaHandler'
});
}
}
シンプルなやつ
- とても素直
var __defProp = Object.defineProperty;
var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, {get: all[name], enumerable: true});
};
// hello.ts
__markAsModule(exports);
__export(exports, {
lambdaHandler: () => lambdaHandler
});
var lambdaHandler = async (event, context) => {
console.log(event);
console.log(context);
let response = null;
try {
response = {
statusCode: 200,
body: JSON.stringify({
message: "hello world"
})
};
} catch (err) {
console.log(err);
return err;
}
return response;
};
モジュールを利用しているもの
- date-fnsを使ったjsは以下の感じ。date-fnsが埋め込まれて長い。
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
var __commonJS = (callback, module2) => () => {
// 省略
var __toModule = (module2) => {
return __exportStar(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? {get: () => module2.default, enumerable: true} : {value: module2, enumerable: true})), module2);
};
// ../../../node_modules/date-fns/_lib/requiredArgs/index.js
// 省略
// ../utilities/dateUtility.ts
var import_format = __toModule(require_format());
var formatISO = (d) => (0, import_format.default)(d, "yyyy-MM-dd'T'HH:mm:ss");
// test.ts
var handler = async (event) => {
const test = `Hello from Lambda! TS!${formatISO(new Date())}, key: ${JSON.stringify(process.env.API_KEY)}`;
const response = {
statusCode: 200,
body: JSON.stringify(test)
};
return response;
};
externalにモジュールを指定した場合
lib/sample-stack.ts
new NodejsFunction(this, 'test2', {
runtime: lambda.Runtime.NODEJS_14_X,
entry: 'src/lambda/handlers/test.ts',
functionName: 'kotatest',
+ bundling: {
+ externalModules: [
+ 'aws-sdk', // Use the 'aws-sdk' available in the Lambda runtime
+ 'date-fns', // Layrerに入れておきたいモジュール
+ ],
define: { // Replace strings during build time
'process.env.API_KEY': JSON.stringify(JSON.stringify('{"value":"xxx-xxx"}')), // バグってそう
},
},
});
- requireに置き換わってすっきり
// 省略
// ../utilities/dateUtility.ts
var import_date_fns = __toModule(require("date-fns"));
var formatISO = (d) => (0, import_date_fns.format)(d, "yyyy-MM-dd'T'HH:mm:ss");
// test.ts
var handler = async (event) => {
const test = `Hello from Lambda! TS!${formatISO(new Date())}, key: ${JSON.stringify(define_process_env_API_KEY_default)}`;
const response = {
statusCode: 200,
body: JSON.stringify(test)
};
return response;
};
exports.handler = handler;
ビルド時の文字置換を設定したとき
- 2回JSON.stringifyをする必要があり、バグっていそう
lib/sample-stack.ts
new NodejsFunction(this, 'test2', {
runtime: lambda.Runtime.NODEJS_14_X,
entry: 'src/lambda/handlers/test.ts',
functionName: 'kotatest',
bundling: {
externalModules: [
'aws-sdk', // Use the 'aws-sdk' available in the Lambda runtime
'date-fns', // Layrerに入れておきたいモジュール
],
+ define: { // Replace strings during build time
+ 'process.env.API_KEY': JSON.stringify(JSON.stringify('"xxx-xxx"')), // バグってそう
+ },
},
});
- JSON.stringfyを2回しているが、1回だけだと
> error: Invalid define value (must be valid JSON syntax or a single identifier): {value:xxx-xxx}
のエラーがでる
~~エスケープ別解~~ 2021/04/13 1.97.0で使用不可
- 以下でもエスケープできそう *
JSON.stringify(`\\"${'xxx-xxx'}\\"`)
トランスパイル前のソースは以下
const test: string = `Hello from Lambda! TS!${dateUtility.formatISO(new Date())}, key: ${process.env.API_KEY}`
const response = {
トランスパイル後のソースは以下
// test.ts
var handler = async (event) => {
const test = `Hello from Lambda! TS!${formatISO(new Date())}, key: ${"xxx-xxx"}`;
※2021/04/13 1.97.0 でできなくなっている
Done in 5ms
Bundling asset SampleStack2021/test/Code/Stage...
> error: Invalid define value (must be valid JSON syntax or a single identifier): \"xxx-xxx\"
1 error
D:\develop\develop\tutorial\lesson\aws\typescript\projects\cdk_sample\cdk\node_modules\@aws-cdk\core\lib\asset-staging.ts:472
for (const key of Object.keys(object).sort()) {
^
参考
CDK Workshop
aws cdk
aws cdk / esbuild config
aws cdk / lambda nodejs ts
cdk コマンドの機能を 実際に叩いて理解する 【 AWS CDK Command Line Interface 】
AWS CDK で AWS SAM のテンプレートを出力してデプロイする
AWS CDK Toolkit (cdk command)
SAM Hello World
CDKで作ったLambda@Edgeのコードに環境変数を渡せるようにする
Cloud Formationでしっておきたかったこと