Upgrade to Pro — share decks privately, control downloads, hide ads and more …

AWS でサーバーレスな LINE Bot/LIFF アプリ環境 をIaC な感じで構築するよ!

AWS でサーバーレスな LINE Bot/LIFF アプリ環境 をIaC な感じで構築するよ!

クラウドが得意なLINE API Expert集合!サーバレス×LINEでアプリ開発してみたLT 発表資料
https://linedevelopercommunity.connpass.com/event/175636/

sumihiro3

May 28, 2020
Tweet

More Decks by sumihiro3

Other Decks in Programming

Transcript

  1. Ճ઒ ੅ኍʢ͔͕Θ ͢ΈͻΖʣ n 5XJUUFS l !TVNJIJSP n ډॅ஍ l

    ฌݿݝ n ৬ۀ l ίϯαϧˍΤϯδχΞ n ͦͷଞଐੑ l -*/&"1*&YQFSU l -*/& ք۾ͷΠϕϯτ΍ϋοΧιϯ౳ʹΑ͘ग़຅͠·͢ l "84 ΛΑ͘࢖͍·͢ Ø ͕ɺ࠷ۙ͸($1 ͕޷͖͔΋͆ 3
  2. $MPVE'PSNBUJPOͱݴ͑͹ nઃఆϑΝΠϧʁ l :".- l +40/ 18 AWSTemplateFormatVersion: '2010-09-09' Description:

    Lambda function with cfn-response. Resources: primer: Type: AWS::Lambda::Function Properties: Runtime: nodejs12.x Role: arn:aws:iam::123456789012:role/lambda-role Handler: index.handler Code: ZipFile: | var aws = require('aws-sdk') var response = require('cfn-response') exports.handler = function(event, context) { console.log("REQUEST RECEIVED:¥n" + JSON.stringify(event)) // For Delete requests, immediately send a SUCCESS response. if (event.RequestType == "Delete") { response.send(event, context, "SUCCESS") return } var responseStatus = "FAILED" var responseData = {} var functionName = event.ResourceProperties.FunctionName var lambda = new aws.Lambda() lambda.invoke({ FunctionName: functionName }, function(err, invokeResult) { if (err) { responseData = {Error: "Invoke call failed"} console.log(responseData.Error + ":¥n", err) } else responseStatus = "SUCCESS" response.send(event, context, responseStatus, responseData) }) } Description: Invoke a function during stack creation. TracingConfig: Mode: Active ͢Έ·ͤΜɻਖ਼௚ۤखͰ͢ʜ N @@ N
  3. 4FSWFSMFTT'SBNFXPSL ͱ͸ l &BTZ:".-  $-* EFWFMPQNFOU BOEEFQMPZNFOU UP"84 "[VSF

     (PPHMF$MPVE  ,OBUJWF NPSF 21 service: myService provider: name: aws runtime: nodejs12.x memorySize: 512 timeout: 10 versionFunctions: false tracing: lambda: true functions: hello: handler: handler.hello name: ${self:provider.stage}-lambdaName description: Description of what the lambda function does runtime: python2.7 memorySize: 512 timeout: 10 provisionedConcurrency: 3 reservedConcurrency: 5 tracing: PassThrough ͢Έ·ͤΜɻਖ਼௚ۤखͰ͢ʜ N @@ N
  4. "84$%, nରԠͨ͠ϓϩάϥϛϯάݴޠ l 1ZUIPO l 5ZQF4DSJQU l +BWB l /&5

    $ 24 https://aws.amazon.com/jp/cdk/features/ ੩తܕ෇͖ݴޠʂ ͭ·Γɺ*%&ʹॿ͚ͯ΋Β͑Δʂ
  5. "84$%,ͱ͸ n"84$MPVE%FWFMPQNFOU,JU l ʹҰൠެ։ Ø ղઆهࣄ͸·ͩগͳ͍ʢ2JJUB Ͱ໿౤ߘʣ l ࠓ͸ͪΐͬͱᙱ͍ॴʹख͕ಧ͔ͳ͍͔΋ʁ Ø

    *%& ʹΑΔิ׬ͷԸܙ͕ಘΒΕқ͍zIJHIFSMFWFM DPOTUSVDUTz͕ॱ࣍ϦϦʔε͞Ε͍ͯΔ l "1* ϦϑΝϨϯε͋Γ·͢ʢӳޠͷΈʣ Ø ຊࢿྉͰ͸WΛϕʔεʹઆ໌ 25 https://docs.aws.amazon.com/cdk/api/latest/
  6. "84$%, n΋ͪΖΜ$-* ΋͋Γ·͢ʂ l DEL JOJU Ø ৽͍͠ΞϓϦέʔγϣϯΛ೚ҙͷݴޠͰॳظԽ l DEL

    TZOUI Ø "84$MPVE'PSNBUJPOςϯϓϨʔτʹίϯύΠϧ l DEL EFQMPZ Ø ΞϓϦέʔγϣϯΛσϓϩΠ l DEL EFTUSPZ Ø ΞϓϦέʔγϣϯΛ࡟আ 26 https://aws.amazon.com/jp/cdk/features/
  7. ࣮ࡍʹ n -*/&#PU-*''ΞϓϦͷαʔόʔϨε؀ڥΛߏஙʂ 27 Amazon Simple Storage Service Amazon DynamoDB

    Amazon API Gateway Amazon Simple Queue Service AWS Lambda Amazon CloudFront
  8. -*/&#PU؀ڥ n-BNCEB "1*(BUFXBZ %ZOBNP%# 424 28 AWS Cloud AWS Lambda

    DynamoDB Table Bot Users Webhook AWS Lambda Reply kintone SQS Queue API Gateway Endpoint REST API DB Streams
  9. DEL JOJU n·ͣ͸ΞϓϦͷॳظԽ l ॳظԽ͸ద౰ͳσΟϨΫτϦʹͯίϚϯυͰ0, Ø ΞϓϦͷςϯϓϨʔτ͕ల։͞ΕΔ - ࠓճ͸ 5ZQF4DSJQUͰʂ

    29 $ mkdir cdk_sample && cd cdk_sample $ cdk init app --language=typescript cdk_sample/lib/CdkSampleStack.ts にリソース定義のコードを記述していく 使いたい言語を指定
  10. %ZOBNP%# nςʔϒϧͷ࡞੒ 30 // Create User table const userTableName =

    'BotWithLambdaUserTable' const userTable = new Table(this, userTableName, { tableName: userTableName, partitionKey: { name: 'id', type: AttributeType.STRING, }, readCapacity: 1, writeCapacity: 1, stream: StreamViewType.NEW_IMAGE, }) DynamoDB Stream の設定もこれだけ! PK の指定も明確!
  11. -BNCEB n-BNCEB-BZFSͷ࡞੒ 31 // Create Lambda Layer const layerFunctionName =

    'BotWithLambdaLayer' const layer = new LayerVersion(this, layerFunctionName, { layerVersionName: layerFunctionName, compatibleRuntimes: [Runtime.NODEJS_12_X], code: Code.fromAsset('dist'), })
  12. -BNCEB n-BNCEB'VODUJPO<"1*>ͷ࡞੒ 32 const functionName = 'BotWithLambda' const apiLambdaFunction =

    new LambdaFunction(this, functionName, { functionName: functionName, handler: 'index.handler', -- 中略 -- environment: { REGION: process.env.CDK_DEFAULT_REGION || 'ap-northeast-1', ACCESS_TOKEN: process.env.ACCESS_TOKEN || '', CHANNEL_SECRET: process.env.CHANNEL_SECRET || '', USER_TABLE_NAME: userTable.tableName, }, layers: [layer], }) Layer の差し込みもこれだけ! 環境変数も 簡単に設定
  13. -BNCEB n-BNCEB'VODUJPO<4USFBNT>ͷ࡞੒ 33 const dbStreamFunctionName = 'BotWithLambdaDbStream' const dbStreamLambdaFunction =

    new LambdaFunction( { -- 中略 -- handler: 'db_stream.handler', -- 中略 –environment: { REGION: process.env.CDK_DEFAULT_REGION || 'ap-northeast-1', KINTONE_API_URL: process.env.KINTONE_API_URL || '', KINTONE_APP_ID: process.env.KINTONE_APP_ID || '', KINTONE_API_TOKEN: process.env.KINTONE_API_TOKEN || '', }, layers: [layer], } )
  14. -BNCEB 424 n&WFOU4PVSDF2VFVFͷ࡞੒ 34 // Dead letter queue const deadLetterQueue

    = new Queue(this, dlqName, {queueName: dlqName,}) // add Event source dbStreamLambdaFunction.addEventSource( new DynamoEventSource(userTable, { startingPosition: StartingPosition.TRIM_HORIZON, batchSize: 5, bisectBatchOnError: true, onFailure: new SqsDlq(deadLetterQueue), retryAttempts: 10, }) ) Event source の… (ry Queue の 作成も一行!
  15. "1*(BUFXBZ n"1*(BUFXBZ&OEQPJOUͷ࡞੒ 35 // API Gateway const apiGwName = 'BotWithLambdaApiGW'

    const api = new LambdaRestApi(this, apiGwName, { restApiName: apiGwName, handler: apiLambdaFunction, proxy: false, }) api.root.addMethod('POST')
  16. DEL CVJMEDEL EFQMPZ nߏஙίʔυ͕ग़དྷͨΒϏϧυˍσϓϩΠ 36 $ yarn build $ cdk

    synth $ cdk deploy CloudFormation テンプレートを確認できる
  17. -*''ΞϓϦ؀ڥ n4 $MPVE'SPOU 41 AWS Cloud S3 Bucket LIFF Users

    HTTPS Access LINE Platform LIFF API Amazon CloudFront Deploy SPA app
  18. 4 nόέοτͷ࡞੒ 42 // Create Bucket const spaBucketName = `spa-bucket-liff-with-s3-cloudfront`

    const spaBucket = new s3.Bucket(this, spaBucketName, { bucketName: spaBucketName, websiteErrorDocument: 'index.html', websiteIndexDocument: 'index.html', })
  19. $MPVE'SPOU *". nόέοτ΁ͷΞΫηεݖݶઃఆ 43 // Create OriginAccessIdentity const websiteOAI =

    new cloudfront.OriginAccessIdentity( this, `CFIdentity-${this.stackName}` ) // Add S3 Access Resource Policy to OAI const spaBucketPolicyStatement = new iam.PolicyStatement({ actions: ['s3:GetObject'], effect: iam.Effect.ALLOW, principals: [websiteOAI.grantPrincipal], resources: [`${spaBucket.bucketArn}/*`], }) spaBucket.addToResourcePolicy(spaBucketPolicyStatement) Resource Policy
  20. $MPVE'SPOU nσΟετϦϏϡʔγϣϯͷ࡞੒ 44 // Create CloudFront Distribution const websiteDistribution =

    new cloudfront.CloudFrontWebDistribution( this, `CFDistribution-${this.stackName}`, { errorConfigurations: [ -- 中略 -- ], originConfigs: [ { s3OriginSource: { s3BucketSource: spaBucket, originAccessIdentity: websiteOAI,}, behaviors: [{isDefaultBehavior: true,},], }, ], priceClass: cloudfront.PriceClass.PRICE_CLASS_ALL, }, )
  21. 4 ΁σϓϩΠ n41"Ϗϧυ݁ՌΛόέοτ΁σϓϩΠ 45 // Deploy to S3 bucket new

    s3Deploy.BucketDeployment(this, `DeploySpa-${this.stackName}`, { sources: [s3Deploy.Source.asset('./spa/dist')], destinationBucket: spaBucket, distribution: websiteDistribution, distributionPaths: ['/*'], }) // Show CloudFront distribution domain name new cdk.CfnOutput(this, 'CFTopURL', { value: `https://${websiteDistribution.domainName}/`, }) CloudFront のドメイン名を 出力しておくと何気に楽w