使用Lambda(Node.js)在ElastiCache for Memcached上运行
AWS云开发工具包 (AWS CDK)
创建VPC,并将ElastiCache和Lambda部署在同一子网中。将ElastiCache的配置终端节点设置为Lambda的环境变量。请从以下链接中选择适当的节点来设置cacheNodeType。
- サポートされているノードの種類 – Amazon ElastiCache
为了从VPC Lambda访问ElastiCache,需要创建安全组的入站规则。
import { Construct } from 'constructs';
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as elasticache from 'aws-cdk-lib/aws-elasticache';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as lambdaNodeJs from 'aws-cdk-lib/aws-lambda-nodejs';
export class SampleStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// VPC
const vpc = this.createVpc();
const securityGroup = this.createVpcSecurityGroup(vpc);
// ElastiCache
const memcache = this.createElasticache(vpc, securityGroup);
const memcachedConfigEndpoint = `${memcache.attrConfigurationEndpointAddress}:${memcache.attrConfigurationEndpointPort}`;
// VPC Lambda
this.createFunc(vpc, memcachedConfigEndpoint);
}
/**
* @description VPCを作成する
* {@link Vpc | https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.Vpc.html}
*/
private createVpc(): ec2.IVpc {
return new ec2.Vpc(this, 'Vpc', {
ipAddresses: ec2.IpAddresses.cidr('192.168.0.0/16'),
subnetConfiguration: [
{
name: `vpc-public`,
cidrMask: 24,
subnetType: ec2.SubnetType.PUBLIC,
},
{
cidrMask: 24,
name: `vpc-private`,
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
},
],
});
}
/**
* {@link SecurityGroup | https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2.SecurityGroup.html}
*/
private createVpcSecurityGroup(vpc: ec2.IVpc): ec2.SecurityGroup {
return new ec2.SecurityGroup(this, 'VpcSecurityGroup', { vpc });
}
/**
* @description ElastiCache for Memcachedを作成する
* {@link Amazon ElastiCache for Memcached | https://docs.aws.amazon.com/ja_jp/AmazonElastiCache/latest/mem-ug/WhatIs.html}
* {@link CfnSubnetGroup | https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_elasticache.CfnSubnetGroup.html}
* {@link CfnCacheCluster | https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_elasticache.CfnCacheCluster.html}
*/
private createElasticache(vpc: ec2.IVpc, securityGroup: ec2.SecurityGroup): elasticache.CfnCacheCluster {
const subnetGroup = new elasticache.CfnSubnetGroup(this, 'SubnetGroup', {
description: 'private subnet',
subnetIds: vpc.selectSubnets({ subnetType: ec2.SubnetType.PRIVATE_ISOLATED }).subnetIds,
});
const cluster = new elasticache.CfnCacheCluster(this, 'ElastiCache', {
engine: 'memcached',
cacheNodeType: 'cache.r6g.large',
numCacheNodes: 1,
cacheSubnetGroupName: subnetGroup.ref,
vpcSecurityGroupIds: [securityGroup.securityGroupId], // vpcSecurityGroupIds,cacheSecurityGroupNamesのいずれかが必須
});
// インバウンドルールの追加
const port = ec2.Port.tcp(cluster.port ?? 11211);
securityGroup.addIngressRule(ec2.Peer.anyIpv4(), port);
return cluster;
}
/**
* @description Lambdaの作成
* {@link NodejsFunction | https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda_nodejs.NodejsFunction.html}
*/
private createFunc(vpc: ec2.IVpc, memcachedConfigEndpoint: string): lambdaNodeJs.NodejsFunction {
return new lambdaNodeJs.NodejsFunction(this, 'SampleFunc', {
entry: 'src/lambda/sample/index.ts',
handler: 'handler',
runtime: lambda.Runtime.NODEJS_18_X,
timeout: cdk.Duration.minutes(5),
environment: {
MEMCACHED_CONFIG_ENDPOINT: memcachedConfigEndpoint,
},
vpc,
vpcSubnets: vpc.selectSubnets({ subnetType: ec2.SubnetType.PRIVATE_ISOLATED }),
});
}
}
Lambda(拉姆达)
在这里,我们将使用一个名为”Memcache Plus”的库作为memcached客户端。
这个库具有一个名为”autodiscover”的功能,可以从ElastiCache的配置端点获取端点信息。
% npm install memcache-plus aws-lambda
% npm install --save-dev @types/aws-lambda esbuild@0
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
import MemcachePlus = require('memcache-plus');
export async function handler(event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> {
const configEndpoint: string = process.env.MEMCACHED_CONFIG_ENDPOINT ?? '';
const client = new MemcachePlus({
hosts: [configEndpoint],
autodiscover: true,
});
const ttl = 86400 * 30; // キャッシュ期間。単位秒。
const key = 'key';
const input = Math.floor(Math.random() * 1000);
// データをキャッシュする。keyは250バイトまで。
await client.set(key, input, ttl);
// キャッシュしたデータを取得する。存在しない場合はnullを返却する。
const output = await client.get(key);
if (input === output) {
console.debug(`memcachedから取得した値: ${output}`);
return {
statusCode: 200,
body: output,
};
} else {
console.error(`期待値: ${input}, 実際: ${output}`);
return {
statusCode: 500,
body: `期待値: ${input}, 実際: ${output}`
};
}
}
declare module 'memcache-plus' {
interface MemcacheOptions {
autodiscover?: boolean;
backoffLimit?: number;
bufferBeforeError?: number;
disabled?: boolean;
hosts?: string | string[];
maxValueSize?: number;
queue?: boolean;
netTimeout?: number;
reconnect?: boolean;
}
class MemcacheClient {
constructor(param: string | string[] | MemcacheOptions);
get(key: string): Promise<any>;
set(key: string, value: any, lifetime?: number): Promise<any>;
// 必要に応じて他のメソッドの型定義を追加
}
export = MemcacheClient;
}
考试
在测试文件 `test/sample.test.ts` 中,
“`typescript
import * as cdk from ‘aws-cdk-lib’;
import { Match, Template } from ‘aws-cdk-lib/assertions’;
import { SampleStack } from ‘../lib/sample-stack’;
test(‘SecurityGroup’, () => {
const app = new cdk.App();
const stack = new SampleStack(app, ‘SampleStack’);
const template = Template.fromStack(stack);
// Security Group
template.hasResourceProperties(‘AWS::EC2::SecurityGroup’, {
VpcId: Match.anyValue(),
SecurityGroupIngress: [{
“CidrIp”: “0.0.0.0/0”,
“Description”: “from 0.0.0.0/0:11211”,
“FromPort”: 11211,
“IpProtocol”: “tcp”,
“ToPort”: 11211
}]
});
});
test(‘ElastiCache’, () => {
const app = new cdk.App();
const stack = new SampleStack(app, ‘SampleStack’);
const template = Template.fromStack(stack);
// Subnet Group
template.resourceCountIs(‘AWS::ElastiCache::SubnetGroup’, 1);
template.hasResourceProperties(‘AWS::ElastiCache::SubnetGroup’, {
SubnetIds: Match.anyValue(),
Description: ‘private subnet’,
});
// Cache Cluster
template.resourceCountIs(‘AWS::ElastiCache::CacheCluster’, 1);
template.hasResourceProperties(‘AWS::ElastiCache::CacheCluster’, {
Engine: ‘memcached’,
CacheNodeType: ‘cache.r6g.large’,
NumCacheNodes: 1,
CacheSubnetGroupName: Match.anyValue(),
VpcSecurityGroupIds: Match.anyValue(),
});
});
test(‘Lambda’, () => {
const app = new cdk.App();
const stack = new SampleStack(app, ‘SampleStack’);
const template = Template.fromStack(stack);
// Lambda
template.resourceCountIs(‘AWS::Lambda::Function’, 1);
template.hasResourceProperties(‘AWS::Lambda::Function’, {
Runtime: ‘nodejs18.x’,
Timeout: 300,
VpcConfig: Match.anyValue(),
});
});
“`
将测试文件 `test/sample.test.ts` 中的内容进行了汉语表达。
错误 (wù)
错误:spawnSync docker ENOENT
通过安装esbuild来解决该问题。
$ npm install --save-dev esbuild@0
- @aws-cdk/aws-lambda-nodejs module · AWS CDK
错误 TS2688:找不到类型定义文件“babel__generator”。
有时通过删除 node_modules 并重新执行 npm install 可以解决该问题。
自动发现失败。错误:连接超时 192.168.3.236:11211。
如果安全组没有添加入站规则,可能会发生此错误。TCP的入站和出站流量都需要允许。出站规则默认情况下被全部允许,所以需要设置入站规则。如果要手动设置,可以按照以下方式进行设置。
只需要一种选择:在中文中改述以下内容。
-
- AWS CDK
AWS CDK Intro Workshop
API Reference · AWS CDK
ElastiCache
Amazon ElastiCache for Memcached とは – Amazon ElastiCache
Memcached Client
Memcache Plus
victorquinn/memcache-plus: Memcache Plus – Better memcache for node