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

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

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

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

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

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

664b6e8ebe272fcfa5dbd6070eaf3cd4?s=128

Yusuke Wada

June 26, 2020
Tweet

Transcript

  1. ࣮ફϓϩμΫγϣϯαʔόʔϨε "84$%,ͱ5ZQF4DSJQUʹΑΔ 8FCΞϓϦέʔγϣϯ։ൃύλʔϯ  ࿨ా༞հ

  2.  αʔόʔϨε Web ΞϓϦέʔγϣϯΛຊ൪؀ڥʹσϓϩΠ͢Δ ࠓ೔ͷ࿩

  3. 

  4.  ͜ͷܗΛߟ͑Δεςοϓ·ͰͲ͏ͨͲΓͭ͘ͷʁ ύλʔϯ͸Θ͔͚ͬͨͲͲ͏σϓϩΠ͢Δͷʁ

  5. ຊηογϣϯͷ໨త  αʔόʔϨεΞϓϦέʔγϣϯΛϓϩμΫγϣϯσϓϩΠ ͢ΔϋʔυϧΛԼ͛Δ Ξϓϩʔνɿ ΞϓϦέʔγϣϯσϓϩΠ·ͰͷεςοϓΛ໌Β͔ʹ͢Δ σϓϩΠՄೳੑ͕༗༻Ͱ͋Δ͜ͱΛࣔ͢ ۩ମతͳઃܭύλʔϯΛه࿥ʹ࢒͢

  6. ࿩͢͜ͱɾ࿩͞ͳ͍͜ͱ  ࿩͢ "84ʹΑΔαʔόʔϨεΞϓϦέʔγϣϯ ΞϓϦέʔγϣϯઃܭɺσϓϩΠઓུ ࿩͞ͳ͍ (PPHMF$MPVE1MBUGPSNɼ.JDSPTPGU"[VSF ૊৫ɺίϥϘϨʔγϣϯɺ։ൃख๏

  7. $POUFOUT  αʔόʔϨεͷಘҙͳ͜ͱɾۤखͳ͜ͱΛ࠶֬ೝ͢Δ σϓϩΠ·Ͱͷεςοϓͱ"84$%, "84ͰͷΞϓϦέʔγϣϯύλʔϯ ͦͯ͠ϞμϯΞϓϦέʔγϣϯ΁

  8. $POUFOUT  αʔόʔϨεͷಘҙͳ͜ͱɾۤखͳ͜ͱΛ࠶֬ೝ͢Δ σϓϩΠ·Ͱͷεςοϓͱ"84$%, "84ͰͷΞϓϦέʔγϣϯύλʔϯ ͦͯ͠ϞμϯΞϓϦέʔγϣϯ΁

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

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

    OS Ծ૝Խج൫ ϋʔυ΢ΣΞ Ϣʔβʔ Ϣʔβʔ Ϋϥ΢υϕϯμʔ Ϣʔβʔ Ϋϥ΢υϕϯμʔ Ϋϥ΢υϕϯμʔ αʔϏε੹೚Ϟσϧ ͜ΕΒΛ૊Έ߹ΘͤΔ͜ͱ
  11. 'BSHBUF͸ "VSPSB4FSWFSMFTT͸ʁ  ࿩ΛΘ͔Γ΍͘͢͢ΔͨΊʹɺຊηογϣϯͰ͸αʔ όʔϨεͷର৅ʹؚΊ·ͤΜ 71$͕ొ৔͢ΔͱɺαʔϏεؒ࿈ܞʹ͓͍ͯϓϥΠϕʔτ ωοτϫʔΫΛҙࣝ͢Δඞཁ͕ग़ͯ͘ΔͨΊͰ͢ ͱͯ΋ศརͰɺڧྗͰɺΞϓϦέʔγϣϯߏஙͷબ୒ࢶͱͯ͠ ັྗతͰ͢

  12. αʔόʔϨε͕ಘҙͳ͜ͱϏδωεޮՌͱ͸  αʔϏεΠϯ· Ͱͷ࣌ؒΛ୹ॖ ୹ظ࣮૷࣮ݱ ػձଛࣦ๷ࢭ ӡ༻ɾอकͷ লྗԽ αʔϏε඼࣭҆ఆԽ ಺੡Խ֦େͷࢧԉ

    ΞδϦςΟ޲্ ݱ৔ཁٻͷਝ଎࣮૷ αʔόʔϨεͷޮՌͱ͸? https://aws.amazon.com/jp/serverless/patterns/serverless-benefit/
  13. αʔόʔϨε͕ಘҙͳ͜ͱ  αʔϏεͱ'BB4Λ૊Έ߹ΘͤͯϢʔεέʔεͷҰ෦Λૣظࢢ৔౤ೖՄೳ wطଘγεςϜͷҰ෦Λஔ͖׵͑ w৽͍͠Ϗδωεͷ1SPPGPG$PODFQU wϚΠΫϩαʔϏεͱͯ͠ఆٛͨ͠γεςϜͷߏங 1BZBTZPVHPʢ࢖ͬͨ෼͚ͩ՝ۚʣʹΑΔίετ࠷దԽ wར༻࣌ؒଳ͕ܾ·͍ͬͯΔ؅ཧը໘ͳͲͷΞϓϦ wಛఆͷ࣌ظ͚ͩՔಇ͢ΔΩϟϯϖʔϯαΠτ wσόΠε͕ͲΜͲΜ૿͍͑ͯ͘*P5όοΫΤϯυ

  14. αʔόʔϨε͕ۤखͳ͜ͱ  ϦϨʔγϣϯʹΑͬͯσʔλΛ࿈ܞ͢ΔλΠϓͷγεςϜ w4'"ɺ$3. τϥϯβΫγϣϯ͕සൟ͔ͭେྔʹൃੜ͢ΔγεςϜ wۜߦ ௿ϨΠςϯγ͕ٻΊΒΕΔγεςϜ wΦϯϥΠϯ'14ήʔϜͷαʔόʔ

  15.  ͜͜·Ͱͷ·ͱΊ αʔόʔϨεΞϓϦέʔγϣϯ͸ಛఆͷϢʔεέʔεɺҰ෦ͷ αʔϏεΛ࣮૷ͯ͠ૉૣ͘ࢢ৔ʹ౤ೖ͢Δ͜ͱ͕ಘҙ τϥϯβΫγϣϯ΍௿ϨΠςϯγ͕ٻΊΒΕΔγεςϜ͸ۤख

  16. $POUFOUT  αʔόʔϨεͷಘҙͳ͜ͱɾۤखͳ͜ͱΛ࠶֬ೝ͢Δ σϓϩΠ·Ͱͷεςοϓͱ"84$%, "84ͰͷΞϓϦέʔγϣϯύλʔϯ ͦͯ͠ϞμϯΞϓϦέʔγϣϯ΁

  17. ຊηογϣϯͷ໨త  αʔόʔϨεΞϓϦέʔγϣϯΛϓϩμΫγϣϯσϓϩΠ ͢ΔϋʔυϧΛԼ͛Δ Ξϓϩʔνɿ ΞϓϦέʔγϣϯσϓϩΠ·ͰͷεςοϓΛ໌Β͔ʹ͢Δ σϓϩΠՄೳੑ͕༗༻Ͱ͋Δ͜ͱΛࣔ͢ ۩ମతͳઃܭύλʔϯΛه࿥ʹ࢒͢

  18. ϓϩμΫγϣϯσϓϩΠͷεςοϓ֓؍  ։ൃલ ཁ͕݅αʔόʔϨεʹ߹க͍ͯ͠Δ͜ͱΛ֬ೝ͢Δ AWS ΞΧ΢ϯτ΁ͷαʔϏε഑ஔΛܾΊΔ ֎෦ΠϯλʔϑΣʔεΛઃܭ͢Δ

  19. ϓϩμΫγϣϯσϓϩΠͷεςοϓ֓؍  αʔόʔϨε։ൃܾఆޙ AWS CDK ͷ࠾༻Λݕ౼͢Δ ιʔεϦϙδτϦΛ࡞ͬͯσϓϩΠ͢Δ DEV, STG, PRD

    ؀ڥ΁σϓϩΠͰ͖ΔΑ͏ʹ͓ͯ͘͠ σϓϩΠՄೳͳ CI /CD ύΠϓϥΠϯΛ૊Ή
  20. ϓϩμΫγϣϯσϓϩΠͷεςοϓ֓؍  σϓϩΠͰ͖ͨΒ AWSߏ੒Λઃܭ͢Δ ΞϓϦέʔγϣϯΛ࣮૷͍ͯ͘͠ PRD΁σϓϩΠ ຊ൪Քಇ

  21.  ઌʹσϓϩΠՄೳͳঢ়ଶΛ֫ಘ͠ɺҡ࣋͢Δ Ұ؏ͨ͠ํ਑

  22. ͳͥʁ  Ϋϥ΢υΞϓϦͷҰ൪ͷน͸ɺσϓϩΠͱ݁߹ *".ϙϦγʔɺϦιʔε੍ݶɺΠϕϯτۦಈαʔϏεͷൃՐͳ Ͳɺ࣮؀ڥͰ͔͠Θ͔Βͳ͍͜ͱ͕ͨ͘͞Μ͋Δ ͜Ε͕ϓϩμΫγϣϯσϓϩΠʹ͋ͨͬͯͷҰ൪ͷ৺ཧతɺ࣌ ؒత՝୊ͩͱߟ͑Δ ͳΒ͹ɺૣ͍ஈ֊Ͱ͜ͷ՝୊ΛͭͿͯ͠͠·͓͏ͱ͍͏ൃ૝

  23. ϓϩμΫγϣϯσϓϩΠͷεςοϓ֓؍  ։ൃલ ཁ͕݅αʔόʔϨεʹ߹க͍ͯ͠Δ͜ͱΛ֬ೝ͢Δ AWS ΞΧ΢ϯτ΁ͷαʔϏε഑ஔΛܾΊΔ ֎෦ΠϯλʔϑΣʔεΛઃܭ͢Δ

  24. ཁ͕݅αʔόʔϨεʹ߹க͍ͯ͠Δ͜ͱΛ֬ೝ͢Δ  ௿ϨΠςϯγ΍ಉ࣌ʹेݸҎ্ͷτϥϯβΫγϣϯ͕ٻ ΊΒΕ͍ͯͳ͍͔ͳͲɺۤख෼໺Λ͖͚ͭͭΕ͍ͯͳ͍ ͔ΛνΣοΫ͢Δ

  25. ϏδωεαΠυͱͷڠྗ͕ඞਢ  ͜͜ͰαʔόʔϨε͕߹Θͳ͍Ͱ͢Ͷɺͱ͍͏൑அ΋े ෼͋Γ͑Δ ͦͷܾஅ͸ɺεςʔΫϗϧμʔશһ͕ڵຯΛ࣋ͪɺશһ ͕ೲಘ͢Δ͜ͱ͕ඞਢ

  26. ࣄྫ঺հɿ4#(JGU༷ 

  27. ࣄྫ঺հɿ4#(JGU༷ 

  28. ࣄྫ঺հɿ4#(JGU༷  ͢ͰʹαʔόʔϨεͷಘҙྖҬΛཧղ͓ͯ͠Γ ࠷ॳ͔Βద༻ൣғΛߜ͍͍ͬͯͨͩͨ͜ͱ͕੒ޭཁҼ

  29. ϓϩμΫγϣϯσϓϩΠͷεςοϓ֓؍  ։ൃલ ཁ͕݅αʔόʔϨεʹ߹க͍ͯ͠Δ͜ͱΛ֬ೝ͢Δ AWS ΞΧ΢ϯτ΁ͷαʔϏε഑ஔΛܾΊΔ ֎෦ΠϯλʔϑΣʔεΛઃܭ͢Δ

  30. "84ΞΧ΢ϯτ΁ͷαʔϏε഑ஔΛܾΊΔ  σϓϩΠ͢ΔͨΊʹඞཁͳࡐྉͷͻͱͭ AWSΞΧ΢ϯτ DEV؀ڥ %&7 αʔϏε# AWSΞΧ΢ϯτ PRD؀ڥ AWSΞΧ΢ϯτ

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

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

    αʔϏε$ αʔϏε" αʔϏε# αʔϏε$ 13% αʔϏε" %&7 AWSΞΧ΢ϯτ αʔϏεB 13% %&7 AWSΞΧ΢ϯτ αʔϏεC 13% ͪ͜Β͕͓͢͢Ί - ΞΧ΢ϯτ͝ͱͷϦιʔε্ݶʹͻ͔͔ͬΓʹ͘͘ͳΔ - ΞΧ΢ϯτͷϥϯχϯάίετ = αʔϏεͷϥϯχϯάίετ - ৽͍͠αʔϏεΛ౤ೖ͢Δͱ͖ɺطଘͷՔಇதαʔϏεΛؾʹʹ͠ͳ͘ ͯΑ͍
  33. ϓϩμΫγϣϯσϓϩΠͷεςοϓ֓؍  ։ൃલ ཁ͕݅αʔόʔϨεʹ߹க͍ͯ͠Δ͜ͱΛ֬ೝ͢Δ AWS ΞΧ΢ϯτ΁ͷαʔϏε഑ஔΛܾΊΔ ֎෦ΠϯλʔϑΣʔεΛઃܭ͢Δ

  34. ֎෦ΠϯλʔϑΣʔεΛઃܭ͢Δ  ৄࡉ·Ͱ٧ΊΔඞཁ͸ͳ͘ɺཁ͢ΔʹʮͲ͏ݴ͏ܗͰ αʔϏεΛ࢖ͬͯ΋Β͏͔ʯΛܾΊΔ 3&45"1*ɺ(SBQI2-"1* ΩϡʔΠϯά 4JOHMF1BHF"QQMJDBUJPO ϑΝΠϧΞοϓϩʔυ

  35.  ͊͞։ൃͩ

  36. ϓϩμΫγϣϯσϓϩΠͷεςοϓ֓؍  αʔόʔϨε։ൃܾఆޙ AWS CDK ͷ࠾༻Λݕ౼͢Δ ιʔεϦϙδτϦΛ࡞ͬͯσϓϩΠ͢Δ DEV, STG, PRD

    ؀ڥ΁σϓϩΠͰ͖ΔΑ͏ʹ͓ͯ͘͠ σϓϩΠՄೳͳ CI /CD ύΠϓϥΠϯΛ૊Ή
  37. "84$%,ͷ࠾༻Λݕ౼͢Δ 

  38. αϯϓϧ$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
  39. αϯϓϧ$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
  40. αϯϓϧ$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
  41. αϯϓϧ$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 ςϯϓϨʔτΛੜ੒͍ͯ͠Δ
  42. ͜Ε͸"84͔ΒͷϝοηʔδΛײ͡Δ 

  43. ͜Ε͸"84͔ΒͷϝοηʔδΛײ͡Δ  Ϋϥ΢υϦιʔεͷσϓϩΠ·ͰؚΊͯ ΞϓϦέʔγϣϯ։ൃͩͧ ͓·͕͑΍Ε

  44. "84$%,ͷ࠾༻Λݕ౼͢Δ  ͸͍ɺ΍Γ·͢ ։ൃऀͷٕज़εΠονίετ͕ݮΓɺσϓϩΠ·Ͱͷো น͕গͳ͘ͳΔ ΞϓϦέʔγϣϯΤϯδχΞ͕ΠϯϑϥΛɺΠϯϑϥΤ ϯδχΞ͕ΞϓϦέʔγϣϯΛ։ൃ͢Δ͖͔͚ͬʹͳΔ

  45. ϓϩμΫγϣϯσϓϩΠͷεςοϓ֓؍  αʔόʔϨε։ൃܾఆޙ AWS CDK ͷ࠾༻Λݕ౼͢Δ ιʔεϦϙδτϦΛ࡞ͬͯσϓϩΠ͢Δ DEV, STG, PRD

    ؀ڥ΁σϓϩΠͰ͖ΔΑ͏ʹ͓ͯ͘͠ σϓϩΠՄೳͳ CI /CD ύΠϓϥΠϯΛ૊Ή
  46. ϞϊϨϙʹΑΔΞϓϦέʔγϣϯͷ؅ཧ  ύοέʔδߏ੒Ͳ͏͠·͔͢ ZBSOXPSLTQBDFʹΑΔϞϊϨϙ͕͓͢͢Ί yarn workspace | Yarn https://classic.yarnpkg.com/ja/docs/cli/workspace/

  47. ߏ੒ྫ  QBDLBHFT NBOBHFNFOUSFBDU MBNCEBOPEF JOGSBBXTDEL QBDLBHFKTPO UTDPOpHCBTFKTPO ./

  48. ߏ੒ྫ  QBDLBHFT NBOBHFNFOUSFBDU MBNCEBOPEF JOGSBBXTDEL QBDLBHFKTPO UTDPOpHCBTFKTPO ./ -

    ֤ workspace Ͱಠཱͯ͠։ൃ͕Մೳ - ಉ࣌ʹɺϓϩδΣΫτϧʔτͷ package.json ͷεΫϦϓτ࣍ୈͰɺ·ͱ ΊͯϏϧυ͢Δ͜ͱ΋Մೳ
  49. -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, }, }); …
  50. -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, }, }); …
  51. -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
  52. ϓϩμΫγϣϯσϓϩΠͷεςοϓ֓؍  αʔόʔϨε։ൃܾఆޙ AWS CDK ͷ࠾༻Λݕ౼͢Δ ιʔεϦϙδτϦΛ࡞ͬͯσϓϩΠ͢Δ DEV, STG, PRD

    ؀ڥ΁σϓϩΠͰ͖ΔΑ͏ʹ͓ͯ͘͠ σϓϩΠՄೳͳ CI /CD ύΠϓϥΠϯΛ૊Ή
  53. ؀ڥ͝ͱʹσϓϩΠͰ͖ΔΑ͏ʹ͓ͯ͘͠  ελοΫ໊Λ؀ڥ͝ͱʹม͑Δ͜ͱͰରԠՄೳ ؀ڥม਺ͳͲʹೖΕ͓ͯ͘ͱྑ͍ 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; }
  54. ؀ڥ͝ͱʹσϓϩΠͰ͖ΔΑ͏ʹ͓ͯ͘͠  ελοΫ໊Λ؀ڥ͝ͱʹม͑Δ͜ͱͰରԠՄೳ ؀ڥม਺ͳͲʹೖΕ͓ͯ͘ͱྑ͍ 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; }
  55. ϓϩμΫγϣϯσϓϩΠͷεςοϓ֓؍  αʔόʔϨε։ൃܾఆޙ AWS CDK ͷ࠾༻Λݕ౼͢Δ ιʔεϦϙδτϦΛ࡞ͬͯσϓϩΠ͢Δ DEV, STG, PRD

    ؀ڥ΁σϓϩΠͰ͖ΔΑ͏ʹ͓ͯ͘͠ σϓϩΠՄೳͳ CI /CD ύΠϓϥΠϯΛ૊Ή
  56. "84$PEF1JQFMJOFͰ΋σϓϩΠͰ͖ΔΑ͏ʹ͓ͯ͘͠  NBTUFSϒϥϯν͔Β EFW؀ڥ͸ࣗಈσϓϩΠ TUH QSE͸ঝೝϑϩʔΛڬ Ήɺ͕͓͢͢Ί ঝೝΛϦδΣΫτ͢Ε͹ɺϦ Ϧʔε͞Εͳ͍ʢӈਤʣ

  57. ΋ͪΖΜ"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, }, }, }, }, );
  58.  αʔόʔϨεͰઓ͏͜ͱʹશһೲಘ͢Δ AWS CDK ͰσϓϩΠՄೳঢ়ଶΛ࠷ॳʹͭ͘Δ ͜͜·Ͱͷ·ͱΊ

  59. ϓϩμΫγϣϯσϓϩΠͷεςοϓ֓؍  σϓϩΠͰ͖ͨΒ AWSߏ੒Λઃܭ͢Δ ΞϓϦέʔγϣϯΛ࣮૷͍ͯ͘͠ PRD΁σϓϩΠ ຊ൪Քಇ

  60. $POUFOUT  αʔόʔϨεͷಘҙͳ͜ͱɾۤखͳ͜ͱΛ࠶֬ೝ͢Δ σϓϩΠ·Ͱͷεςοϓͱ"84$%, "84ͰͷΞϓϦέʔγϣϯύλʔϯ ͦͯ͠ϞμϯΞϓϦέʔγϣϯ΁

  61. ͋ͱ͸ΞϓϦέʔγϣϯͷத਎Λॆ࣮͍ͤͯ͘͞  "84ߏ੒Λઃܭ͢Δ ඞཁʹԠͯ͡-BNCEB'VODUJPOͷίʔυΛॻ͘ "84$%,ʹ͓ͯ͜͠σϓϩΠ͢Δ σϓϩΠ͢Δ

  62. ͋ͱ͸ΞϓϦέʔγϣϯͷத਎Λॆ࣮͍ͤͯ͘͞  "84ߏ੒Λઃܭ͢Δ ඞཁʹԠͯ͡-BNCEB'VODUJPOͷίʔυΛॻ͘ "84$%,ʹ͓ͯ͜͠σϓϩΠ͢Δ σϓϩΠ͢Δ ✖ N

  63. ࣮੷ͷ͋Δύλʔϯ͔Βͷྲྀ༻ɾԠ༻͕Φεεϝ  ઃܭϦϑΝϨϯε"84ެࣜ αʔόʔϨεύλʔϯ https://aws.amazon.com/jp/serverless/patterns/serverless-pattern/

  64. ࣮੷ͷ͋Δύλʔϯ͔Βͷྲྀ༻ɾԠ༻͕Φεεϝ  ઃܭϦϑΝϨϯεॻ੶

  65. ࣮੷ͷ͋Δύλʔϯ͔Βͷྲྀ༻ɾԠ༻͕Φεεϝ  ઃܭϦϑΝϨϯεαʔόʔϨείϛϡχςΟ

  66. ࣮੷ͷ͋Δύλʔϯ͔Βͷྲྀ༻ɾԠ༻͕Φεεϝ  ࣮૷ϦϑΝϨϯε"84$%,&YBNQMFT

  67. ࣮੷ͷ͋Δύλʔϯ͔Βͷྲྀ༻ɾԠ༻͕Φεεϝ  ࣮૷ϦϑΝϨϯε%FWFMPQFST*0

  68. ࠓ೔͸·ͩαϯϓϧ͕গͳͦ͏ͳύλʔϯΛ঺հ  "84"QQ4ZODʹΑΔ#BDLFOEGPS'SPOUFOE 71$-JOLΛར༻ͨ͠71$಺"1*ͱͷ࿈ܞ Ͳ͏ͯ͠΋+BWBϥΠϒϥϦΛ࢖Θͳͯ͘͸ͳΒͳ͍ 4UFQ'VODUJPOTΛར༻ͨ͠αʔόʔϨεδϣϒ

  69. "84"QQ4ZODʹΑΔ#BDLFOEGPS'SPOUFOE  ओʹΫϥΠΞϯτΞϓϦέʔγϣϯʹ(SBQI2-"1* Λఏڙ͢Δ 'BDFCPPL΍(JU)VCͳͲɺϢʔβʔ޲͚"1*Λ(SBQI2-Ͱఏ ڙ͢Δύλʔϯ΋૿͖͑ͯͨ ·ͣ͸؅ཧը໘ͷόοΫΤϯυͱͯ͠ར༻͢Δͷ͕ศར

  70. "84"QQ4ZODʹΑΔ#BDLFOEGPS'SPOUFOE 

  71. "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'), });
  72. "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'), });
  73. "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(), });
  74. "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Λ௥Ճ
  75. "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Λ௥Ճ
  76. 71$-JOLΛར༻ͨ͠71$಺"1*ͱͷ࿈ܞ  طଘγεςϜͷҰ෦ΛαʔόʔϨεͰஔ͖׵͑Δ৔߹ʹ ૺ۰͢Δέʔε ߏஙࡁΈͷ&$αʔόʔ܊ʹ/FUXPSL-PBE#BMBODFSܦ༝ Ͱ"1*(BUFXBZ͔ΒΞΫηεͰ͖Δ Πϯλʔωοτʢͨͱ͑͹41"ͳͲͷ؅ཧը໘ʣ͔ΒͷΞΫ ηεΛ"1*(BUFXBZʹू໿Ͱ͖ΔɻಛʹೝূํࣜΛ"VUI ϩάΠϯͳͲͰ౷Ұ͍ͨ͠ύλʔϯ

  77. 71$-JOLΛར༻ͨ͠71$಺"1*ͱͷ࿈ܞ 

  78. 71$-JOLΛར༻ͨ͠71$಺"1*ͱͷ࿈ܞ  71$ /-#·Ͱ ߏஙࡁΈͱ͠·͢

  79. "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'}] },
  80. "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࡞੒
  81. "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Λ઀ଓ
  82. Ͳ͏ͯ͠΋+BWBϥΠϒϥϦΛ࢖ΘͶ͹ͳΒͳ͍  ଞϕϯμʔͷ֎෦"1*ͷར༻ํ๏Ͱɺ+"3Λ௨͔͢͠ ͳ͍έʔεͳͲ -BNCEB'VODUJPOશମΛ+BWBݴޠͰ࣮૷͢Δ͔ ϨΠςϯγ͕͞΄Ͳؔ৺ࣄͰͳ͍৔߹͸ɺ+"3ϥΠϒϥϦΛ࣮ ߦ͢ΔͷΈͷ࠷খͷ-BNCEB'VODUJPOʢ+BWBʣΛ༻ҙ͢Δͷ ΋ख

  83. Ͳ͏ͯ͠΋+BWBϥΠϒϥϦΛ࢖ΘͶ͹ͳΒͳ͍  ଞϕϯμʔͷ֎෦"1*ͷར༻ํ๏Ͱɺ+"3Λ௨͔͢͠ ͳ͍έʔεͳͲ -BNCEB'VODUJPOશମΛ+BWBݴޠͰ࣮૷͢Δ͔ ϨΠςϯγ͕͞΄Ͳؔ৺ࣄग़ͳ͍৔߹͸ɺ+"3ϥΠϒϥϦΛ࣮ ߦ͢ΔͷΈͷ࠷খͷ-BNCEB'VODUJPOʢ+BWBʣΛ༻ҙ͢Δͷ ΋ख

  84. -BNCEB'VODUJPOͰϥοϓ͢Δ 

  85. ߏ੒ྫ  QBDLBHFT NBOBHFNFOUSFBDU MBNCEBOPEF JOGSBBXTDEL QBDLBHFKTPO UTDPOpHCBTFKTPO ./ MBNCEBKBWB

  86. ߏ੒ྫ  QBDLBHFT NBOBHFNFOUSFBDU MBNCEBOPEF JOGSBBXTDEL QBDLBHFKTPO UTDPOpHCBTFKTPO ./ MBNCEBKBWB

    ௥Ճ (SBEMFͰϏϧυɺ-BNCEB 'VODUJPOͷ+"3ੜ੒
  87. 4UFQ'VODUJPOTΛར༻ͨ͠αʔόʔϨεδϣϒ  ͲΜͳͱ͖ʹ4UFQ'VODUJPOT͕ొ৔͢Δ͔ -BNCEB'VODUJPO͕ෳ਺ͷ࢓ࣄΛ੥͚ෛ͓ͬͯΓɺ෼ղͯ͠ δϣϒͱ͍ͨ͠ ͳ͓͔ͭɺਐߦঢ়گ͕Θ͔Δͱخ͍͠έʔε ྫɿ؅ཧը໘͔Β࣮ߦ͢ΔσʔλҰׅొ࿥ॲཧ

  88. 4UFQ'VODUJPOTΛར༻ͨ͠αʔόʔϨεδϣϒ 

  89. ҰׅॲཧͳͲͰΑ͘Έ͔͚ΔΞϨΛαʔόʔϨεͰ 

  90. ҰׅॲཧͳͲͰΑ͘Έ͔͚ΔΞϨΛαʔόʔϨεͰ  ΞϨ MPBEJOH

  91. "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), });
  92. 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ΛϝιουͰ਺चͭͳ͗ʹ 
  93.  ͜͜·Ͱͷ·ͱΊ "84$%,Λ࢖ͬͯઌʹσϓϩΠͯ͠͠·͏͜ͱΛ͓ ͢͢Ί͠·ͨ͠ த਎ͷ࡞ΓࠐΈύλʔϯΛ͍͕ͭࣔ͘͠·ͨ͠ ͜ΕͰɺʮσϓϩΠ͚ͩͰ΋΍ͬͯΈΑ͏͔ͳʯͱͳΕ ͹໨తୡ੒Ͱ͢

  94. αϯϓϧ༻ҙ͋Γ·͢  "84$%, -BNCEB'VODUJPOͷϞϊϨϙ https://github.com/cm-wada-yusuke/template-aws-cdk-typescript-serverless-app

  95. $POUFOUT  αʔόʔϨεͷಘҙͳ͜ͱɾۤखͳ͜ͱΛ࠶֬ೝ͢Δ σϓϩΠ·Ͱͷεςοϓͱ"84$%, "84ͰͷΞϓϦέʔγϣϯύλʔϯ ͦͯ͠ϞμϯΞϓϦέʔγϣϯ΁

  96. Ϋϥεϝιου.PEFSO"QQMJDBUJPO%FWFMPQNFOU  Ϋϥεϝιουɹ."%

  97. Ϋϥεϝιου.PEFSO"QQMJDBUJPO%FWFMPQNFOU  ຊ൪؀ڥʹσϓϩΠͨ͠ΞϓϦέʔγϣϯ͸ɺ͓ۚΛՔ͙͜ ͱ͕Ͱ͖·͢ wมԽ΁ͷ௥ਵ͕ඞཁ wຊηογϣϯ͸αʔόʔϨεΞϓϦέʔγϣϯͷσϓϩΠՄೳੑʹ ΑΔΞδϦςΟ֬อʹϑΥʔΧε͕ͨ͠ɺՁ஋Λఏڙ͚ͭͮ͠Δͨ Ίͷཁૉ͸ͨ͘͞Μ͋Δ wมԽʹ௥ਵͰ͖ɺՁ஋Λఏڙ͚ͭͮ͠ΒΕΔΞϓϦΛϞμϯΞϓϦ έʔγϣϯͱఆٛ͠ɺ։ൃɾίϯαϧαʔϏεͱͯ͠ల։͍ͯ͠·

    ͢
  98. %FW*0$POOFDU݄೔͕."%ಛूͰ͢ʂ 

  99. ࢀߟॻ੶ɾར༻ͨ͠πʔϧ  w DODGXHTFSWFSMFTT$/$'4FSWFSMFTT8(IUUQTHJUIVCDPNDODG XHTFSWFSMFTT w "84$%,IUUQTHJUIVCDPNBXTBXTDEL w DNXBEBZVTVLFUTBTDELIUUQTHJUIVCDPNDNXBEBZVTVLF UTBTDEL

  100. ηογϣϯޙ͸Ξϯέʔτ΁ͷ͝ڠྗΛΑΖ͓͘͠ئ͍͠·͢ɻ ͝ճ౴͍͍ͨͩͨํʹ͸ޙ೔ɺࢿྉΛૹ෇͍ͨ͠·͢ɻ SNS౤ߘʹ͸ͪ͜ΒΛ͓࢖͍͍ͩ͘͞ɻ #devio2020 ΠϕϯτͷϙʔλϧαΠτ͸ͪ͜Β https://classmethod.jp/m/devio_2020_connect/ ϥΠϒηογϣϯ࿥ը΋ͪ͜Β͔Β ࢹௌͰ͖·͢ɻ https://forms.gle/ytMbgwo3ujUi2bsX9 15:00ʙ15:45

    ʮ࣮ફϓϩμΫγϣϯαʔόʔϨε - AWS CDKͱTypeScript ʹΑΔWebΞϓϦέʔγϣϯ։ൃύλʔϯʯ
 ͷΞϯέʔτ͸ͪ͜ΒͰ͢ɻ Q&A Q&A