ããã«ã¡ã¯ãXïŒã¯ãã¹ïŒ ã€ãããŒã·ã§ã³ æ¬éš ãœãããŠã§ã¢ãã¶ã€ã³ã»ã³ã¿ãŒ ã»ãã¥ãªãã£ã°ã«ãŒãã®è¿ã§ãã Amazon CloudWatch RUM 㯠Webãã©ãŠã¶ ã§çºçããã¢ããªã±ãŒã·ã§ã³ã®ãšã©ãŒãããã©ãŒãã³ã¹æ
å ±ãåéããã¢ãã¿ãªã³ã°ããããã®æ©èœã§ãã ECSã®ãã«ãŒã°ãªãŒã³ãããã€ã¡ã³ããå©çšããŠWebã¢ããªããããã€ããŠããã®ã§ãããCloudWatch RUMãå©çšããã«ããã£ãŠæ¬çªææ Œåã®ãã¹ãç°å¢ããã®ããŒã¿ããåæã®éã®ãã€ãºã«ãªããªããããæ¬çªç°å¢ããã®ããŒã¿ãšæ··ãããªãããã«ããããšæããŸãããããã§ä»åã¯ãã¹ãç°å¢ããã¯CloudWatch RUMã«ããŒã¿ãéä¿¡ãããæ¬çªç°å¢ã«ææ Œããæã®ã¿ããŒã¿ãéä¿¡ããæ¹æ³ã«ã€ããŠæžããŸãã ã€ã³ãã©æ§æ CloudWatch RUMã®å©çšéå§æ¹æ³ ãã¹ãç°å¢ã®ããŒã¿ãæ··ãã£ãŠããŸã è§£æ±ºæ¹æ³ CloudFrontçšTLSèšŒææžã®çšæ Webã¢ããªã§å€éšããã³ãŒãã¹ãããããååŸãã ã³ãŒãã¹ããããæ ŒçŽçšã®S3ãã±ãããäœæ S3ãã±ããã®ã¢ã¯ã»ã¹å¶åŸ¡ S3ãã±ããã«ã³ãŒãã¹ããããããã¡ã€ã«ã§è¿œå ãã CloudFrontãã£ã¹ããªãã¥ãŒã·ã§ã³ã®äœæ ãã¡ã€ã³ã®ã«ãŒãã£ã³ã° åãã®ç¢ºèª ã€ã³ãã©æ§æ 以äžã®æ§æã§ã¢ããªã±ãŒã·ã§ã³ã皌åããŠããŸãã Webã¢ããªã±ãŒã·ã§ã³ãã³ã³ãããšããŠãã«ãããECRã«ã€ã¡ãŒãžãããã·ã¥ ECSãµãŒãã¹ïŒFargateïŒãšã㊠ãã¹ãã£ã³ã° ECSã®ãã«ãŒã°ãªãŒã³ãããã€ã¡ã³ããå©çšïŒè©³çŽ°ã¯ ãã¡ãã®èšäº ã«ãŠïŒ æ¬çªç°å¢ã443ããŒãããã¹ãç°å¢ã8443ããŒããšããŠALBã§ç°ãªãã¿ãŒã²ããã°ã«ãŒãã«ã«ãŒãã£ã³ã° ã«ã¹ã¿ã ãã¡ã€ã³ ã® TLS èšŒææžãALBã§äœ¿çš CloudWatch RUMã®å©çšéå§æ¹æ³ CloudWatch RUMãå©çšããã«ã¯ããŸããããžã¡ã³ãã³ã³ãœãŒã«ããã¢ããªã±ãŒã·ã§ã³ã¢ãã¿ãŒã远å ããŸãã äœæãããš JavaScript ã® ã³ãŒãã¹ãããã ã衚瀺ããããããã¢ããªã±ãŒã·ã§ã³ã«è¿œå ããã ãã§ãã©ãŠã¶ããããŒã¿ãåéãããããã«ãªããŸãã ãã¹ãç°å¢ã®ããŒã¿ãæ··ãã£ãŠããŸã ã¢ããªã±ãŒã·ã§ã³ã¢ãã¿ãŒã远å ããéã«ã¯ããŒã¿ãåéããã¢ããªã® ãã¡ã€ã³ ãæå®ããã®ã§ããã以å€ã® ãã¡ã€ã³ ããéä¿¡ãããããŒã¿ã¯åãä»ããŠãããŸãããããªãã¡ localhost ãªã©ã§ã¢ããªãå®è¡ããå Žåã®ããŒã¿ãã¢ããªã±ãŒã·ã§ã³ã¢ãã¿ãŒã«ç»é²ãããããšã¯ãããŸããã ãããããŒã¿åéå
ã®ã¢ããªã®ããŒãçªå·ã¯ãèšäºå·çæç¹ã§ã¯æå®ã§ããŸããã§ãããECSã®ãã«ãŒã°ãªãŒã³ãããã€ã¡ã³ãã§ã¯åäž ãã¡ã€ã³ ã®ç°ãªãããŒãçªå·ã§æ¬çªç°å¢ãšãã¹ãç°å¢ãååšããããããã¹ãç°å¢ã«ã¢ã¯ã»ã¹ãããšãã®ããŒã¿ãCloudWatch RUMã«ç»é²ãããŠããŸããŸããæ¬çª ãã©ãã£ãã¯ ã«æ¯ã¹ãŠãã¹ã ãã©ãã£ã㯠ãååã«å°ãªããã°æ°ã«ããªããŠãè¯ããããããŸããããä»åã¯æ¬çªç°å¢ããã®ã¿ããŒã¿ãéä¿¡ãããä»çµã¿ãäœã£ãŠã¿ãŸãã è§£æ±ºæ¹æ³ ãã«ãŒã°ãªãŒã³ãããã€ã¡ã³ãã®å Žåãæ¬çªç°å¢ãšãã¹ãç°å¢ã¯åäžã®æ§æã§ãããã«ãŒãã£ã³ã°ã ããç°ãªããŸããã€ãŸãæ¬çªç°å¢ã«ã ãCloudWatch RUMã® ã³ãŒãã¹ãããã ãå«ããããã³ã³ããã«æž¡ã ç°å¢å€æ° ã«ãã£ãŠããŒã¿éä¿¡ã®æå¹åãå¶åŸ¡ãããããããšã¯ã§ããŸããã ããã§CloudWatch RUMã® ã³ãŒãã¹ãããã ãçŽæ¥ã¢ããªã«å«ããã®ã§ã¯ãªããã©ãŠã¶ã§å€éšããååŸããããã«ããååŸãã ã¹ã¯ãªãã ãžã®ãã©ãŠã¶å
ã¢ã¯ã»ã¹ãCORSã§å¶éããããã«ããŸãããããããããšã§ããã¹ãç°å¢ã§ã¯ ã¹ã¯ãªãã ããã©ãŠã¶ã§å®è¡ããããããŒã¿ã¯CloudWatch RUMã«éä¿¡ãããŸããããã¹ãç°å¢ã§ã¯ãã©ãŠã¶ã®ã³ã³ãœãŒã«ã«CORSãšã©ãŒãåºãŸãããæ¬çªç°å¢ã§ã¯ãªãã®ã§åé¡ãªãã§ãããã å
·äœçã«ã¯ã€ã³ ãã©ãª ãœãŒã¹ãšããŠS3 ãã±ãã ãäœæããŠCORSãèšå®ããããã« ã³ãŒãã¹ãããã ããã¡ã€ã«ã§è¿œå ããŸããããã ãã§ãåããšã¯æããŸãããS3 ãã±ãã ããããªãã¯ã«ããããªãããã ãã±ãã èªäœã¯éå
¬éã®ãŸãŸã§CloudFrontçµç±ã§ã³ã³ãã³ããé
ä¿¡ããããã«ããŸããCloudFront ãã£ã¹ããªãã¥ãŒã·ã§ã³ ã«ã¯ ç¬èªãã¡ã€ã³ ã® TLS èšŒææžãé¢é£ä»ããŸãããCORSãå¹ãããããã«ãã¢ããªã® ãã¡ã€ã³ ãšã¯ã¯ãã¹ ãã¡ã€ã³ ã«ãªãããã«ããŸãã 以äžã§ã¯é ã远ã£ãŠèšå®æ¹æ³ã説æããŸãã CloudFrontçš TLS èšŒææžã®çšæ TLS èšŒææžããããããçºè¡ããŠãããŸããä»åã¯ã¢ã㪠ãã¡ã€ã³ my-domain.com ã® ãµããã¡ã€ã³ ãšããŠã static.my-domain.com ãå©çšãããšããŸããCORSã®ãªãªãžã³ã¯ ãããã³ã« ã ãã¡ã€ã³ ãããŒãã®3ç¹ã»ããã§åºå¥ããããããã¢ã㪠ãã¡ã€ã³ ãšã¯ã¯ãã¹ãªãªãžã³ã®é¢ä¿ã«ãªããŸãã åèãŸã§ã«CDKã®å Žåã®ã³ãŒããµã³ãã«ãæ²èŒããŸããïŒèšŒææžã¯CloudFrontã§å©çšãããããus-east-1 ãªãŒãžã§ã³ã«ãããã€ããŸãïŒ const hostedZone = route53.PublicHostedZone.fromHostedZoneAttributes ( this , "MyHostedZone" , { hostedZoneId: "<ãã¹ããŸãŒã³ID>" , zoneName: "my-domain.com" , } ); const cloudfrontCertificate = new certificatemanager.DnsValidatedCertificate ( this , "CloudFrontCertificate" , { domainName: "static.my-domain.com" , hostedZone: hostedZone , validation: certificatemanager.CertificateValidation.fromDns ( hostedZone ), } ); Webã¢ããªã§å€éšãã ã³ãŒãã¹ãããã ãååŸãã ã¢ããªã±ãŒã·ã§ã³ã¢ãã¿ãŒã® ã³ãŒãã¹ãããã ãçŽæ¥ã³ãŒãã«å«ããã®ã§ã¯ãªãã以äžã®åœ¢ã§ src ãšããŠèªã¿èŸŒãã§ãããããšãèããŸãã < script src = "https://static.my-domain.com/rum.js" ></ script > <script> ã¿ã°ã® src ã§å€éšãããã¡ã€ã«ãååŸããå ŽåãGETã«ããåçŽãªã¯ ãšã¹ ãã«ãªããããCORSã® ããªãã©ã€ããªã¯ãšã¹ã ã¯çºçããŸãããããªãã¡OPTIONSãªã¯ ãšã¹ ãã¯éä¿¡ãããã443ããŒãã§ã8443ããŒãã§ãGETãªã¯ ãšã¹ ãã§ãªãœãŒã¹ã¯ååŸãããå®è¡ãããŸãã ããã§ crossorigin屿§ ãå©çšããŸãããããæå®ããããšã«ãã£ãŠ ãªã¯ãšã¹ãã¢ãŒã ã cors ãšãªããã¯ãã¹ãªãªãžã³ç°å¢äžã§ã¯ãµã€ãã® Origin ã ã¹ã¯ãªãã ãååŸããéã® Access-Control-Allow-Origin ã¬ã¹ãã³ã¹ããããŒã«å«ãŸããªãéããããŒããã ã¹ã¯ãªãã ããã©ãŠã¶ã§å®è¡ãããªããªããŸããïŒcrossorigin屿§ãä»ããªãå Žåã <script> ã¿ã°ã®ãªã¯ ãšã¹ ãã¢ãŒã㯠no-cors ãšãªãããµã€ãã® Origin ã«é¢ãããããŒããã ã¹ã¯ãªãã ããã©ãŠã¶ã§å®è¡ãããŸãïŒ ïŒåèïŒ https://nhiroki.jp/2021/01/07/crossorigin-attribute 以äžã®ããã«ãWebã¢ããªã® <head> ã¿ã°å
ã§ã¢ããªã±ãŒã·ã§ã³ã¢ãã¿ãŒã® ã³ãŒãã¹ãããã ãèªã¿èŸŒãããã«ããcrossorigin屿§ãèšå®ããŸãïŒ rum.js ãã¡ã€ã«ã¯ã®ã¡ã«S3 ãã±ãã ãäœæãããšãã«è¿œå ããŸãïŒã < head > < script src = "https://static.my-domain.com/rum.js" crossorigin = "anonymous" ></ script > </ head > ãŸãcrossorigin屿§ãä»ããããšã«ããã ã¹ã¯ãªãã ãååŸããéã®GETãªã¯ ãšã¹ ãã« Origin ããããŒãä»äžãããããã«ãªããŸããããã¯æ¬¡ã«è¿°ã¹ãS3 ãã±ãã ããã®ã¬ã¹ãã³ã¹ããããŒã«ã圱é¿ããŸãã å³ã«ãŸãšãããšãcrossorigin屿§ã䜿çšããªãå Žåãæ¬çªç°å¢ã§ããã¹ãç°å¢ã§ã ã¹ã¯ãªãã ãå®è¡ãããŠããŸããŸãã crossorigin屿§ã䜿çšããå Žåã¯æ¬¡ã®ããã«ãªããæ¬çªç°å¢ã®ã¿ ã¹ã¯ãªãã ãå®è¡ãããŸãã ã³ãŒãã¹ãããã æ ŒçŽçšã®S3 ãã±ãã ãäœæ S3 ãã±ãã ãäœæããæ¬çªç°å¢ã®ã¿ãèš±å¯ããCORSãèšå®ããŸãã const myBucket = new s3.Bucket ( this , "MyBucket" , { encryption: s3.BucketEncryption.S3_MANAGED , blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL , objectOwnership: s3.ObjectOwnership.BUCKET_OWNER_ENFORCED , enforceSSL: true , cors: [ { allowedHeaders: [ "*" ] , allowedMethods: [ s3.HttpMethods.GET ] , allowedOrigins: [ "https://my-domain.com" ] , } , ] , } ); ãã®èšå®ãããå Žåã®åãã確èªããŠã¿ãŸããããªã¯ ãšã¹ ãããããŒã« Origin: https://my-domain.com ãå«ãŸããŠããå Žåã以äžã®ã¬ã¹ãã³ã¹ããããŒãä»äžãããŸããã Access-Control-Allow-Credentials: true Access-Control-Allow-Methods: GET Access-Control-Allow-Origin: https://my-domain.com ãªã¯ ãšã¹ ãããããŒã Origin: https://my-domain.com:8443 ãšãªã£ãŠããå Žåã以äžã®3ã€ã® Access-Control-* ã¬ã¹ãã³ã¹ããããŒã¯ä»äžãããŠããŸããã§ããã S3 ãã±ãã ã®ã¢ã¯ã»ã¹å¶åŸ¡ CORSã®èšå®ãšã¯é¢ä¿ãããŸããããCloudFrontã® OACïŒãªãªãžã³ã¢ã¯ã»ã¹ã³ã³ãããŒã«ïŒ ãå©çšããS3 ãã±ãã ãžã®ã¢ã¯ã»ã¹ã次ã®ã¹ãããã§äœæããCloudFront ãã£ã¹ããªãã¥ãŒã·ã§ã³ ããã®ã¿ã«å¶éããŸãã å·çæç¹ã§CDKã®L2ã³ã³ã¹ã ã©ã¯ ãã§ã¯ãŸã OACããµããŒããããŠããªãããã以äžã¯OACã§ã¯ãªãOAIãå©çšããå Žåã®ã³ãŒããµã³ãã«ã§ããL2ã³ã³ã¹ã ã©ã¯ ãã§OACããµããŒããããããããå©çšããã®ãè¯ãã§ãããã const oai = new cloudfront.OriginAccessIdentity ( this , "MyOAI" ); myBucket.addToResourcePolicy ( new iam.PolicyStatement ( { effect: iam.Effect.ALLOW , actions: [ "s3:GetObject" ] , principals: [ new iam.CanonicalUserPrincipal ( oai.cloudFrontOriginAccessIdentityS3CanonicalUserId ) ] , resources: [ ` ${ myBucket.bucketArn } /*` ] , } ) ); S3 ãã±ãã ã« ã³ãŒãã¹ãããã ããã¡ã€ã«ã§è¿œå ãã ã¢ããªã±ãŒã·ã§ã³ã¢ãã¿ãŒã® ã³ãŒãã¹ãããã ã®ãã¡ã <script> ã¿ã°ãé€å€ããéšåãã³ããŒãã JavaScript ãã¡ã€ã«ãäœæããŸããä»å㯠rum.js ãšãããã¡ã€ã«åãšããŠS3 ãã±ãã ã«ã¢ããããŒãããŸãã CloudFront ãã£ã¹ããªãã¥ãŒã·ã§ã³ ã®äœæ CloudFront ãã£ã¹ããªãã¥ãŒã·ã§ã³ ãäœæããŸããããã§éèŠãªã®ã¯ 3ã€ã®ããªã·ãŒ ã§ãã ãŸãã¯ãã£ãã·ã¥ããªã·ãŒãšããŠã Origin ããããŒããã£ãã·ã¥ããŒã«å«ããããã«ããŸããããªãã¡ãªã¯ ãšã¹ ãã® Origin ããããŒãç°ãªãå€ã®å Žåã¯ãç°ãªãã³ã³ãã³ããèŠæ±ããŠãããšã¿ãªããæ¬çªç°å¢ãšãã¹ãç°å¢ã§ã®æ¯ãèããåãæ¿ããŸãã const cachePolicy = new cloudfront.CachePolicy ( this , "MyCachePolicy" , { defaultTtl: cdk.Duration.days ( 1 ), maxTtl: cdk.Duration.days ( 1 ), minTtl: cdk.Duration.days ( 1 ), headerBehavior: cloudfront.CacheHeaderBehavior.allowList ( "Origin" ), } ); 次ã«ãªãªãžã³ãªã¯ ãšã¹ ãããªã·ãŒãšããŠãCloudFrontãããªãªãžã³ãžã®ãªã¯ ãšã¹ ãã« Origin ããããŒãå«ããŠè»¢éããããã«ããŸããããã«ãããS3 ãã±ãã ã§èšå®ããCORSãæ©èœããããã«ãªããŸãã const originRequestPolicy = new cloudfront.OriginRequestPolicy ( this , "MyOriginRequestPolicy" , { headerBehavior: cloudfront.CacheHeaderBehavior.allowList ( "Origin" ), } ); æåŸã«ã¬ã¹ãã³ã¹ããããŒããªã·ãŒãšããŠãCloudFrontããã¬ã¹ãã³ã¹ãè¿ããšãã«CORSããããŒãå«ããããã«ããŸãã const responseHeadersPolicy = new cloudfront.ResponseHeadersPolicy ( this , "MyResponseHeadersPolicy" , { corsBehavior: { accessControlAllowCredentials: false , accessControlAllowHeaders: [ "*" ] , accessControlAllowMethods: [ "GET" ] , accessControlAllowOrigins: [ "https://my-domain.com" ] , originOverride: false , } , } ); 以äžã®ããªã·ãŒãå©çšããŠCloudFront ãã£ã¹ããªãã¥ãŒã·ã§ã³ ãäœæããŸããä»åã®æ§æã§ã¯OPTIONSã¡ãœããã¯éä¿¡ãããªããããèš±å¯ããã¡ãœããã«OPTIONSã¯å«ããŠããŸããã const distribution = new cloudfront.Distribution ( this , "MyDistribution" , { defaultBehavior: { allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD , cachedMethods: cloudfront.CachedMethods.CACHE_GET_HEAD , cachePolicy , originRequestPolicy , responseHeadersPolicy , viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS , origin: new cloudfrontOrigins.S3Origin ( myBucket ), } , priceClass: cloudfront.PriceClass.PRICE_CLASS_200 , geoRestriction: cloudfront.GeoRestriction.allowlist ( "JP" ), sslSupportMethod: cloudfront.SSLMethod.SNI , minimumProtocolVersion: cloudfront.SecurityPolicyProtocol.TLS_V1_2_2021 , certificate: cloudfrontCertificate , domainNames: [ "static.my-domain.com" ] , } ); ãã¡ã€ã³ ã®ã«ãŒãã£ã³ã° äœæããCloudFront ãã£ã¹ããªãã¥ãŒã·ã§ã³ ã«ã static.my-domain.com ã®ã«ãŒãã£ã³ã°ãåããŠå®äºã§ãã new route53.ARecord ( this , "StaticARecord" , { zone: hostedZone , recordName: "static" , target: route53.RecordTarget.fromAlias (new route53Targets.CloudFrontTarget ( distribution )), } ); åãã®ç¢ºèª æ¬çªç°å¢ã® https://my-domain.com ã«ã¢ã¯ã»ã¹ãããšã https://static.my-domain.com/rum.js ã®ååŸã«æåããŠããããšã確èªã§ããŸãããå®éã«ã¯ããã« https://client.rum.us-east-1.amazonaws.com/1.5.x/cwr.js ãã ã¹ã¯ãªãã ã®æ¬äœãããŒãããŠããã https://dataplane.rum.ap-notheast-1.amazonaws.com/appmonitors/ ã«ãã©ãŠã¶ã®ã¯ã©ã€ã¢ã³ãããŒã¿ãéä¿¡ããŠããŸããããããžã¡ã³ãã³ã³ãœãŒã«ã®CloudWatch RUMã®ç»é¢ã«ã¢ã¯ã»ã¹ãããšãããŒã¿ãååŸãããŠããããšãããããŸãã äžæ¹ããã¹ãç°å¢ã® https://my-domain.com:8443 ã«ã¢ã¯ã»ã¹ãããšã https://static.my-domain.com/rum.js ããã®ã¬ã¹ãã³ã¹ã¹ããŒã¿ã¹ã¯200ã§è¿ããŸãããã¯ãã¹ãªãªãžã³ã®èªã¿èŸŒã¿ãèš±å¯ãããŠããªããããã©ãŠã¶ã¯ ã¹ã¯ãªãã ã«ã¢ã¯ã»ã¹ã§ãããå®è¡ãããŸããã ããã§ECSã®ãã«ãŒã°ãªãŒã³ãããã€ã¡ã³ãã®æ¬çªç°å¢ã®ã¿ãCloudWatch RUMã«ããŒã¿éä¿¡ãå®çŸã§ããŸããã ç§ãã¡ã¯åãããŒã ã§åããŠããã仲éã倧åéããŠããŸãïŒããããã®ãå¿åããåŸ
ã¡ããŠããŸãã - ã»ãã¥ãªãã£ãšã³ãžãã¢(ã»ãã¥ãªãã£èšèš) å·çïŒ @kou.kinyo ãã¬ãã¥ãŒïŒ @yamada.y ïŒ Shodo ã§å·çãããŸãã ïŒ