VPC LambdaでMomento Serverless Cacheを使う

2022.09.27

Introduction

Momento Serverless Cacheはクラウドネイティブな
高速のサーバーレスキャッシュサービスです。
設定が簡単で管理する必要もなく、すぐに使えるのが特徴です。

Serverless cacheと名前が付いているくらいなので、
AWS Lambdaなどのサーバレス環境との親和性も高いです。

今回はAWS LambdaをVPC内にデプロイし、
そこからMomentoに対してアクセスしてみます。

Environment

  • OS : MacOS 12.4
  • Serverlss : 3.22.0
  • Node : v18.9
  • AWS CDK : 2.41.0

AWSアカウントは設定済みとします。

Create VPC with AWS CDK

まずはAWS CDKでVPC環境を作成します。
AWS CDK CLIがインストールされていない場合、このへんを参考にインストールしてください。
また、CDKでなくAWSコンソールで作成する場合はここを参考にすると良さそうです。

cdk initコマンドでテンプレートを作成して必要モジュールをインストールします。

% cdk init app --language=typescript
% npm install
・
・

次に、bin/cdk-examples.tsを少し修正。
スタック名とリージョンを適当に決め打ちします。
process.env.XXXみたいに環境変数から取得してもOKです。

#!/usr/bin/env node
import 'source-map-support/register';
import * as cdk from 'aws-cdk-lib';
import { CdkExamplesStack } from '../lib/cdk-examples-stack';

const app = new cdk.App();
new CdkExamplesStack(app, 'CdkExamplesStack', {
  stackName: 'my-cdk-stack',
  env: {
    region: "us-east-1"
  },
});

CDK処理の中身であるlib/cdk-examples-stack.tsの修正をします。
ここなどを参考に
NAT GatewayつきのVPCを作成しましょう。

import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class CdkExamplesStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const vpc = new ec2.Vpc(this, 'my-cdk-vpc', {
      cidr: '10.0.0.0/16',
      natGateways: 1,
      maxAzs: 3,
      subnetConfiguration: [
        {
          name: 'private-subnet-1',
          subnetType: ec2.SubnetType.PRIVATE_WITH_NAT,
          cidrMask: 24,
        },
        {
          name: 'public-subnet-1',
          subnetType: ec2.SubnetType.PUBLIC,
          cidrMask: 24,
        },
        {
          name: 'isolated-subnet-1',
          subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
          cidrMask: 28,
        },
      ],
    });

    // 👇 update the Name tag for the VPC
    cdk.Aspects.of(vpc).add(new cdk.Tag('Name', 'my-cdk-vpc'));

    // 👇 update the Name tag for public subnets
    for (const subnet of vpc.publicSubnets) {
      cdk.Aspects.of(subnet).add(
        new cdk.Tag(
          'PublicSubnet',
          `${vpc.node.id}-${subnet.node.id.replace(/Subnet[0-9]$/, '')}-${
            subnet.availabilityZone
          }`,
        ),
      );
    }

    // 👇 update the Name tag for private subnets
    for (const subnet of vpc.privateSubnets) {
      cdk.Aspects.of(subnet).add(
        new cdk.Tag(
          'PrivateSubnet1',
          `${vpc.node.id}-${subnet.node.id.replace(/Subnet[0-9]$/, '')}-${
            subnet.availabilityZone
          }`,
        ),
      );
    }

    // 👇 update the Name tag for private subnets
    for (const subnet of vpc.isolatedSubnets) {
      cdk.Aspects.of(subnet).add(
        new cdk.Tag(
          'PrivateSubnet2',
          `${vpc.node.id}-${subnet.node.id.replace(/Subnet[0-9]$/, '')}-${
            subnet.availabilityZone
          }`,
        ),
      );
    }

    new cdk.CfnOutput(this, 'vpcId', {
      value: vpc.vpcId,
      description: 'the ID of the VPC',
    });
  }
}

Publicサブネット1つとPrivaetサブネットを2つ作成します。

Publicサブネット(public-subnet-1)にデプロイされたリソースは
インターネットゲートウェイ経由でインターネットアクセス可能です。
また、パブリックIPを持っていればリソースにインターネットからアクセスできます。

isolated-subnet-1サブネットは、インターネットアクセス不可、
インターネットからのアクセスもできず、
同じVPCからのみ相互アクセス可能なサブネットです。

private-subnet-1サブネットが、今回使うLambda用のサブネットです。
ここにプロビジョニングされたリソースはNAT Gateway経由で
インターネットにアクセス可能ですが、インターネットからはアクセスできません。

cdk boostrapで初期化します。
このコマンドは新しい環境でデプロイするとき、最初の1回だけ実行する必要があります。
bootstrapコマンドを実行すると、CloudFormationスタックが作成され、
デプロイ時に必要なリソース郡が作成されます。

% cdk bootstrap

そしてdeployコマンド実行。

% cdk deploy

これでNAT GatewayつきのVPC環境ができ、Lambdaをデプロイする環境の準備ができました。
※料金がかかるので使い終わったら削除を忘れずに

Momentoセットアップ

Momentoを使用するには認証トークンが必要です。
ここにある記事を参考に、
認証トークンを取得しておきましょう。
このトークンはLambdaのコード内で使用します。

AWS Lambdaの実装

環境ができたので、次はLambdaを実装します。
今回はserverless frameworkを使ってLambdaを実装します。
まだインストールしていなければ、Homebrewでserverlssをインストールしておきます。

% brew install serverless

% serverless --version
Running "serverless" from node_modules
Framework Core: 3.22.0 (local) 3.22.0 (global)
Plugin: 6.2.2
SDK: 4.3.2

Lmabdaプロジェクトをcreateして必要モジュールをインストール。

% mkdirp /path/your/lambda-projects && cd /path/your/lambda-projects
% serverless create --template aws-nodejs-ecma-script --name serverless-sample --path serverless-sample
% cd serverless-sample/
% npm install

ここでMomento用SDKをインストールします。

% npm install --save @gomomento/sdk

serverless.ymlを↓のように修正します。

service: serverless-sample
frameworkVersion: '3'

plugins:
  - serverless-webpack

provider:
  name: aws
  runtime: nodejs16.x
  region: us-east-1
  vpc:
    securityGroupIds:
      - <default securitygroup id>
    subnetIds:
      - <private-subnet-1Subnet1のサブネットID>
      - <private-subnet-1Subnet2のサブネットID>

functions:
  hello:
    handler: vpclambda.hello

subnetIdsに、NAT Gatewayを指定したサブネットを指定します。
そして、vpclambda.jsファイルを↓のように記述します。

import {SimpleCacheClient} from '@gomomento/sdk';

// 認証トークンを設定
const AUTH_TOKEN = "<Momento認証トークン>";
const DEFAULT_TTL = 60;

// momentoクライアント初期化
const momento = new SimpleCacheClient(AUTH_TOKEN, DEFAULT_TTL);

const CACHE_NAME = 'vpc_myCache';

export const hello = async (event, context, callback) => {
  
  //キャッシュリスト取得
  const caches = (await momento.listCaches()).getCaches();
  const names = caches.map(c => c.getName());
  console.log(`cache names:${names}`);

  if(!names.includes(CACHE_NAME)) {
      // キャッシュ作成
      await momento.createCache(CACHE_NAME);
      console.log(`${CACHE_NAME} cache created`);
  }

  // キャッシュset
  const setResult = await momento.set(CACHE_NAME, 'key_ts', 'value_ts');
  console.log(`setResult:${setResult.text()}`);

  // キャッシュget
  const getResult = await momento.get(CACHE_NAME, 'key_ts');
  console.log(`getResult:${getResult.text()}`);

  // キャッシュ削除
  await momento.deleteCache(CACHE_NAME);
  console.log(`${CACHE_NAME} cache deleted`);

  return {
    statusCode: 200,
    body: JSON.stringify(
      {
        message: 'Hello VPC Lambda with Momento',
        input: event,
      },
      null,
      2
    ),
  };
};

Momento SDKをつかってキャッシュの作成、取得、設定、削除など
ひととおりの処理を実行しています。
また、ここではトークンをコードに記述していますが、
本来ならAWS Secrets Managerとかを使ったほうがいいです。

最後にデプロイしてinvokeしてみます。

% serverless deploy
・
・
・
% serverless invoke -f hello
Running "serverless" from node_modules
{
    "statusCode": 200,
    "body": "{\n  \"message\": \"Hello VPC Lambda with Momento\",\n  \"input\": {}\n}"
}

ちゃんと実行できてます。
CloudwatchでLambdaの実行ログをみれば、console.logの内容も確認できます。

Summary

本稿ではVPC LambdaでMomento Serverless Cacheを動かしてみました。
Momentoの初期設定やプログラムでへの追加は非常に簡単です。

Momentoについて興味がありましたら、ぜひこちらの記事もご確認ください。

References