LoginSignup
3
4

More than 3 years have passed since last update.

AWS CDKで配布可能なサーバレスアプリを作る

Posted at

これまでAWSのSAM (Serverless Application Model) などを使ってサーバレスアプリを作成することが多かったのですが、サーバレスの構成情報とそこに必要となるパラーメタなどが密に結合してしまいがちで、どこでも使えるようなアプリが作りづらいという問題がありました。定義ファイルに後から値を埋め込めるParameterなどの機能はあったものの、YAMLやJSON上で無理くりやっている感があってわりと消耗が多かったです。

これに対してAWS CDK (Cloud Development Kit) では定義ファイルに相当する Constrcut をコードで記述することができ、かなり柔軟性高く利用できます。配布可能な形でいくつかのサーバレスアプリを作成してみたので、その際の知見を記事にまとめてみます。

CDKそのものの解説はこちら https://docs.aws.amazon.com/cdk/latest/guide/home.html

CDKのプロジェクトを作成する

今回は onigiri という名前のモジュールを作成するとします。まず普通のCDKプロジェクトを作成します。

% mkdir onigiri
% cd onigiri
% cdk init --language typescript

CDK Constructを作る

Construct は最終的にCloudFormationでデプロイされるStackのテンプレートです。 onigiri という名前でinitすると、 lib/onigiri-stack.ts というファイルが生成されるのでそこに記述します。以下の例は、一定時間ごとに起動されるLambdaが何かをクエリして、その結果をDynamoDBに書き込む、という想定のConstructです。Lambda Functionを1つ、DynamoDBを1つデプロイします。

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)],
    });
  }
}

Constructを書く上でのコツは以下の通りです。

  • 今回はLambda functionもTypeScriptで作成しするため、 ./node_modules 以下のモジュールも同時にデプロイするために @aws-cdk/aws-lambda-nodejs パッケージにある NodejsFunction を利用します。他にも別レイヤーに ./node_modules を格納する方法などがあるようですが、今回は割愛。
    • 本来 NodejsFunctionpath パラメータはこのファイルからの相対パスが使えますが、モジュールとして配布した際に相対パスの基準が変わってしまうため、 __dirname をjoinして絶対パス指定にしています。
    • pathを指定する際には .js ファイルにします。これはモジュール配布後に NodejsFunction 経由でトランスコンパイルしようとするとうまくいかない場合があるからです
  • @aws-cdk/aws-lambda などCDKのモジュールを追加する場合は、必ずinit時に追加された aws-cdk/core とバージョンを揃えてインストールします。わずかでもずれると tsc でエラーになります。
  • 単体のCDKプロジェクトとして使う場合、@aws-cdk/aws-lambda などCDKのモジュールを追加は devDependencies になるよう npm i --save-dev @aws-cdk/aws-lambda などとしますが、今回はモジュール配布先で利用する必要があるため、 npm i @aws-cdk/aws-lambda でインストールします。

また、配布用のconstructではパラメータを可変にしておくのが良いので、もともと OnigiriStack の引数である cdk.StackProps を拡張して properties というinterfaceを作っています。これによってなんらかの環境変数を渡したり、外部リソースを参照したりしたい場合に、情報を引数として渡すことが出来ます。

同時にLambda Functionを作成します。今回は ./onigiri/lib/lambda/query.ts にLambda Functionのファイルがある想定です。

package.json を編集する

package.json に以下の変更を加えます。

  • files: ["lib"] を追加: lib 以下にトランスコンパイル済みのコードを配置するため
  • "main": "./lib/onigiri-stack.js" を追加:配布先のプロジェクトからconstructのコードが読み込めるように
  • "type": "./lib/onigiri-stack.d.ts"を追加:同上
  • scripts 内に "prepare": "tsc" を追加:パッケージ公開時やgithubからのインストール時にトランスコンパイルを実行させるため

トランスコンパイルの出力先を dist にするというやりかたもありますが、そのあたりはお好みで調整して下さい。出来上がりのサンプルは https://github.com/m-mizutani/dnstrack/blob/master/package.json などをご参照下さい。

デプロイ

この記事の目的は作成したCDK constructをモジュールとして配布することですが、作成途中でテストとしてデプロイしたい場合があるかと思います。そのための1つの方法として、 bin/onigiri.ts は環境変数でデプロイできるようになっていると、小回りがきいて便利です。例として、 bin/onigiri.ts を以下のようにします。

#!/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!,
});

こうしておくことで、環境変数で指定して開発中のレポジトリからそのままデプロイ出来つつ、開発環境のパラメータを埋め込むのを防ぐことができます。

% env ONIGIRI_STACK_NAME=onigiri-1 ONIGIRI_PARAM=nanika cdk deploy

パッケージ公開&配布

ここまでの設定ができていればあとは npm publish でパッケージ公開、あるいはプッシュされたgithubからインストールすることで、作成したCDK constructを外部プロジェクトで再利用することができるようになります。npm i dareka/onigiri などでインストールし、配布先の ./bin/nanika.ts などで以下のように記述して利用できます。

#!/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",
});

サンプル

上記の手法にそって作成したconstructが以下になりますので、ご参考までに。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4