Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.
Constructos de la capa 3
Si las construcciones L1 traducen literalmente los CloudFormation recursos a código programático y las construcciones L2 sustituyen gran parte de la CloudFormation sintaxis detallada por métodos auxiliares y lógica personalizada, ¿qué hacen las construcciones L3? Solo su imaginación limita la respuesta a esa pregunta. Puede crear la capa 3 para satisfacer cualquier caso de uso concreto. Si su proyecto necesita un recurso que tenga un subconjunto específico de propiedades, puede crear un constructo de la capa 3 reutilizable para satisfacer esa necesidad.
Los constructos de la capa 3 se denominan patrones en AWS CDK. Un patrón es cualquier objeto que extiende la Construct clase AWS CDK (o extiende una clase que extiende la Construct clase) para realizar cualquier lógica abstracta más allá de la capa 2. Cuando usa la AWS CDK CLI para ejecutar cdk init e iniciar un nuevo AWS CDK proyecto, debe elegir entre tres tipos de AWS CDK aplicaciones: applib, y. sample-app
appy sample-app ambas representan AWS CDK aplicaciones clásicas en las que se crean e implementan CloudFormation pilas en entornos de AWS. Si eligeslib, eliges construir una estructura L3 completamente nueva. appy te sample-app permiten elegir cualquier idioma que admitan, pero solo puedes elegir TypeScript con lib él. AWS CDK Esto se debe a que AWS CDK está escrito de forma nativa TypeScript y utiliza un sistema de código abierto llamado JsIIlib para iniciar el proyecto, elige crear una ampliación para AWS CDK.
Cualquier clase que amplíe la clase Construct puede ser un constructo de la capa 3, pero los casos de uso más comunes de la capa 3 son las interacciones de recursos, las ampliaciones de recursos y los recursos personalizados. La mayoría de los constructos de la capa 3 utilizan uno o varios de estos tres casos para ampliar la funcionalidad de AWS CDK
.
Interacciones de los recursos
Por lo general, una solución utiliza varios servicios de AWS que funcionan juntos. Por ejemplo, una CloudFront distribución de Amazon suele utilizar un bucket de S3 como origen y AWS WAF como protección contra las vulnerabilidades más comunes. AWS AppSync y Amazon API Gateway suelen utilizar tablas de Amazon DynamoDB como fuentes de datos para sus API. Una canalización AWS CodePipeline suele utilizar Amazon S3 como fuente y AWS CodeBuild para sus etapas de creación. En estos casos, suele ser útil crear un constructo único de la capa 3 que se encargue del aprovisionamiento de dos o varios constructos de la capa 2 interconectadas.
A continuación, se muestra un ejemplo de una construcción L3 que aprovisiona una CloudFront distribución junto con su origen S3, un AWS WAF registro de Amazon Route 53 y un certificado AWS Certificate Manager (ACM) para añadir un punto final personalizado con cifrado en tránsito, todo en una construcción reutilizable:
// 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, }); } }
Tenga en cuenta que CloudFront Amazon S3, Route 53 y ACM utilizan construcciones L2, pero la ACL web (que define las reglas para gestionar las solicitudes web) usa una construcción L1. Esto se debe a que AWS CDK se trata de un paquete de código abierto en evolución que no está completamente completo y aún no existe una construcción L2. WebAcl Sin embargo, cualquiera puede contribuir a ello AWS CDK creando nuevas construcciones de L2. Por lo tanto, hasta que no AWS CDK ofrezca una construcción L2WebAcl, tendrá que usar una construcción L1. Para crear un nuevo sitio web mediante el constructo de la capa 3 CloudFrontWebsite, utilice el código siguiente:
const siteADotCom = new CloudFrontWebsite(stack, "siteA", siteAProps); const siteBDotCom = new CloudFrontWebsite(stack, "siteB", siteBProps); const siteCDotCom = new CloudFrontWebsite(stack, "siteC", siteCProps);
En este ejemplo, la construcción CloudFront Distribution L2 se expone como una propiedad pública de la construcción L3. Seguirán existiendo casos en los que deba exponer propiedades de la capa 3 como esta, según sea necesario. De hecho, volveremos a ver Distribution más adelante, en la sección Recursos personalizados.
AWS CDK Incluye algunos ejemplos de patrones de interacción de recursos como este. Además del paquete de aws-ecs que contiene los constructos de la capa 2 para Amazon Elastic Container Service (Amazon ECS), AWS CDK tiene un paquete llamado aws-ecs-patterns. Este paquete contiene varios constructos de la capa 3 que combinan Amazon ECS con equilibradores de carga de aplicaciones, equilibradores de carga de red y grupos de destinos, además de que ofrece distintas versiones predefinidas para Amazon Elastic Compute Cloud (Amazon EC2) y AWS Fargate. Dado que muchas aplicaciones sin servidor utilizan Amazon ECS solo con Fargate, estos constructos de la capa 3 ofrecen una comodidad que permite a los desarrolladores ahorrar tiempo y dinero a los clientes.
Extensiones de recursos
Algunos casos de uso requieren que los recursos tengan una configuración predeterminada específica que no sea nativa del constructo de la capa 2. Para las pilas, esto se puede gestionar mediante los aspectos, pero otra manera práctica de dotar a un constructo nuevo de la capa 2 de nuevos valores predeterminados consiste en ampliar la capa 2. Como un constructo es cualquier clase que hereda la clase Construct y los constructos de la capa 2 amplían esa clase, también puede crear un constructo de la capa 3 mediante la ampliación directa de un constructo de la capa 2.
Esto puede resultar muy útil para la lógica empresarial personalizada que respalde las necesidades concretas de un cliente. Supongamos que una empresa tiene un repositorio que almacena todo su código de AWS Lambda función en un único directorio llamado src/lambda y que la mayoría de las funciones de Lambda reutilizan el mismo tiempo de ejecución y el mismo nombre de controlador cada vez. En lugar de configurar la ruta del código cada vez que configure una nueva función de Lambda, podría crear un constructo nuevo de la capa 3:
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 }); }
A continuación, puede reemplazar el constructo de la capa 2 Function en todas partes del repositorio de la manera siguiente:
new MyCompanyLambdaFunction(this, "MyFunction"); new MyCompanyLambdaFunction(this, "MyOtherFunction"); new MyCompanyLambdaFunction(this, "MyThirdFunction", { runtime: Runtime.PYTHON_3_11 });
Los valores predeterminados le permiten crear nuevas funciones de Lambda en una sola línea, y el constructo de la capa 3 está configurada para que pueda anular las propiedades predeterminadas si es necesario.
La extensión directa de los constructos de la capa 2 funciona mejor cuando solo se desean agregar nuevos valores predeterminados a los constructos de la capa 2 existentes. Si también necesita otra lógica personalizada, es mejor ampliar la clase Construct. El motivo de esto se debe al método super, al que se llama desde el constructor. En las clases que amplían otras clases, el método super se utiliza para llamar al constructor de la clase principal. Esto debe ser lo primero que suceda en el constructor. Esto significa que las manipulaciones de los argumentos pasados u otra lógica personalizada solo puede producirse después de que se haya creado el constructo de la capa 2 original. Si necesita hacer alguna de estas lógicas personalizadas antes de crear una instancia del constructo de la capa 2, es mejor seguir el patrón descrito anteriormente en la sección Interacciones entre recursos.
Recursos personalizados
Los recursos personalizados son una potente función CloudFormation que permite ejecutar una lógica personalizada desde una función de Lambda que se activa durante el despliegue de la pila. Siempre que necesite algún proceso durante la implementación que no sea compatible directamente con él CloudFormation, puede utilizar un recurso personalizado para hacerlo realidad. AWS CDK ofrece clases que también le permiten crear recursos personalizados mediante programación. Al utilizar los recursos personalizados en un constructor de la capa 3, puede hacer un constructo a partir de casi cualquier cosa.
Una de las ventajas de usar Amazon CloudFront es su sólida capacidad de almacenamiento en caché global. Si quieres restablecer manualmente esa caché para que tu sitio web refleje inmediatamente los nuevos cambios realizados en tu origen, puedes utilizar una CloudFront invalidación. Sin embargo, las invalidaciones son procesos que se ejecutan en una CloudFront distribución en lugar de ser propiedades de una distribución. CloudFront Se pueden crear y aplicar a una distribución existente en cualquier momento, por lo que no forman parte de manera nativa del proceso de aprovisionamiento e implementación.
En este escenario, es posible que quiera crear y ejecutar una invalidación después de cada actualización del origen de una distribución. Debido a los recursos personalizados, puede crear un constructo de la capa 3 similar a la siguiente:
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') } } } } } }
Con la distribución que se creó anteriormente en el constructo CloudFrontWebsite de la capa 3, podría hacerlo de manera muy fácil:
new CloudFrontInvalidation(this, 'MyInvalidation', { distribution: siteADotCom.distribution });
Esta construcción de L3 utiliza una construcción de AWS CDK L3 llamada AwsCustomResourcepara crear un recurso personalizado que ejecute una lógica personalizada. AwsCustomResourcees muy práctico cuando necesita realizar exactamente una llamada al SDK de AWS, ya que le permite hacerlo sin tener que escribir ningún código Lambda. Si tiene requisitos más complejos y quiere implementar su propia lógica, puede utilizar directamente la CustomResourceclase básica.
Otro buen ejemplo del AWS CDK uso de una construcción L3 de recursos personalizada es el despliegue de cubos de S3. La función Lambda creada por el recurso personalizado dentro del constructor de esta construcción de L3 añade funcionalidades que CloudFormation no se podrían gestionar de otro modo: añade y actualiza objetos en un bucket de S3. Sin la implementación de un bucket de S3, no podría colocar contenido en el bucket que acaba de crear como parte de su pila, lo que sería muy inconveniente.
El mejor ejemplo de cómo se AWS CDK elimina la necesidad de escribir montones de CloudFormation sintaxis es el siguiente: S3BucketDeployment
new BucketDeployment(this, 'BucketObjects', { sources: [Source.asset('./path/to/amzn-s3-demo-bucket')], destinationBucket: amzn-s3-demo-bucket });
Compáralo con el CloudFormation código que tendrías que escribir para lograr lo mismo:
"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 líneas contra 241 es una diferencia enorme. Y esto es solo un ejemplo de lo que es posible cuando se aprovecha la capa 3 para personalizar las pilas.