翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。
レイヤー 3 コンストラクト
L1 コンストラクトでは CloudFormation リソースをプログラムコードにそのまま変換し、L2 コンストラクトでは冗長な CloudFormation 構文の多くをヘルパーメソッドやカスタムロジックに置換しました。では、L3 コンストラクトではどのような処理を行うのでしょうか。それは、ユーザーの想像力によって決まります。レイヤー 3 は、どのような特定のユースケースにも合うよう作成できるのです。例えば、プロジェクトで特定のプロパティサブセットを持つリソースが必要な場合は、再利用可能な L3 コンストラクトを作成して、そうしたニーズを満たすことができます。
AWS CDK内では、L3 コンストラクトをパターンと呼びます。パターンは、 のConstructクラスを拡張する AWS CDK (またはクラスを拡張するConstruct クラスを拡張する) オブジェクトで、レイヤー 2 を超えて抽象化されたロジックを実行します。 AWS CDK CLI を使用して cdk init を実行して新しい AWS CDK プロジェクトを開始する場合は、app、、 libの 3 つの AWS CDK アプリケーションタイプから選択する必要がありますsample-app。
app と sample-appはどちらもCloudFormation スタックを構築して AWS 環境にデプロイする従来の AWS CDK アプリケーションを表します。を選択するとlib、まったく新しい L3 コンストラクトの構築が選択されます。 appと sample-appでは、 が AWS CDK サポートする任意の言語を選択できますが、 では TypeScript しか選択できませんlib。これは、 AWS CDK が TypeScript でネイティブに記述され、JSiilib を選択してプロジェクトを開始します。
Construct クラスを継承するクラスはすべて L3 コンストラクトとして定義できますが、レイヤー 3 の最も一般的なユースケースは、リソースインタラクション、リソース拡張、カスタムリソースです。ほとんどの L3 コンストラクトでは、 AWS CDK
の機能を拡張するために、これら 3 つのケースの 1 つ以上を使用します。
リソースインタラクション
ソリューションでは、通常、連携して動作する複数の AWS サービスを利用します。例えば、Amazon CloudFront ディストリビューションでは、S3 バケットをオリジンとして使用し、一般的なエクスプロイトから保護 AWS WAF することがよくあります。 AWS AppSync Amazon API Gateway では、APIs のデータソースとして Amazon DynamoDB テーブルがよく使用されます。のパイプラインでは、Amazon S3 をソースとして使用し、ビルドステージ AWS CodeBuild に使用する AWS CodePipeline ことがよくあります。こうした場合に有用なのは、単一の L3 コンストラクトを作成し、これによって、相互接続した 2 つ以上の L2 コンストラクトのプロビジョニングを処理することです。
CloudFront ディストリビューションを SL3 オリジン、その前に配置 AWS WAF する 、Amazon Route 53 レコード、転送中の暗号化でカスタムエンドポイントを追加する AWS Certificate Manager (ACM) 証明書とともにプロビジョニングする L3 コンストラクトの例を次に示します。これらはすべて 1 つの再利用可能なコンストラクトです。 S3
// Define the properties passed to the L3 construct export interface CloudFrontWebsiteProps { distributionProps: DistributionProps bucketProps: BucketProps wafProps: CfnWebAclProps zone: IHostedZone } // Define the L3 construct export class CloudFrontWebsite extends Construct { public distribution: Distribution constructor( scope: Construct, id: string, props: CloudFrontWebsiteProps ) { super(scope, id); const certificate = new Certificate(this, "Certificate", { domainName: props.zone.zoneName, validation: CertificateValidation.fromDns(props.zone) }); const defaultBehavior = { origin: new S3Origin(new Bucket(this, "bucket", props.bucketProps)) } const waf = new CfnWebACL(this, "waf", props.wafProps); this.distribution = new Distribution(this, id, { ...props.distributionProps, defaultBehavior, certificate, domainNames: [this.domainName], webAclId: waf.attrArn, }); } }
CloudFront、Amazon S3、Route 53、ACM ではいずれも、L2 コンストラクトを使用していますが、Web ACL (Web リクエストの処理ルールを定義する) では L1 コンストラクトを使用していることに注意してください。これは、 AWS CDK は完全には完了していない進化するオープンソースパッケージであり、WebAclまだ L2 コンストラクトがないためです。ただし、新しい L2 コンストラクトを作成 AWS CDK することで、誰でも に貢献できます。したがって、 が の L2 コンストラクト AWS CDK を提供するまではWebAcl、L1 コンストラクトを使用する必要があります。CloudFrontWebsite L3 コンストラクトを使用して新しいウェブサイトを作成するには、次のコードを使用します。
const siteADotCom = new CloudFrontWebsite(stack, "siteA", siteAProps); const siteBDotCom = new CloudFrontWebsite(stack, "siteB", siteBProps); const siteCDotCom = new CloudFrontWebsite(stack, "siteC", siteCProps);
この例では、CloudFront Distribution の L2 コンストラクトを L3 コンストラクトのパブリックプロパティとして公開しています。このように、場合によっては、L3 プロパティの公開が必要なケースも依然として存在します。実際に、以降の「カスタムリソース」セクションでも、Distribution を再度取り上げます。
AWS CDK には、このようなリソースインタラクションパターンの例がいくつか含まれています。例えば、Amazon Elastic Container Service (Amazon ECS) の L2 コンストラクトが含まれる aws-ecs パッケージのほか、 AWS CDK には、aws-ecs-patterns というパッケージもあります。このパッケージ内には、Amazon ECS を Application Load Balancer、Network Load Balancer、ターゲットグループと組み合わせる複数の L3 コンストラクトに加え、Amazon Elastic Compute Cloud (Amazon EC2) と AWS Fargate向けにプリセットされた異なるバージョンが用意されています。多くのサーバーレスアプリケーションでは Amazon ECS を Fargate とのみ組み合わせて使用するため、こうした L3 コンストラクトを使用すると、開発側の時間と顧客側のコストをともに節約できます。
リソース拡張
一部のユースケースのリソースには、L2 コンストラクトでネイティブではない特定のデフォルト設定が必要です。スタックレベルであれば、アスペクトを使用してこれに対応できますが、L2 コンストラクトに新しいデフォルトを設定するもう 1 つの便利な方法は、レイヤー 2 を継承することです。どのコンストラクトも Construct クラスを継承したコンストラクトであり、L2 コンストラクトはそのクラスを継承するため、L2 コンストラクトを直接継承することでも L3 コンストラクトを作成できます。
これが特に有用なのは、顧客の特定のニーズに対応したカスタムビジネスロジックを実行するときです。会社には、 という名前の単一のディレクトリにすべての AWS Lambda 関数コードを保存src/lambdaし、ほとんどの Lambda 関数が毎回同じランタイムとハンドラー名を再利用するリポジトリがあるとします。その場合は、Lambda 関数の新規設定のたびにコードパスを設定せず、新しい L3 コンストラクトを作成すると良いでしょう。
export class MyCompanyLambdaFunction extends Function { constructor( scope: Construct, id: string, props: Partial<FunctionProps> = {} ) { super(scope, id, { handler: 'index.handler', runtime: Runtime.NODEJS_LATEST, code: Code.fromAsset(`src/lambda/${props.functionName || id}`), ...props }); }
そうすることにより、リポジトリ内の任意の場所で L2 Function のコンストラクトを次のように置き換えることができます。
new MyCompanyLambdaFunction(this, "MyFunction"); new MyCompanyLambdaFunction(this, "MyOtherFunction"); new MyCompanyLambdaFunction(this, "MyThirdFunction", { runtime: Runtime.PYTHON_3_11 });
デフォルト設定を使用すると、新しい Lambda 関数を 1 行で作成でき、必要に応じてデフォルトのプロパティをオーバーライドできるように L3 コンストラクトを設定することも可能です。
既存の L2 コンストラクトに新しいデフォルト設定を追加するだけの場合に最良なのは、L2 コンストラクトを直接継承することです。他のカスタムロジックも必要な場合は、Construct クラスを継承すると良いでしょう。その理由は、コンストラクタ内で呼び出す super メソッドにあります。他のクラスを継承したクラスでは、super メソッドを使用して親クラスのコンストラクタを呼び出します。これは、コンストラクタ内で最初に実行する必要があります。つまり、渡す引数やその他のカスタムロジックの操作は、元の L2 コンストラクトが作成された後にのみ実行できます。L2 コンストラクトのインスタンス化前にこうしたカスタムロジックを実行する必要がある場合は、「リソースインタラクション」セクションで前述したパターンに従うことをお勧めします。
カスタムリソース
カスタムリソースは、CloudFormation の強力な機能であり、これによって、スタックのデプロイ中にアクティブ化した Lambda 関数からカスタムロジックを実行できます。CloudFormation で直接サポートされていないプロセスがデプロイ中に必要な場合は、カスタムリソースを使用して処理すると良いでしょう。 AWS CDK には、カスタムリソースをプログラムで作成可能なクラスも用意されています。L3 コンストラクタ内でカスタムリソースを使用すると、ほぼあらゆるリソースからコンストラクトを作成できます。
Amazon CloudFront を使用する利点の 1 つは、強力なグローバルキャッシュ機能です。キャッシュを手動でリセットしてオリジンへの新しい変更をウェブサイトにすぐに反映したい場合は、CloudFront の無効化を利用できます。ただし、無効化は、CloudFront ディストリビューション上で実行されるプロセスであり、CloudFront ディストリビューションのプロパティではありません。既存のディストリビューションにいつでも作成し適用できるため、プロビジョニングおよびデプロイプロセスに、ネイティブには組み込まれていません。
このシナリオでは、ディストリビューションのオリジンを更新するたびに無効化を作成し実行するという要件が考えられます。カスタムリソースを使用すると、次のような L3 コンストラクトを作成できます。
export interface CloudFrontInvalidationProps { distribution: Distribution region?: string paths?: string[] } export class CloudFrontInvalidation extends Construct { constructor( scope: Construct, id: string, props: CloudFrontInvalidationProps ) { super(scope, id); const policy = AwsCustomResourcePolicy.fromSdkCalls({ resources:AwsCustomResourcePolicy.ANY_RESOURCE }); new AwsCustomResource(scope, `${id}Invalidation`, { policy, onUpdate: { service: 'CloudFront', action: 'createInvalidation', region: props.region || 'us-east-1', physicalResourceId: PhysicalResourceId.fromResponse('Invalidation.Id'), parameters: { DistributionId: props.distribution.distributionId, InvalidationBatch: { Paths: { Quantity: props.paths?.length || 1, Items: props.paths || ['/*'] }, CallerReference: crypto.randomBytes(5).toString('hex') } } } } } }
先ほど CloudFrontWebsite L3 コンストラクトで作成したディストリビューションを使用すると、これを非常に簡単に実行できます。
new CloudFrontInvalidation(this, 'MyInvalidation', { distribution: siteADotCom.distribution });
この L3 コンストラクトは、AwsCustomResource と呼ばれる AWS CDK L3 コンストラクトを使用して、カスタムロジックを実行するカスタムリソースを作成します。 AwsCustomResourceは、Lambda コードを記述しなくても 1 つの AWS SDK 呼び出しを正確に行う必要がある場合に非常に便利です。より複雑な要件があり、独自ロジックの実装が必要な場合は、基本的な CustomResource クラスを直接使用すると良いでしょう。
カスタムリソース L3 コンストラクト AWS CDK を使用する のもう 1 つの良い例は、S3 バケットのデプロイです。カスタムリソースによってこの L3 コンストラクトのコンストラクタ内に作成された Lambda 関数は、CloudFormation では処理できない機能、つまり S3 バケットにオブジェクトを追加し更新する機能を備えるようになります。S3 バケットのデプロイ機能がないと、スタックの一部として作成した S3 バケットにコンテンツを追加できないため、非常に不便です。
CloudFormation 構文のリームを書き出す必要がなく AWS CDK なるのに最適な例は、次の基本的な ですS3BucketDeployment。
new BucketDeployment(this, 'BucketObjects', { sources: [Source.asset('./path/to/amzn-s3-demo-bucket')], destinationBucket: amzn-s3-demo-bucket });
これと同じ目的を達成するために、CloudFormation ではどれくらいの量のコードが必要かを比較してみましょう。
"lambdapolicyA5E98E09": { "Type": "AWS::IAM::Policy", "Properties": { "PolicyDocument": { "Statement": [ { "Action": "lambda:UpdateFunctionCode", "Effect": "Allow", "Resource": "arn:aws:lambda:us-east-1:123456789012:function:my-function" } ], "Version": "2012-10-17" }, "PolicyName": "lambdaPolicy", "Roles": [ { "Ref": "myiamroleF09C7974" } ] }, "Metadata": { "aws:cdk:path": "CdkScratchStack/lambda-policy/Resource" } }, "BucketObjectsAwsCliLayer8C081206": { "Type": "AWS::Lambda::LayerVersion", "Properties": { "Content": { "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, "S3Key": "e2277687077a2abf9ae1af1cc9565e6715e2ebb62f79ec53aa75a1af9298f642.zip" }, "Description": "/opt/awscli/aws" }, "Metadata": { "aws:cdk:path": "CdkScratchStack/BucketObjects/AwsCliLayer/Resource", "aws:asset:path": "asset.e2277687077a2abf9ae1af1cc9565e6715e2ebb62f79ec53aa75a1af9298f642.zip", "aws:asset:is-bundled": false, "aws:asset:property": "Content" } }, "BucketObjectsCustomResourceB12E6837": { "Type": "Custom::CDKBucketDeployment", "Properties": { "ServiceToken": { "Fn::GetAtt": [ "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536", "Arn" ] }, "SourceBucketNames": [ { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" } ], "SourceObjectKeys": [ "f888a9d977f0b5bdbc04a1f8f07520ede6e00d4051b9a6a250860a1700924f26.zip" ], "DestinationBucketName": { "Ref": "amzn-s3-demo-bucket77F80CC0" }, "Prune": true }, "UpdateReplacePolicy": "Delete", "DeletionPolicy": "Delete", "Metadata": { "aws:cdk:path": "CdkScratchStack/BucketObjects/CustomResource/Default" } }, "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { "Statement": [ { "Action": "sts:AssumeRole", "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" } } ], "Version": "2012-10-17" }, "ManagedPolicyArns": [ { "Fn::Join": [ "", [ "arn:", { "Ref": "AWS::Partition" }, ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" ] ] } ] }, "Metadata": { "aws:cdk:path": "CdkScratchStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/Resource" } }, "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF": { "Type": "AWS::IAM::Policy", "Properties": { "PolicyDocument": { "Statement": [ { "Action": [ "s3:GetBucket*", "s3:GetObject*", "s3:List*" ], "Effect": "Allow", "Resource": [ { "Fn::Join": [ "", [ "arn:", { "Ref": "AWS::Partition" }, ":s3:::", { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, "/*" ] ] }, { "Fn::Join": [ "", [ "arn:", { "Ref": "AWS::Partition" }, ":s3:::", { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" } ] ] } ] }, { "Action": [ "s3:Abort*", "s3:DeleteObject*", "s3:GetBucket*", "s3:GetObject*", "s3:List*", "s3:PutObject", "s3:PutObjectLegalHold", "s3:PutObjectRetention", "s3:PutObjectTagging", "s3:PutObjectVersionTagging" ], "Effect": "Allow", "Resource": [ { "Fn::GetAtt": [ "amzns3demobucket77F80CC0", "Arn" ] }, { "Fn::Join": [ "", [ { "Fn::GetAtt": [ "amzns3demobucket77F80CC0", "Arn" ] }, "/*" ] ] } ] } ], "Version": "2012-10-17" }, "PolicyName": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF", "Roles": [ { "Ref": "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265" } ] }, "Metadata": { "aws:cdk:path": "CdkScratchStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/ServiceRole/DefaultPolicy/Resource" } }, "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C81C01536": { "Type": "AWS::Lambda::Function", "Properties": { "Code": { "S3Bucket": { "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}" }, "S3Key": "9eb41a5505d37607ac419321497a4f8c21cf0ee1f9b4a6b29aa04301aea5c7fd.zip" }, "Role": { "Fn::GetAtt": [ "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265", "Arn" ] }, "Environment": { "Variables": { "AWS_CA_BUNDLE": "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" } }, "Handler": "index.handler", "Layers": [ { "Ref": "BucketObjectsAwsCliLayer8C081206" } ], "Runtime": "python3.9", "Timeout": 900 }, "DependsOn": [ "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRoleDefaultPolicy88902FDF", "CustomCDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756CServiceRole89A01265" ], "Metadata": { "aws:cdk:path": "CdkScratchStack/Custom::CDKBucketDeployment8693BB64968944B69AAFB0CC9EB8756C/Resource", "aws:asset:path": "asset.9eb41a5505d37607ac419321497a4f8c21cf0ee1f9b4a6b29aa04301aea5c7fd", "aws:asset:is-bundled": false, "aws:asset:property": "Code" } }
4 行に対し 241 行とは、かなりの差です。しかし、これは、レイヤー 3 を活用してスタックをカスタマイズする場合の可能性を示す一例にすぎません。