ããã«ã¡ã¯ãXïŒã¯ãã¹ïŒ ã€ãããŒã·ã§ã³ æ¬éš ãœãããŠã§ã¢ãã¶ã€ã³ã»ã³ã¿ãŒã®è¿ã§ãã Amazon Aurora Serverlessã¯ãã³ã³ãã¥ãŒãã£ã³ã°ãšã¡ã¢ãªã®ãã£ãã·ãã£ïŒAurora 容éãŠããã = ACUïŒããªã¯ ãšã¹ ãæ°ã«å¿ããŠèªåã§å€åãããããšãã§ããããŒã¿ããŒã¹ãµãŒãã¹ã§ããäºåã®ã¹ã±ãŒãªã³ã°èšç»ãäžèŠã«ãªãã»ããå®éã®ã¯ãŒã¯ããŒãã«åãããŠãã£ãã·ãã£ã墿žããããããè²»çšã®æé©åã«åããŠããŸãã Aurora Serverless㯠v1 ãš v2 ã®2ã€ã®ããŒãžã§ã³ãäžè¬å©çšå¯èœã§ãããv1 ã®æ¹ã¯ãã°ããã¢ã¯ã»ã¹ããªããš 0 ACU ãŸã§èœãšããŠäžæåæ¢ããæ©èœããããã³ã³ãã¥ãŒãã£ã³ã°ãšã¡ã¢ãªã®æéãå
šãããããªããªããŸããæ¬çªç°å¢ã§ããã°å©çšãããããšã¯ããŸããªããšæããŸããããªã¯ ãšã¹ ãæ°ãéå®çãªéçºç°å¢ã§ã¯ãã®æ©èœãããŸãå©çšããããšã§ãè²»çšãããã«ç¯çŽããããšãã§ããŸãã ãããã 0 ACU ã§äžæåæ¢ããŠããç¶æ
ã®ããŒã¿ããŒã¹ã«ã¢ã¯ã»ã¹ããããšãããšããã£ãã·ãã£ããªãããå忥ç¶ã倱æããŠããŸããŸããæ¬èšäºã¯ã·ã³ãã«ã«ãã®åé¡ãè§£æ¶ããè²»çšãæããæ¹æ³ãèšèŒããŸãã Aurora Serverless ã® ACU èšå® Aurora Serverless ã®ããŒãžã§ã³ã®éã éçºã§ééããåé¡ç¹ å®çŸãããããš å®çŸæ¹æ³ ä»ã®éžæè¢ CDKã³ãŒã å®éã®æé Aurora Serverless ã® ACU èšå® Aurora Serverless 㯠Aurora 容éãŠããã (ACU) ã®æå€§å€ãšæå°å€ãæå®ããããšã§ããã®éã§è² è·ã«å¿ããŠãªãŒãã¹ã±ãŒãªã³ã°ããŸããv1ã«éã£ãŠäžæåæ¢èšå®ãå¯èœã§ããªã¯ ãšã¹ ããå
šããªãæã« 0 ACU ãŸã§ã¹ã±ãŒã«ããŠã³ã§ããŸãã Aurora Serverless v1ã®äžæåæ¢èšå® Aurora Serverless ã®ããŒãžã§ã³ã®éã 2022幎5ææç¹ã«ãããŠãAurora Serverless v1ã«ã§ããŠv2ã«ã§ããªãããšããããŸãã v2㯠Data API ãå©çšã§ããªã CloudFormationã¯ãŸã v2ããµããŒãããŠããªã v2㯠0 ACUïŒäžæåæ¢ïŒã«ã§ãããæå°ACU㯠0.5 ã§ãã ACUã«é¢ããè£è¶³ãšããŠã2022幎5ææç¹ã§v2ã®ACUè²»çšã¯v1ã®2åã«èšå®ãããŠããŸããããªãã¡v2ãæå°ãã£ãã·ãã£0.5 ACUã§åžžæçšŒåããå Žåã®è²»çšã¯ãv1ã1 ACUã§åžžæçšŒåããå Žåã®è²»çšãšåçã«ãªããŸããv1ã¯äžæåæ¢ãå¯èœãªãããäžæåæ¢ãããæéã®åã ãv2ããè²»çšãç¯çŽã§ããŸãã â»ãã®ä»ã®Aurora Serverless v2ã®ç¹åŸŽã¯ã以äžã®åç»ã§è©³ãã解説ãããŠããŸãã JAWS-UGæšªæµ #44 Aurora Serverless v2 éçºã§ééããåé¡ç¹ Data API ãšCDKãå©çšãããã£ããããAurora Serverless v1ãå©çšããŠéçºããŠããç°å¢ããããŸããè²»çšãæããããã«äžæåæ¢æ©èœãæå¹ã«ããŠããŸããã0 ACUã®ãšãã¯ã³ã³ãã¥ãŒãã£ã³ã°ãšã¡ã¢ãªã®åäœãå®å
šã«åæ¢ããããã å忥ç¶ã§ã¯ããŒã¿ããŒã¹ãžã®æ¥ç¶ã倱æãã ãšããåé¡ã«ééããŸããã Communications link failure The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.; SQLState: 08S01 ãã°ããïŒäœæ30ç§ã1åçšåºŠïŒãããšããŒã¿ããŒã¹ãèµ·åããåé¡ãªãæ¥ç¶ã§ããããã«ãªãã®ã§ãããè²»çšãæãã€ã€å忥ç¶ã§ã倱æããªãããã«ãããã£ãããããã®ä»çµã¿ãæ€èšããŸããã å®çŸãããããš Aurora Serverlessã« ã¢ã¯ã»ã¹ããå¯èœæ§ã®ããæé垯ã¯äžæåæ¢ããããã«æäœã§ã1 ACUã§çšŒåããããã以å€ã®æé垯ã¯0 ACUãžã®ã¹ã±ãŒã«ããŠã³ã蚱容 ããã°ãäœåãªè²»çšãåæžããå忥ç¶ã§ãšã©ãŒã«ãªãåé¡ãè§£æ¶ã§ããŸããä»åã¯ã¢ã¯ã»ã¹ããå¯èœæ§ã®ããæé垯ã¯äœè£ãæã£ãŠ 7:00 ~ 22:00 ãšåºãã«å®çŸ©ããŠããŸãã æŽçãããšãå®çŸãããããšã¯æ¬¡ã®ããã«ãªããŸãã å¹³æ¥ã®7:00 ~ 22:00 ã¯ãAurora Serverlessã 0 ACUã«ãªããªããããã«ãDBãžã®æ¥ç¶ãåžžã«æåããããã«ããã ãã以å€ã®æéïŒå¹³æ¥æ·±å€ãšåæ¥ïŒã¯ 0 ACUã«ã¹ã±ãŒã«ããŠã³ããŠãè¯ã å®çŸæ¹æ³ Aurora ã¯ã©ã¹ã¿ ãäžæåæ¢ããªãå Žåã®æå° ACU ã 1 ãšãã Aurora ã¯ã©ã¹ã¿ ãž 2æé ã¢ã¯ã»ã¹ããªãã£ãå Žåãäžæåæ¢ããããïŒ0 ACU ã«ãªãããïŒã«èšå®ãã Aurora ã¯ã©ã¹ã¿ ãžåç
§ç³»ã®ã¯ãšãªãçºè¡ããLambda颿°ãäœæãã å¹³æ¥ã®7:00 ~ 20:00 ã®éã¯1æéããšã«å®æå®è¡ ãã ããããããšã§ãå¹³æ¥ã¯20:00ã«æåŸã®Lambda颿°ãå®è¡ããã22:00ãŸã§ã¯Aurora ã¯ã©ã¹ã¿ ã0 ACUã«ãªããªãããšãä¿èšŒãããŸããå€éã忥ã¯Aurora ã¯ã©ã¹ã¿ ãžã®æ¥ç¶ããªããšäžæåæ¢ããæ¬¡ã®å¹³æ¥ã®æ7:00ã«æåã®Lambda颿°ãå®è¡ãããAurora ã¯ã©ã¹ã¿ ãèµ·åããŸãã ä»ã®éžæè¢ ã·ãã¢ã«è²»çšãæããå¿
èŠããªãå Žå ãäžæåæ¢èšå®ãç¡å¹ã«ãã0 ACUã«ã¹ã±ãŒã«ããŠã³ããªãããã«ããã°ãããŒã¿ããŒã¹ãžã®ã¢ã¯ã»ã¹ããªããŠã忢ããªããªããæ¬èšäºã§è¿°ã¹ããã㪠ã¯ãŒã¯ã¢ã©ãŠã³ã ã¯å¿
èŠãããŸããã ããã㯠0 ACUã«ã¹ã±ãŒã«ããŠã³ããåŸã®å忥ç¶ã倱æããŠãã èµ·åãåŸ
ã£ãŠããã¯ãšãªãåå®è¡ããããšã蚱容ã§ããå Žå ããæ¬èšäºã® ã¯ãŒã¯ã¢ã©ãŠã³ã ã¯å¿
èŠãããŸããã CDKã³ãŒã 以äžã®CDKã³ãŒãã§ç°å¢ãäœæããŸãã import { Duration , Stack , StackProps } from "aws-cdk-lib" ; import * as ec2 from "aws-cdk-lib/aws-ec2" ; import * as events from "aws-cdk-lib/aws-events" ; import * as eventTargets from "aws-cdk-lib/aws-events-targets" ; import * as iam from "aws-cdk-lib/aws-iam" ; import * as lambda from "aws-cdk-lib/aws-lambda" ; import * as lambdaNodejs from "aws-cdk-lib/aws-lambda-nodejs" ; import * as rds from "aws-cdk-lib/aws-rds" ; import { Construct } from "constructs" ; export class MyStack extends Stack { constructor( scope: Construct , id: string , props?: StackProps ) { super( scope , id , props ); const privateSubnetName = "myPrivateSubnet" ; // VPCãäœæ const vpc = new ec2.Vpc ( this , "MyVpc" , { cidr: "10.0.0.0/16" , enableDnsHostnames: true , enableDnsSupport: true , subnetConfiguration: [ { name: privateSubnetName , subnetType: ec2.SubnetType.PRIVATE_ISOLATED , cidrMask: 20 , } , ] , } ); // Aurora Serverless v1 ã¯ã©ã¹ã¿ãäœæ const auroraCluseter = new rds.ServerlessCluster ( this , "MyAuroraCluster" , { engine: rds.DatabaseClusterEngine.AURORA_MYSQL , vpc: vpc , vpcSubnets: { subnetGroupName: privateSubnetName } , // æå°ACUã1ãæå€§ACUã2ãäžæåæ¢ãŸã§ã«å¿
èŠãªéã¢ã¯ãã£ãæéã2æéã«æå® scaling: { minCapacity: 1 , maxCapacity: 2 , autoPause: Duration.hours ( 2 ) } , // Data APIãæå¹å enableDataApi: true , } ); // Lambda颿° const awakeAuroraFunction = new lambdaNodejs.NodejsFunction ( this , "MyFunction" , { // Lambda颿°ãžã®ãã¡ã€ã«ãã¹ entry: "functions/awake-aurora-serverless.ts" , runtime: lambda.Runtime.NODEJS_14_X , // Data APIãå©çšãããããAuroraã¯ã©ã¹ã¿ARNãšã·ãŒã¯ã¬ããã®ARNãç°å¢å€æ°ã§æž¡ã environment: { CLUSTER_ARN: auroraCluseter.clusterArn , SECRET_ARN: auroraCluseter.secret?.secretArn ?? "" , } , } ); // Lambda颿°ã®å®è¡ããŒã«ã«Data APIãžã®ã¢ã¯ã»ã¹ãèš±å¯ããããªã·ãŒã远å if ( auroraCluseter.secret?.secretArn ) { const auroraDataApiPolicy = new iam.ManagedPolicy ( this , "MyAuroraDataApiPolicy" , { statements: [ new iam.PolicyStatement ( { resources: [ ` ${ auroraCluseter.secret.secretArn } *` ] , actions: [ "secretsmanager:GetSecretValue" ] , effect: iam.Effect.ALLOW , } ), new iam.PolicyStatement ( { resources: [ auroraCluseter.clusterArn ] , actions: [ "rds-data:ExecuteStatement" ] , effect: iam.Effect.ALLOW , } ), ] , } ); awakeAuroraFunction.role?.addManagedPolicy ( auroraDataApiPolicy ); } // æ¥æ¬æé å¹³æ¥ã® 7:00 ~ 20:00 ã®éã«1æéããšã«Lambda颿°ãå®æèµ·åãã // cronåŒã¯ UTC ã§èšèŒããããããæ¥æ ~ æšæã®22:00 ~ 23:00ããšãææ ~ éæã® 0:00 ~ 11:00ãã®2ã€ã«åãã new events.Rule ( this , "MyEventRule1" , { schedule: events.Schedule.cron ( { minute: "0" , hour: "22-23" , weekDay: "SUN-THU" } ), targets: [ new eventTargets.LambdaFunction ( awakeAuroraFunction , { retryAttempts: 3 } ) ] , } ); new events.Rule ( this , "MyEventRule2" , { schedule: events.Schedule.cron ( { minute: "0" , hour: "0-11" , weekDay: "MON-FRI" } ), targets: [ new eventTargets.LambdaFunction ( awakeAuroraFunction , { retryAttempts: 3 } ) ] , } ); } } Lambda颿°ã®éšåã§ã¯ã NodejsFunction ã³ã³ã¹ãã©ã¯ã ãå©çšããããšã§ãTypeScript ã§èšè¿°ãã Lambda 颿°ã® JavaScript ãžã® ã³ã³ãã€ã« ãããããã€ãŸã§ãCDKããã£ãŠãããŸãã Aurora ã¯ã©ã¹ã¿ ãžã¯ãšãªãçºè¡ããLambda颿°ã¯ã functions/awake-aurora-serverless.ts ã«ä»¥äžã®ããã«å®è£
ããŸããData API ãå©çšããŠã¯ãšãªãçºè¡ããŸãã import * as RDS from "@aws-sdk/client-rds-data" ; import { Handler } from "aws-lambda" ; const client = new RDS.RDSDataClient ( { region: "ap-northeast-1" } ); export const handler: Handler = async () => { const input = { // Data APIãå©çšããããã®ãAuroraã¯ã©ã¹ã¿ã®ãªãœãŒã¹ARNãšã·ãŒã¯ã¬ããã®ARNãç°å¢å€æ°ããååŸãã resourceArn: process .env.CLUSTER_ARN , secretArn: process .env.SECRET_ARN , // çºè¡ããSQLæ sql: "SHOW databases;" , } ; const command = new RDS.ExecuteStatementCommand ( input ); await client.send ( command ); } ; çºè¡ãã SQL æã¯æŽæ°ç³»ã§ãªããã°äœã§ãè¯ãã§ãããã·ã³ãã«ã« SHOW databases; ãšããŠããŸãã å®éã®æé 以äžã®æ§æã§ãã°ãã皌åããå Žåã®æéã確èªããŸããã 忥ã¯Aurora ã¯ã©ã¹ã¿ ã忢ããæéãããã£ãŠããªãããšãããããŸããå¹³æ¥ã24æéã®ãã¡15æéãã皌åããŠããŸããã ä»åã®æ¡ä»¶ã§ã¯äžé±é 24h * 7æ¥ = 168h ã®ãã¡ã 15h * 5æ¥ = 75h 皌åããŠãããããåžžæçšŒåã«æ¯ã¹ãŠ 75h / 168h = 45% ã®è²»çšã«æããããšãã§ããŠããŸãã äžæ¹ã§å¹³æ¥ã®æ¥åæéäžã¯åžžã«çšŒåç¶æ
ã®ãããæ¥ç¶ã倱æããããšããªããªããä»åã®èŠä»¶ãæºããããšãã§ããŸããã å·çïŒ @kou.kinyo2 ãã¬ãã¥ãŒïŒ @higa ïŒ Shodo ã§å·çãããŸãã ïŒ