Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

Deploy production with AWS CDK / 実践プロダクションサーバーレス

Deploy production with AWS CDK / 実践プロダクションサーバーレス

AWS CDK と TypeScript による Web アプリケーション開発パターン

プロダクションでのアプリケーション構築にサーバーレスを採用することも増えてきました。本セッションではサーバーレスアプリケーションのデプロイを考えます。昨今のクラウドアプリに共通して、デプロイのハードルがかなり上がっています。実際の環境、例えばAWSにあげてみないとわからないことが増えてきたためです。

プロダクションデプロイのハードルを下げるアプローチとして、「デプロイ可能な状態を維持する」方針を立ててみました。そして、そのために AWS CDK が寄与することを述べます。さらに、実際にいくつかの実例を交えて、AWS 構成、その際の AWS CDK コードを説明します。

Yusuke Wada

June 26, 2020
Tweet

More Decks by Yusuke Wada

Other Decks in Technology

Transcript

  1. 

  2. લఏɿαʔόʔϨεͷఆٛ  ΦϯϓϨϛε Ϋϥ΢υ FaaS Ϛωʔδυ αʔϏε ΞϓϦέʔγϣϯ ϥϯλΠϜ ϛυϧ΢ΣΞ

    OS Ծ૝Խج൫ ϋʔυ΢ΣΞ Ϣʔβʔ Ϣʔβʔ Ϋϥ΢υϕϯμʔ Ϣʔβʔ Ϋϥ΢υϕϯμʔ Ϋϥ΢υϕϯμʔ αʔϏε੹೚Ϟσϧ
  3. લఏɿαʔόʔϨεͷఆٛ  ΦϯϓϨϛε Ϋϥ΢υ FaaS Ϛωʔδυ αʔϏε ΞϓϦέʔγϣϯ ϥϯλΠϜ ϛυϧ΢ΣΞ

    OS Ծ૝Խج൫ ϋʔυ΢ΣΞ Ϣʔβʔ Ϣʔβʔ Ϋϥ΢υϕϯμʔ Ϣʔβʔ Ϋϥ΢υϕϯμʔ Ϋϥ΢υϕϯμʔ αʔϏε੹೚Ϟσϧ ͜ΕΒΛ૊Έ߹ΘͤΔ͜ͱ
  4. αʔόʔϨε͕ಘҙͳ͜ͱϏδωεޮՌͱ͸  αʔϏεΠϯ· Ͱͷ࣌ؒΛ୹ॖ ୹ظ࣮૷࣮ݱ ػձଛࣦ๷ࢭ ӡ༻ɾอकͷ লྗԽ αʔϏε඼࣭҆ఆԽ ಺੡Խ֦େͷࢧԉ

    ΞδϦςΟ޲্ ݱ৔ཁٻͷਝ଎࣮૷ αʔόʔϨεͷޮՌͱ͸? https://aws.amazon.com/jp/serverless/patterns/serverless-benefit/
  5. "84ΞΧ΢ϯτ΁ͷαʔϏε഑ஔΛܾΊΔ  σϓϩΠ͢ΔͨΊʹඞཁͳࡐྉͷͻͱͭ AWSΞΧ΢ϯτ DEV؀ڥ %&7 αʔϏε# AWSΞΧ΢ϯτ PRD؀ڥ AWSΞΧ΢ϯτ

    αʔϏεA αʔϏε$ αʔϏε" αʔϏε# αʔϏε$ 13% αʔϏε" %&7 AWSΞΧ΢ϯτ αʔϏεB 13% %&7 AWSΞΧ΢ϯτ αʔϏεC 13%
  6. "84ΞΧ΢ϯτ΁ͷαʔϏε഑ஔΛܾΊΔ  AWSΞΧ΢ϯτ DEV؀ڥ %&7 αʔϏε# AWSΞΧ΢ϯτ PRD؀ڥ AWSΞΧ΢ϯτ αʔϏεA

    αʔϏε$ αʔϏε" αʔϏε# αʔϏε$ 13% αʔϏε" %&7 AWSΞΧ΢ϯτ αʔϏεB 13% %&7 AWSΞΧ΢ϯτ αʔϏεC 13% ͪ͜Β͕͓͢͢Ί
  7. "84ΞΧ΢ϯτ΁ͷαʔϏε഑ஔΛܾΊΔ  AWSΞΧ΢ϯτ DEV؀ڥ %&7 αʔϏε# AWSΞΧ΢ϯτ PRD؀ڥ AWSΞΧ΢ϯτ αʔϏεA

    αʔϏε$ αʔϏε" αʔϏε# αʔϏε$ 13% αʔϏε" %&7 AWSΞΧ΢ϯτ αʔϏεB 13% %&7 AWSΞΧ΢ϯτ αʔϏεC 13% ͪ͜Β͕͓͢͢Ί - ΞΧ΢ϯτ͝ͱͷϦιʔε্ݶʹͻ͔͔ͬΓʹ͘͘ͳΔ - ΞΧ΢ϯτͷϥϯχϯάίετ = αʔϏεͷϥϯχϯάίετ - ৽͍͠αʔϏεΛ౤ೖ͢Δͱ͖ɺطଘͷՔಇதαʔϏεΛؾʹʹ͠ͳ͘ ͯΑ͍
  8. αϯϓϧ$MPVE8BUDI&WFOUTͰىಈ͢Δ-BNCEB'VODUJPO  import events = require('@aws-cdk/aws-events'); import targets = require('@aws-cdk/aws-events-targets');

    import lambda = require('@aws-cdk/aws-lambda'); import cdk = require('@aws-cdk/core'); import fs = require('fs'); export class LambdaCronStack extends cdk.Stack { constructor(app: cdk.App, id: string) { super(app, id); const lambdaFn = new lambda.Function(this, 'Singleton', { code: new lambda.InlineCode(fs.readFileSync('lambda-handler.py', { encoding: 'utf-8' })), handler: 'index.main', timeout: cdk.Duration.seconds(300), runtime: lambda.Runtime.PYTHON_3_6, }); // Run every day at 6PM UTC // See https://docs.aws.amazon.com/lambda/latest/dg/tutorial-scheduled-events-schedule- expressions.html const rule = new events.Rule(this, 'Rule', { schedule: events.Schedule.expression('cron(0 18 ? * MON-FRI *)') }); rule.addTarget(new targets.LambdaFunction(lambdaFn)); } } const app = new cdk.App(); new LambdaCronStack(app, 'LambdaCronExample'); app.synth(); aws-cdk-examples/index.ts at master · aws-samples/aws-cdk-examples https://github.com/aws-samples/aws-cdk-examples/blob/master/typescript/lambda-cron/index.ts
  9. αϯϓϧ$MPVE8BUDI&WFOUTͰىಈ͢Δ-BNCEB'VODUJPO  import events = require('@aws-cdk/aws-events'); import targets = require('@aws-cdk/aws-events-targets');

    import lambda = require('@aws-cdk/aws-lambda'); import cdk = require('@aws-cdk/core'); import fs = require('fs'); export class LambdaCronStack extends cdk.Stack { constructor(app: cdk.App, id: string) { super(app, id); const lambdaFn = new lambda.Function(this, 'Singleton', { code: new lambda.InlineCode(fs.readFileSync('lambda-handler.py', { encoding: 'utf-8' })), handler: 'index.main', timeout: cdk.Duration.seconds(300), runtime: lambda.Runtime.PYTHON_3_6, }); // Run every day at 6PM UTC // See https://docs.aws.amazon.com/lambda/latest/dg/tutorial-scheduled-events-schedule- expressions.html const rule = new events.Rule(this, 'Rule', { schedule: events.Schedule.expression('cron(0 18 ? * MON-FRI *)') }); rule.addTarget(new targets.LambdaFunction(lambdaFn)); } } const app = new cdk.App(); new LambdaCronStack(app, 'LambdaCronExample'); app.synth(); aws-cdk-examples/index.ts at master · aws-samples/aws-cdk-examples https://github.com/aws-samples/aws-cdk-examples/blob/master/typescript/lambda-cron/index.ts
  10. αϯϓϧ$MPVE8BUDI&WFOUTͰىಈ͢Δ-BNCEB'VODUJPO  import events = require('@aws-cdk/aws-events'); import targets = require('@aws-cdk/aws-events-targets');

    import lambda = require('@aws-cdk/aws-lambda'); import cdk = require('@aws-cdk/core'); import fs = require('fs'); export class LambdaCronStack extends cdk.Stack { constructor(app: cdk.App, id: string) { super(app, id); const lambdaFn = new lambda.Function(this, 'Singleton', { code: new lambda.InlineCode(fs.readFileSync('lambda-handler.py', { encoding: 'utf-8' })), handler: 'index.main', timeout: cdk.Duration.seconds(300), runtime: lambda.Runtime.PYTHON_3_6, }); // Run every day at 6PM UTC // See https://docs.aws.amazon.com/lambda/latest/dg/tutorial-scheduled-events-schedule- expressions.html const rule = new events.Rule(this, 'Rule', { schedule: events.Schedule.expression('cron(0 18 ? * MON-FRI *)') }); rule.addTarget(new targets.LambdaFunction(lambdaFn)); } } const app = new cdk.App(); new LambdaCronStack(app, 'LambdaCronExample'); app.synth(); aws-cdk-examples/index.ts at master · aws-samples/aws-cdk-examples https://github.com/aws-samples/aws-cdk-examples/blob/master/typescript/lambda-cron/index.ts
  11. αϯϓϧ$MPVE8BUDI&WFOUTͰىಈ͢Δ-BNCEB'VODUJPO  import events = require('@aws-cdk/aws-events'); import targets = require('@aws-cdk/aws-events-targets');

    import lambda = require('@aws-cdk/aws-lambda'); import cdk = require('@aws-cdk/core'); import fs = require('fs'); export class LambdaCronStack extends cdk.Stack { constructor(app: cdk.App, id: string) { super(app, id); const lambdaFn = new lambda.Function(this, 'Singleton', { code: new lambda.InlineCode(fs.readFileSync('lambda-handler.py', { encoding: 'utf-8' })), handler: 'index.main', timeout: cdk.Duration.seconds(300), runtime: lambda.Runtime.PYTHON_3_6, }); // Run every day at 6PM UTC // See https://docs.aws.amazon.com/lambda/latest/dg/tutorial-scheduled-events-schedule- expressions.html const rule = new events.Rule(this, 'Rule', { schedule: events.Schedule.expression('cron(0 18 ? * MON-FRI *)') }); rule.addTarget(new targets.LambdaFunction(lambdaFn)); } } const app = new cdk.App(); new LambdaCronStack(app, 'LambdaCronExample'); app.synth(); aws-cdk-examples/index.ts at master · aws-samples/aws-cdk-examples https://github.com/aws-samples/aws-cdk-examples/blob/master/typescript/lambda-cron/index.ts - $ cdk deploy - TypeScriptίʔυ͔Β AWS ϦιʔεΛσϓϩΠՄೳ - ཪͰ AWS CloudFormation ςϯϓϨʔτΛੜ੒͍ͯ͠Δ
  12. ߏ੒ྫ  QBDLBHFT NBOBHFNFOUSFBDU MBNCEBOPEF JOGSBBXTDEL QBDLBHFKTPO UTDPOpHCBTFKTPO ./ -

    ֤ workspace Ͱಠཱͯ͠։ൃ͕Մೳ - ಉ࣌ʹɺϓϩδΣΫτϧʔτͷ package.json ͷεΫϦϓτ࣍ୈͰɺ·ͱ ΊͯϏϧυ͢Δ͜ͱ΋Մೳ
  13. -BNCEB'VODUJPOͱ"84$%,ͷ࿈ܞ  "84$%,Ͱ-BNCEB'VODUJPOΛఆٛ͢Δͱ͖ ʹɺϏϧυޙͷ-BNCEB'VODUJPOύεΛࢦఆ͢Ε͹ 0, … new lambda.Function(stack, 'getGreetingReply', {

    functionName: 'getGreetingReply-function', code: lambda.Code.fromAsset(NODE_LAMBDA_SRC_DIR), handler: 'lambda/handlers/api-gw/greeting/api-gw-get-greeting-reply-handler.handler', runtime: lambda.Runtime.NODEJS_12_X, layers: [nodeModulesLayer], environment: { REGION: cdk.Stack.of(stack).region, }, }); …
  14. -BNCEB'VODUJPOͱ"84$%,ͷ࿈ܞ  "84$%,Ͱ-BNCEB'VODUJPOΛఆٛ͢Δͱ͖ ʹɺϏϧυޙͷ-BNCEB'VODUJPOύεΛࢦఆ͢Ε͹ 0, … new lambda.Function(stack, 'getGreetingReply', {

    functionName: 'getGreetingReply-function', code: lambda.Code.fromAsset(NODE_LAMBDA_SRC_DIR), handler: 'lambda/handlers/api-gw/greeting/api-gw-get-greeting-reply-handler.handler', runtime: lambda.Runtime.NODEJS_12_X, layers: [nodeModulesLayer], environment: { REGION: cdk.Stack.of(stack).region, }, }); …
  15. -BNCEB'VODUJPOͱ"84$%,ͷ࿈ܞ  "84$%,Ͱ-BNCEB'VODUJPOΛఆٛ͢Δͱ͖ ʹɺϏϧυޙͷ-BNCEB'VODUJPOύεΛࢦఆ͢Ε͹ 0, … new lambda.Function(stack, 'getGreetingReply', {

    functionName: 'getGreetingReply-function', code: lambda.Code.fromAsset(NODE_LAMBDA_SRC_DIR), handler: 'lambda/handlers/api-gw/greeting/api-gw-get-greeting-reply-handler.handler', runtime: lambda.Runtime.NODEJS_12_X, layers: [nodeModulesLayer], environment: { REGION: cdk.Stack.of(stack).region, }, }); … - $ yarn deploy
  16. ؀ڥ͝ͱʹσϓϩΠͰ͖ΔΑ͏ʹ͓ͯ͘͠  ελοΫ໊Λ؀ڥ͝ͱʹม͑Δ͜ͱͰରԠՄೳ ؀ڥม਺ͳͲʹೖΕ͓ͯ͘ͱྑ͍ export async function greetingApplicationStack( scope: cdk.Construct,

    id: string, global: GlobalProps, ): Promise<Stack> { // dev | stg | prd const stage = process.env.STAGE_NAME!; const stack = new cdk.Stack(scope, id, { stackName: `${stage}-${id}-stack`, }); const buckets = await s3Construct.reference(stack, bucketArns); const dynamoTables = await dynamoConstruct.reference(stack, dynamoArns); const nodeModulesLayer = cdkLambda.LayerVersion.fromLayerVersionArn( stack, 'NodeModulesLayer', nodeModulesLayerArn, ); // lambda const lambda = await greetingLambdaConstruct(stack, global, { buckets,dynamoTables,nodeModulesLayer, }); // API Gateway await greetingApiConstruct(stack, global, { lambda, env: { apiStageName: 'v1'}, }); return stack; }
  17. ؀ڥ͝ͱʹσϓϩΠͰ͖ΔΑ͏ʹ͓ͯ͘͠  ελοΫ໊Λ؀ڥ͝ͱʹม͑Δ͜ͱͰରԠՄೳ ؀ڥม਺ͳͲʹೖΕ͓ͯ͘ͱྑ͍ export async function greetingApplicationStack( scope: cdk.Construct,

    id: string, global: GlobalProps, ): Promise<Stack> { // dev | stg | prd const stage = process.env.STAGE_NAME!; const stack = new cdk.Stack(scope, id, { stackName: `${stage}-${id}-stack`, }); const buckets = await s3Construct.reference(stack, bucketArns); const dynamoTables = await dynamoConstruct.reference(stack, dynamoArns); const nodeModulesLayer = cdkLambda.LayerVersion.fromLayerVersionArn( stack, 'NodeModulesLayer', nodeModulesLayerArn, ); // lambda const lambda = await greetingLambdaConstruct(stack, global, { buckets,dynamoTables,nodeModulesLayer, }); // API Gateway await greetingApiConstruct(stack, global, { lambda, env: { apiStageName: 'v1'}, }); return stack; }
  18. ΋ͪΖΜ"84$PEF1JQFMJOFͷߏங΋"84$%,Ͱ  const applicationBuild = new codeBuild.PipelineProject( stack, 'GreetingApplicationDeploy-project', {

    projectName: 'GreetinApplicationDeploy-project', buildSpec: codeBuild.BuildSpec.fromSourceFilename( './buildspec/buildspec-deploy.yml', ), role: deployRole, environment: { buildImage: LinuxBuildImage.STANDARD_3_0, environmentVariables: { AWS_DEFAULT_REGION: { type: codeBuild.BuildEnvironmentVariableType.PLAINTEXT, value: stack.region, }, }, }, }, );
  19. "84"QQ4ZODʹΑΔ#BDLFOEGPS'SPOUFOE  const graphApi = new appsync.GraphQLApi(stack, 'AdminBff', { name:

    global.getGraphApiName('AdminBff'), authorizationConfig: { defaultAuthorization: { authorizationType: AuthorizationType.API_KEY, apiKeyConfig: { description: 'test api key config', expires: luxon.DateTime.local().plus({ days: 7 }).toISO(), name: 'default test api key', }, }, additionalAuthorizationModes: [], }, logConfig: { fieldLogLevel: FieldLogLevel.ALL, excludeVerboseContent: true, }, schemaDefinitionFile: path.join(__dirname, 'schema.graphql'), });
  20. "84"QQ4ZODʹΑΔ#BDLFOEGPS'SPOUFOE  const graphApi = new appsync.GraphQLApi(stack, 'AdminBff', { name:

    global.getGraphApiName('AdminBff'), authorizationConfig: { defaultAuthorization: { authorizationType: AuthorizationType.API_KEY, apiKeyConfig: { description: 'test api key config', expires: luxon.DateTime.local().plus({ days: 7 }).toISO(), name: 'default test api key', }, }, additionalAuthorizationModes: [], }, logConfig: { fieldLogLevel: FieldLogLevel.ALL, excludeVerboseContent: true, }, schemaDefinitionFile: path.join(__dirname, 'schema.graphql'), });
  21. "84"QQ4ZODʹΑΔ#BDLFOEGPS'SPOUFOE  const greetingTableDataSource = graphApi.addDynamoDbDataSource( 'GreetingTableDataSource', 'greeting table', dynamoTables.greetingTable,

    ); greetingTableDataSource.createResolver({ typeName: 'Mutation', fieldName: 'createHello', requestMappingTemplate: MappingTemplate.dynamoDbPutItem( PrimaryKey.partition('id').auto(), Values.projecting('input'), ), responseMappingTemplate: MappingTemplate.dynamoDbResultItem(), });
  22. "84"QQ4ZODʹΑΔ#BDLFOEGPS'SPOUFOE  const greetingTableDataSource = graphApi.addDynamoDbDataSource( 'GreetingTableDataSource', 'greeting table', dynamoTables.greetingTable,

    ); greetingTableDataSource.createResolver({ typeName: 'Mutation', fieldName: 'createHello', requestMappingTemplate: MappingTemplate.dynamoDbPutItem( PrimaryKey.partition('id').auto(), Values.projecting('input'), ), responseMappingTemplate: MappingTemplate.dynamoDbResultItem(), }); (SBQI"QJʹ %BUB4PVSDFΛ௥Ճ
  23. "84"QQ4ZODʹΑΔ#BDLFOEGPS'SPOUFOE  const greetingTableDataSource = graphApi.addDynamoDbDataSource( 'GreetingTableDataSource', 'greeting table', dynamoTables.greetingTable,

    ); greetingTableDataSource.createResolver({ typeName: 'Mutation', fieldName: 'createHello', requestMappingTemplate: MappingTemplate.dynamoDbPutItem( PrimaryKey.partition('id').auto(), Values.projecting('input'), ), responseMappingTemplate: MappingTemplate.dynamoDbResultItem(), }); %BUB4PVSDFʹ 3FTPMWFSΛ௥Ճ
  24. "84$%,"1*(BUFXBZߏங෦෼  const api = new apig.RestApi(scope, 'GreetingApi', { restApiName:

    global.getApiName('Greeting'), endpointTypes: [apig.EndpointType.REGIONAL], deployOptions: { stageName:’v1’, }, }); const helloLink = new VpcLink(scope, 'VpcLink', { targets: [ NetworkLoadBalancer.fromNetworkLoadBalancerAttributes( scope, ‘GreetingNlb', { loadBalancerArn: ‘arn:aws:elasticloadbalancing:ap-northeast-1:xxxxxx:loadbalancer/net/Bonjo-Ec2Se-xfsda/12234567890', }, ), ], }); const helloResource = api.root.addResource('helo'); helloResource.addMethod( 'GET', new apig.Integration({ type: IntegrationType.HTTP_PROXY, integrationHttpMethod: ‘GET', url: ‘http://abxyfskeladk.elb.ap-northeast-1.amazonaws.com’, options: { connectionType: ConnectionType.VPC_LINK, vpcLink: helloLink, }, }), { methodResponses: [{statusCode: ‘200'}] },
  25. "84$%,"1*(BUFXBZߏங෦෼  const api = new apig.RestApi(scope, 'GreetingApi', { restApiName:

    global.getApiName('Greeting'), endpointTypes: [apig.EndpointType.REGIONAL], deployOptions: { stageName:’v1’, }, }); const helloLink = new VpcLink(scope, 'VpcLink', { targets: [ NetworkLoadBalancer.fromNetworkLoadBalancerAttributes( scope, ‘GreetingNlb', { loadBalancerArn: ‘arn:aws:elasticloadbalancing:ap-northeast-1:xxxxxx:loadbalancer/net/Bonjo-Ec2Se-xfsda/12234567890', }, ), ], }); const helloResource = api.root.addResource('helo'); helloResource.addMethod( 'GET', new apig.Integration({ type: IntegrationType.HTTP_PROXY, integrationHttpMethod: ‘GET', url: ‘http://abxyfskeladk.elb.ap-northeast-1.amazonaws.com’, options: { connectionType: ConnectionType.VPC_LINK, vpcLink: helloLink, }, }), { methodResponses: [{statusCode: ‘200'}] }, طଘ/-#ΑΓ 71$-JOL࡞੒
  26. "84$%,"1*(BUFXBZߏங෦෼  const api = new apig.RestApi(scope, 'GreetingApi', { restApiName:

    global.getApiName('Greeting'), endpointTypes: [apig.EndpointType.REGIONAL], deployOptions: { stageName:’v1’, }, }); const helloLink = new VpcLink(scope, 'VpcLink', { targets: [ NetworkLoadBalancer.fromNetworkLoadBalancerAttributes( scope, ‘GreetingNlb', { loadBalancerArn: ‘arn:aws:elasticloadbalancing:ap-northeast-1:xxxxxx:loadbalancer/net/Bonjo-Ec2Se-xfsda/12234567890', }, ), ], }); const helloResource = api.root.addResource('helo'); helloResource.addMethod( 'GET', new apig.Integration({ type: IntegrationType.HTTP_PROXY, integrationHttpMethod: ‘GET', url: ‘http://abxyfskeladk.elb.ap-northeast-1.amazonaws.com’, options: { connectionType: ConnectionType.VPC_LINK, vpcLink: helloLink, }, }), { methodResponses: [{statusCode: ‘200'}] }, "1*(BUFXBZͱ 71$-JOLΛ઀ଓ
  27. "84$%,ͳΒ4UBUF.BDIJOFΛϝιουͰ਺चͭͳ͗ʹ  const convertCsvTask = new sfnTasks.LambdaInvoke( stack, 'CsvConvertTask', {

    lambdaFunction: lambda.convertCsvToJsonLinesFn, invocationType: LambdaInvocationType.REQUEST_RESPONSE, resultPath: '$.convert', }, ); const waitForConvert = new sfn.Wait(stack, 'WaitForConvert', { time: sfn.WaitTime.duration(Duration.seconds(5)), }); const writeDynamodbTask = new sfnTasks.LambdaInvoke( stack, 'WriteDynamodbTask', { lambdaFunction: lambda.writeDynamodbFn, invocationType: LambdaInvocationType.REQUEST_RESPONSE, resultPath: '$.writeData', inputPath: '$.convert.Payload', }, ); const batchWriteChain = sfn.Chain.start(convertCsvTask) .next(waitForConvert) .next(writeDynamodbTask) const batchWriteMachine = new sfn.StateMachine(stack, 'BatchWriteMachine', { stateMachineName: global.getStateMachineName('BatchWriteMachine'), definition: chain, timeout: cdk.Duration.minutes(20), });
  28. const convertCsvTask = new sfnTasks.LambdaInvoke( stack, 'CsvConvertTask', { lambdaFunction: lambda.convertCsvToJsonLinesFn,

    invocationType: LambdaInvocationType.REQUEST_RESPONSE, resultPath: '$.convert', }, ); const waitForConvert = new sfn.Wait(stack, 'WaitForConvert', { time: sfn.WaitTime.duration(Duration.seconds(5)), }); const writeDynamodbTask = new sfnTasks.LambdaInvoke( stack, 'WriteDynamodbTask', { lambdaFunction: lambda.writeDynamodbFn, invocationType: LambdaInvocationType.REQUEST_RESPONSE, resultPath: '$.writeData', inputPath: '$.convert.Payload', }, ); const batchWriteChain = sfn.Chain.start(convertCsvTask) .next(waitForConvert) .next(writeDynamodbTask) const batchWriteMachine = new sfn.StateMachine(stack, 'BatchWriteMachine', { stateMachineName: global.getStateMachineName('BatchWriteMachine'), definition: chain, timeout: cdk.Duration.minutes(20), }); "84$%,ͳΒ4UBUF.BDIJOFΛϝιουͰ਺चͭͳ͗ʹ