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

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

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

55c9804c185f493816433678393cbe92?s=128

sumihiro3

May 28, 2020
Tweet

Transcript

  1. "84 ͰαʔόʔϨεͳ -*/&#PU-*''ΞϓϦ؀ڥ Λ*B$ ͳײ͡Ͱߏங͢ΔΑʂ -*/&%FWFMPQFS$PNNVOJUZ αʔόϨεʷ-*/&ͰΞϓϦ։ൃͯ͠Έͨ-5  4VNJIJSP ,BHBXB

  2. ࣗݾ঺հ

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

    ฌݿݝ n ৬ۀ l ίϯαϧˍΤϯδχΞ n ͦͷଞଐੑ l -*/&"1*&YQFSU l -*/& ք۾ͷΠϕϯτ΍ϋοΧιϯ౳ʹΑ͘ग़຅͠·͢ l "84 ΛΑ͘࢖͍·͢ Ø ͕ɺ࠷ۙ͸($1 ͕޷͖͔΋͆ 3
  4. MJOFED MEHL MEHR n ։ൃऀίϛϡχςΟͰϋϯζΦϯߨࢣͳͲΛ͍ͯ͠·͢ 4 -*/&%FWFMPQFS $PNNVOJUZ ʢ౦ژʣ -*/&%FWFMPQFS

    (SPVQ,BOTBJ ʢؔ੢ʣ -*/&%FWFMPQFS (SPVQ,ZVTIV ʢ۝भʣ
  5. ࣍ճΠϕϯτొஃ͠·͢ʂ https://linedevelopercommunity.connpass.com/event/176924/

  6. ެࣜ4%, ͕ແ͍ͷͰ࡞ͬͪΌ͍·ͨ͠ -*/&1BZ"1*4%,GPS1ZUIPO 6 https://pypi.org/project/line-pay/ $ pip install line-pay

  7. Ϋϥ΢υ αʔόʔϨε -*/& ຊ೔ͷςʔϚ

  8. ݸਓతʹ޷͖ͳαʔόʔϨε؀ڥ n(PPHMF$MPVE1MBUGPSN'JSFCBTF 8 Google App Engine

  9. ݸਓతʹ޷͖ͳαʔόʔϨε؀ڥ n"NB[PO8FC4FSWJDFT 9 Amazon Simple Storage Service Amazon DynamoDB Amazon

    API Gateway Amazon Simple Queue Service AWS Lambda Amazon CloudFront
  10. ݸਓతʹ޷͖ͳαʔόʔϨε؀ڥ nLJOUPOF 10

  11. Ϋϥ΢υͷ؀ڥߏங Ͳ͏͍ͯ͠·͔͢ʁ

  12. ख࡞ۀʁ n ドキュメントやQiita 記事を⾒ながら︖ ຖճɺؒҧΘͣʹ΍Δͷ͸େม 12

  13. ຖճख࡞ۀ͸ແཧʂ ࢲɺࣦഊͪ͠Ό͏ͷͰ 13

  14. ΍ͬͺΓࣗಈԽʂ n サーバーレスだし⾃動化したい︕ Πϯϑϥ͸Ϛωʔδυͳͷʹߏங͸खಈͳΜͯΠϠͩʂ 14

  15. ͦ͜Ͱ *B$ 

  16. *OGSBTUSVDUVSFBT$PEF *B$ ͱ͸ n8JLJQFEJB l *OGSBTUSVDUVSFBT$PEFʢ*B$ʣ ͸ίϯϐϡʔςΟ ϯάɾΠϯϑϥʢϓϩηεɺϕΞϝλϧαʔόʔɺԾ ૝αʔόʔͳͲʣͷߏ੒؅ཧɾػցॲཧՄೳͳఆٛ ϑΝΠϧͷઃఆɾϓϩϏδϣχϯάΛࣗಈԽ͢Δϓϩ

    ηεͰ͋Δɻ 16 https://ja.wikipedia.org/wiki/Infrastructure_as_Code
  17. "84Y*B$ ͱݴ͑͹ n"84$MPVE'PSNBUJPO $'O l ϓϩάϥϛϯάݴޠ·ͨ͸γϯϓϧͳςΩετϑΝΠ ϧΛ࢖༻ͯ͠ɺ͋ΒΏΔϦʔδϣϯͱΞΧ΢ϯτͰΞ ϓϦέʔγϣϯʹඞཁͱ͞ΕΔ͢΂ͯͷϦιʔεΛɺ ࣗಈԽ͞Εͨ҆શͳํ๏ͰϞσϧԽ͠ɺϓϩϏδϣχ ϯάͰ͖·͢ɻ͜Ε͸ɺ"84ͱαʔυύʔςΟ੡ͷ

    Ϧιʔεʹਅʹ୯ҰͷιʔεΛ༩͑·͢ɻ 17 https://aws.amazon.com/jp/cloudformation/
  18. $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
  19. αʔόʔϨε ϑϨʔϜϫʔΫ ͷग़൪ͩʂ

  20. 4FSWFSMFTT'SBNFXPSL ͱ͸ n4FSWFSMFTT'SBNFXPSL l 5IF4FSWFSMFTT'SBNFXPSLHJWFTZPV FWFSZUIJOHZPVOFFEUPEFWFMPQ EFQMPZ  NPOJUPSBOETFDVSFTFSWFSMFTTBQQMJDBUJPOT POBOZDMPVE

    20 https://www.serverless.com/
  21. 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
  22. ͦ͜Ͱ "84$%,

  23. "84$%,ͱ͸ n"84$MPVE%FWFMPQNFOU,JU l "84Ϋϥ΢υ։ൃΩοτ "84$%, ͸ɺ࢖͍׳ ΕͨϓϩάϥϛϯάݴޠΛ࢖༻ͯ͠Ϋϥ΢υΞϓϦ έʔγϣϯϦιʔεΛϞσϧԽ͓ΑͼϓϩϏδϣχϯ ά͢ΔͨΊͷΦʔϓϯιʔεͷιϑτ΢ΣΞ։ൃϑ ϨʔϜϫʔΫͰ͢ɻ

    23 https://aws.amazon.com/jp/cdk/
  24. "84$%, nରԠͨ͠ϓϩάϥϛϯάݴޠ l 1ZUIPO l 5ZQF4DSJQU l +BWB l /&5

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

    *%& ʹΑΔิ׬ͷԸܙ͕ಘΒΕқ͍zIJHIFSMFWFM DPOTUSVDUTz͕ॱ࣍ϦϦʔε͞Ε͍ͯΔ l "1* ϦϑΝϨϯε͋Γ·͢ʢӳޠͷΈʣ Ø ຊࢿྉͰ͸WΛϕʔεʹઆ໌ 25 https://docs.aws.amazon.com/cdk/api/latest/
  26. "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/
  27. ࣮ࡍʹ n -*/&#PU-*''ΞϓϦͷαʔόʔϨε؀ڥΛߏஙʂ 27 Amazon Simple Storage Service Amazon DynamoDB

    Amazon API Gateway Amazon Simple Queue Service AWS Lambda Amazon CloudFront
  28. -*/&#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
  29. DEL JOJU n·ͣ͸ΞϓϦͷॳظԽ l ॳظԽ͸ద౰ͳσΟϨΫτϦʹͯίϚϯυͰ0, Ø ΞϓϦͷςϯϓϨʔτ͕ల։͞ΕΔ - ࠓճ͸ 5ZQF4DSJQUͰʂ

    29 $ mkdir cdk_sample && cd cdk_sample $ cdk init app --language=typescript cdk_sample/lib/CdkSampleStack.ts にリソース定義のコードを記述していく 使いたい言語を指定
  30. %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 の指定も明確!
  31. -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'), })
  32. -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 の差し込みもこれだけ! 環境変数も 簡単に設定
  33. -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], } )
  34. -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 の 作成も一行!
  35. "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')
  36. DEL CVJMEDEL EFQMPZ nߏஙίʔυ͕ग़དྷͨΒϏϧυˍσϓϩΠ 36 $ yarn build $ cdk

    synth $ cdk deploy CloudFormation テンプレートを確認できる
  37. CVJMEEFQMPZ nσϓϩΠͷ༷ࢠΛோΊͳ͕Β࢑͠଴ͭ 37 $ cdk deploy

  38. %ZOBNP%# nςʔϒϧͰ͖ͯΔʂ 38

  39. -BNCEB n-BNCEB'VODUJPO<"1*> 39

  40. -BNCEB n-BNCEB'VODUJPO<%#4USFBN> 40

  41. -*''ΞϓϦ؀ڥ n4 $MPVE'SPOU 41 AWS Cloud S3 Bucket LIFF Users

    HTTPS Access LINE Platform LIFF API Amazon CloudFront Deploy SPA app
  42. 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', })
  43. $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
  44. $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, }, )
  45. 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
  46. ઃఆ಺༰Λཧղ͠΍͍͢  *%& ͷิ׬͕ར͍ͯخ͍͠ʂ

  47. ΞϓϦ؀ڥߏங͕ खܰʹࣗಈԽͰ͖Δʂ

  48. ͋ͱ͸-*/&ଆΛઃఆ͢Δ͚ͩ

  49. Μʁ -*/&ଆͷ ઃఆ͸ը໘Ͱ ΍Δ͔͠ͳ͍ʁ

  50. ΞΧ΢ϯτઃఆͷ$-*ɾ"1* ͷ͝ఏڙ͓ئ͍͠·͢ʂ ѪΏ͑ʹʂ

  51. ·ͱΊ nં֯ͷΫϥ΢υɾαʔόʔϨε؀ڥͳͷͰ ؀ڥߏங΋ੵۃతʹࣗಈԽ͍͖ͯ͠·͠ΐ͏ʂ l :".-౳Ͱͷઃఆ͕ۤखͳਓ͸"84$%,Λࢼͦ͏ Ø ޷ΈͷϓϩάϥϛϯάݴޠͰॻ͚Δ͸ͣ l (" ͞Εͨ͹͔ΓͰ"1*

    ͸සൟʹߋ৽͞Ε͍ͯΔ໛༷ l $*$% ςετ؀ڥߏஙͷࣗಈԽʹ΋ϐολϦʂ l -*/&1MBUGPSN΋πʔϧ͍ͩ͘͞ʂ 51
  52. ঺հͨ͠"84 $%, ͷ ιʔείʔυ͸ίνϥ https://github.com/sumihiro3/line-bot-and-liff-with-aws-cdk

  53. ͝ਗ਼ௌ ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ

  54. &0$