Implementación de infraestructura en la nube utilizando AWS CDK

Álvaro Mongil
Computación en la nube
Al definir Infraestructura como Código (IaC) en AWS, escribir plantillas YAML

1. Introducción

Al definir Infraestructura como Código (IaC) en AWS, escribir plantillas YAML CloudFormation es probablemente la opción más común entre las Empresas. La razón principal es que CloudFormation es un servicio de primera clase en el ecosistema de AWS. Está completamente integrado con todos los demás servicios de la plataforma y sigue recibiendo actualizaciones y mejoras con el tiempo.

Pero YAML no es un lenguaje de programación y el método habitual de colaboración y uso compartido de plantillas es copiar y pegar las plantillas existentes y adaptarlas para que se ajusten a sus necesidades actuales. Esto hace que el proceso de describir la infraestructura para un nuevo proyecto o incluso actualizar uno existente sea tedioso y muy propenso a errores humanos. No ha existido una herramienta oficial para describir e implementar pilas de CloudFormation y recursos de AWS mediante programación, y la comunidad ha recurrido a herramientas de terceros como la troposfera para obtener beneficios de un lenguaje de alto nivel como Python, pero viene con su propio problemas, como estar atrasado en los servicios compatibles con CloudFormation, actualizaciones y alguna inconsistencia en la asignación de nombres de parámetros.

Esa era la situación hasta ahora, que AWS lanza su CDKo Kit de desarrollo en la nube.

AWS CDK le brinda el poder expresivo de los lenguajes de programación para definir la infraestructura. Características familiares como objetos, bucles y condiciones aceleran su proceso de desarrollo. También puede usar AWS CDK con su entorno de desarrollo integrado (IDE) para aprovechar las herramientas de productividad existentes y los marcos de prueba.

A partir de agosto de 2019, los idiomas admitidos son TypeScript, Python, Java (vista previa del desarrollador) y .NET (vista previa del desarrollador).

Este taller se basará en las bibliotecas TypeScript.

Este taller tiene como objetivo mostrar algunas de las características que hacen que el CDK sea único (como construcciones y aspectos) y demostrar cómo pueden resolver algunos desafíos comunes, como el cumplimiento corporativo, la colaboración en el desarrollo, la reutilización de plantillas, las pruebas y el CI automatizado. / Procesos de CD.

2. Requisitos previos

3. Conceptos de CDK

Construcciones

Las construcciones son los bloques de construcción básicos de las aplicaciones de AWS CDK. Una construcción representa un "componente de la nube" y encapsula todo lo que AWS CloudFormation necesita para crear el componente. Una construcción puede representar un único recurso, como un depósito de Amazon Simple Storage Service (Amazon S3), o puede representar un componente de nivel superior que consta de múltiples recursos AWS CDK. 1

El CDK viene incluido con una biblioteca de Construcciones, que proporciona recursos de Cloud Formation de bajo nivel y también Construcciones de alto nivel basadas en estos, pero que incluyen ciertas funcionalidades fijas (por ejemplo, un depósito S3 encriptado por defecto o una política de ciclo de vida aplicada).

Las clases de construcción amplían otras construcciones.

Aspectos

Los aspectos son la forma de aplicar una operación a todas las construcciones en un ámbito dado. La funcionalidad podría modificar las construcciones, como agregar etiquetas, o podría estar verificando algo sobre el estado de las construcciones, como garantizar que todos los depósitos estén encriptados. 2

Clases de construcción implementan Aspectos.

4. Importación y despliegue de construcciones de CDK existentes

Vamos a utilizar la CLI de CDK para crear nuestra primera aplicación de CDK. Consistirá en una construcción de cubo S3 proporcionada por la biblioteca de construcción.

  
mkdir my-first-cdk-app && cd my-first-cdk-app
cdk init --language typescript
 

Verifique los archivos creados en las bin y lib carpetas. Tienen la aplicación y los recursos de la pila.

A continuación, verifiquemos que nuestras herramientas funcionan correctamente creando y sintetizando nuestro código en una plantilla de CloudFormation.

  
npm run-script build
cdk synth
 

Debería ver una pila vacía con solo información de metadatos.

Ahora agreguemos nuestro recurso, un cubo S3.

Primero, necesitamos instalar la biblioteca S3 usando npm ...

  
npm install @aws-cdk/aws-s3
 

Agregue el siguiente fragmento al archivo Stack en la carpeta lib, que contendrá nuestros recursos.

  
// The code that defines your stack goes here
const bucket = new s3.Bucket(this, 'MyBucket');
 

¡No olvides importar la biblioteca S3!

Echemos un vistazo a la plantilla de CloudFormation nuevamente:

  
npm run-script build
cdk synth
 

Debería ver el recurso de cubo S3 agregado a su plantilla de CloudFormation.

Hemos mencionado anteriormente cómo podemos tener construcciones de orden superior que enmascaren los recursos subyacentes de CloudFormation (como el recurso de cubo S3 que acabamos de crear) o cómo podríamos acceder directamente a los recursos de CloudFormation de bajo nivel. Veamos las diferencias por nosotros mismos y agreguemos un recurso de bajo nivel CfnBucket a nuestra pila. Agregue las siguientes líneas a su archivo de pila:

  
const cfnBucket = new s3.CfnBucket(esto, 'MyCfnBucket');
 

Si compilamos y sintetizamos de nuevo, podemos ver cómo los dos cubos tienen propiedades diferentes, la construcción S3 de orden superior de la Biblioteca de Construcciones ha configurado algunos atributos de política para nosotros, a pesar de que hemos creado los recursos de construcción de alto orden y bajo nivel sin ninguna propiedad Las propiedades y funcionalidades de enmascaramiento y abstracción del consumidor son uno de los puntos fuertes de CDK.

¡Desplieguemos nuestra pila ahora!

  
cdk deploy
 

Fácil como eso.

5. Creación de construcciones de CDK

Vamos a escribir una biblioteca NPM que será importada por nuestra aplicación cliente. Para eso, vamos a alojar nuestro código en Github en lugar de publicarlo en el registro NPM. Vamos a omitir los comandos relacionados con git en este tutorial.

Ahora que hemos aprendido sobre construcciones de alto y bajo orden y cómo consumirlas en nuestras pilas, es hora de crear nuestra primera construcción y publicarla como una biblioteca NPM que luego puede ser consumida por una aplicación cliente.

Creación de nuestra clase de construcción

De la misma manera que lo hicimos al crear nuestra primera aplicación, podemos usar la CLI de CDK para crear nuestra biblioteca, pero tenemos que especificar que queremos crear una biblioteca ahora:

  
mkdir cdk-workshop-golden-constructs && cd cdk-workshop-golden-constructs
cdk init lib --language=typecript
 

Vea cómo ya no tenemos una clase de aplicación y cómo se exporta la clase que extiende el Construct

El contexto de nuestro ejemplo será el siguiente: una nube central El equipo de arquitectura proporcionará recursos dorados al resto de la organización. Estos recursos se adherirán a un conjunto de políticas de cumplimiento que se aplicarán a los recursos a través de los Aspectos implementados.

Para ejemplificar, vamos a construir un Constructo para una instancia RDS de PostgreSQL de producción que cumpla con los siguientes requisitos de cumplimiento:

  • Almacenamiento habilitado para MultiAZ
  • cifrado con la clave KMS predeterminada o proporcionada (los clientes que consuman esta construcción tendrán la libertad de elegir cuál)
  • Debe ser etiquetado con la Departamento tecla

Vamos a cambiar ligeramente la estructura que la CLI crea para nosotros. Vamos a tener un archivo TypeScript para cada clase que vamos a exportar (un archivo .ts para RDS en nuestro caso) y luego un index.ts que exportará las clases en esos archivos.

Entonces, la estructura de nuestra carpeta lib será la siguiente:

Golden Lib Folder Structure

Contenido de index.ts:

  
export * from './rds';
 

Contenido de rds.ts:

  
import cdk = require('@aws-cdk/core');
import rds = require('@aws-cdk/aws-rds');


export interface ProductionPostgresqlProps {}

export class ProductionPostgresql extends cdk.Construct {
  public readonly productionPostgresql: rds.CfnDBInstance;

  constructor(scope: cdk.Construct, id: string, props: ProductionPostgresqlProps) {
    super(scope, id);
  }
}
 


No se olvide de instalar y guardar la dependencia para aws-rds con shell npm install --save @ aws-cdk / aws-rdsavancemos

Así quey construyamos primero el recurso RDS para la instancia de PostgreSQL y luego implementemos lo mencionado Aspectos en una etapa posterior. Haremos uso de la documentación de referencia y, dado que vamos a crear recursos sin formato de CloudFormation de bajo nivel, también la referencia de CloudFormation .

  
import cdk = require('@aws-cdk/core');
import rds = require('@aws-cdk/aws-rds');


export interface ProductionPostgresqlProps {}

export class ProductionPostgresql extends cdk.Construct {
  public readonly productionPostgresql: rds.CfnDBInstance;

  constructor(scope: cdk.Construct, id: string, props: ProductionPostgresqlProps) {
    super(scope, id);

    this.productionPostgresql = new rds.CfnDBInstance(this, 'productionPostgresql', props);
  }
}
 

Este será nuestro constructor para crear una instancia RDS de propósito general de bajo nivel de CloudFormation. A continuación, aplicaremos un motor de base de datos al Construct y algunas restricciones de cumplimiento también.

Implementando Aspectos

En esta sección vamos a implementar un total de 4 Aspectos con los siguientes objetivos:

  1. Seleccionar el motor DB y fijarlo a una versión específica.
  2. Aplicar la regla de cumplimiento MultiAZ
  3. Verificar la regla de cumplimiento de cifrado
  4. Verificar la regla de cumplimiento de etiquetado

Seleccionar el motor DB y fijarlo a unaversión específica

  
class RdsPostgresqlEngine implements cdk.IAspect {
    public visit(node: cdk.IConstruct): void {
      if (node instanceof rds.CfnDBInstance) {
        node.engine = 'postgres';
        node.engineVersion = '11.4'
      }
    }
}
 

Verificarregla de cumplimiento MultiAZ

  
class RdsMultiAzEnabled implements cdk.IAspect {
    public visit(node: cdk.IConstruct): void {
      if (node instanceof rds.CfnDBInstance) {
        node.multiAz = true
      }
    }
}
 

Verificar laregla de cumplimiento de cifrado

  
class RdsEncryptionChecker implements cdk.IAspect {
  public visit(node: cdk.IConstruct): void {
    if (node instanceof rds.CfnDBInstance) {
      if (!node.storageEncrypted) {
        node.node.addError('Database storage encryption must be enabled');
      }
    }
  }
}
 

Comprobarregla de cumplimiento de etiquetado

  
class RdsTagsChecker implements cdk.IAspect {
  public visit(node: cdk.IConstruct): void {
    if (node instanceof rds.CfnDBInstance) {
      var tags: string[] = [];
      for (let tagObject of node.tags.renderTags()) {
        tags.push(tagObject['key'])
      }
    
      if (!(tags.includes('Department'))) {
          node.node.addError('You must specify the \'Department\' tag for your DynamoDB CfnTable construct');
      }
    }
  }
}
 

A continuación vamos a aplicar los Aspectos a nuestra Construcción de esta manera:

  
  //...(constructor function)
    this.productionPostgresql = new rds.CfnDBInstance(this, 'productionPostgresql', props);
    this.node.applyAspect(new RdsMultiAzEnabled())
    this.node.applyAspect(new RdsPostgresqlEngine())
    this.node.applyAspect(new RdsEncryptionChecker())
    this.node.applyAspect(new RdsTagsChecker())
 

Nuestra biblioteca está lista para ser consumida por la aplicación cliente.

El consumo de nuestra clase de construcción

NPM nos permite instalar directamente una biblioteca NPM desde un repositorio de GitHub.

Cree un nuevo repositorio para nuestra aplicación cliente e instale la biblioteca Construct como una dependencia.

  
  mkdir cdk-workshop-client-app && cd cdk-workshop-client-app
  cdk init --language typecript
  npm install Amongil/cdk-workshop-golden-constructs
 

Ahora podemos en nuestro archivo fuente de la aplicación cliente importarlo así en la parte superior del archivo:

  
import rds = require('cdk-poc-corporate-constructs / lib / rds');
 

Podríamos crear un recurso como este:

  
new rds.ProductionPostgresql(this, 'MyProductionPostgresql', {
    dbInstanceClass: 'db.t2.medium',
    allocatedStorage: '10',
    masterUsername: 'myUser',
    masterUserPassword: 'myPassword',
    dbName: 'cdkpocpostgres',
    storageEncrypted: true,
    tags: [
        {
            key: 'Department',
            value: 'AI Factory',
        },
    ],
});
 

Escribir pruebas para nuestra aplicación CDK

El AWS CDK nos brinda una biblioteca de afirmaciones para simplificar la creación de nuestras pruebas unitarias. Aquí vamos a usar Jest para el marco y esta biblioteca para crear las afirmaciones. El proceso es el siguiente: creamos una aplicación, definimos una pila y le agregamos recursos, luego afirmamos ciertas propiedades contra esta colección de recursos.

Entonces, continuemos con nuestro ejemplo de instancia PostgreSQL de producción con cumplimiento cumplido de antes y escriba una prueba para verificar la versión del motor:

  
test('test engine version is 11.4 for a production postgresql instance', () => {
  const stack = new cdk.Stack();

  const postgres = new rds.ProductionPostgresql(stack, 'MyProdPostgres', {
    dbInstanceClass: 'db.t2.micro'
  });
  
  expect(stack).to(haveResource('AWS::RDS::DBInstance', {
    EngineVersion: '11.4'
  }))
});
 

Escribir pruebas unitarias en nuestra aplicación cliente que consumen otras bibliotecas es importante si vamos a usar la última versión que publican. De esta forma, siempre podemos beneficiarnos de las últimas correcciones de errores y características, al tiempo que nos aseguramos de que los motores centrales y las propiedades de las que realmente dependemos sean los mismos.

6. Aplicaciones CDK CI / CD con CircleCI

Ahora que nuestra infraestructura está definida como código y como una aplicación TypeScript, podemos ponerla en el mismo ciclo de vida que cualquier otra aplicación de software. Esto significa, capacidades de CI / CD con linting, pruebas, fase de construcción y fase de implementación (con la ayuda de la herramienta CDK CLI).

Para lograr esto, he usado CircleCI ya que es realmente fácil de conectar con repositorios públicos de GitHub. El flujo de trabajo se puede definir de la siguiente manera:

  
version: 2
jobs:
  build:
    docker:
      - image: circleci/node
    steps:
      - checkout
      - run:
          name: update-npm
          command: 'sudo npm install -g npm@latest'
      - restore_cache:
          key: dependency-cache-{{ checksum "package.json" }}
      - run:
          name: install-npm-wee
          command: npm install
      - save_cache:
          key: dependency-cache-{{ checksum "package.json" }}
          paths:
            - ./node_modules
      - run:
          name: build
          command: npm run build
  lint:
    docker:
      - image: circleci/node
    steps:
      - checkout
      - run:
          name: update-npm
          command: 'sudo npm install -g npm@latest'
      - restore_cache:
          key: dependency-cache-{{ checksum "package.json" }}
      - run:
          name: install-npm-wee
          command: npm install
      - save_cache:
          key: dependency-cache-{{ checksum "package.json" }}
          paths:
            - ./node_modules
      - run:
          name: lint
          command: npm run lint
  test:
    docker:
      - image: circleci/node
    steps:
      - checkout
      - run:
          name: update-npm
          command: 'sudo npm install -g npm@latest'
      - restore_cache:
          key: dependency-cache-{{ checksum "package.json" }}
      - run:
          name: install-npm-wee
          command: npm install
      - save_cache:
          key: dependency-cache-{{ checksum "package.json" }}
          paths:
            - ./node_modules
      - run:
          name: test
          command: npm run test
  deploy:
    docker:
      - image: circleci/node
    steps:
      - checkout
      - run:
          name: update-npm
          command: 'sudo npm install -g npm@latest'
      - restore_cache:
          key: dependency-cache-{{ checksum "package.json" }}
      - run:
          name: install-npm-wee
          command: npm install
      - save_cache:
          key: dependency-cache-{{ checksum "package.json" }}
          paths:
            - ./node_modules
      - run:
          name: deploy
          command: npm run cdk deploy
          no_output_timeout: "30m"

workflows:
  version: 2
  lint_build_test_deploy:
    jobs:
      - lint
      - build
      - test
      - deploy:
          requires:
            - lint
            - build
            - test
          filters:
            branches:
              only: master
 

Ese flujo de trabajo se vería así:

CircleCI workflow

7. Conclusiones

Verdaderamente, AWS CDK es un paso adelante en el paradigma de Infraestructura como Código. Escribir aplicaciones de software reales, con pruebas unitarias y prácticas estándar de CI / CD solo hará que nuestra infraestructura sea más robusta y atraerá más a los desarrolladores cuando se trata de cumplir con los estándares. Con la biblioteca de afirmaciones, podemos ver que el CDK viene con estas ideas desde el principio.

Como desarrollador de infraestructura, espero que podamos alejarnos de DSL, archivos .yaml y usar AWS CDK como base para el desarrollo de infraestructura dentro del ecosistema de AWS y no puedo esperar para ver qué hará la comunidad con el CDK, encontrando nuevos formas y aportando innovación.


Álvaro Mongil

Working in Madrid for BBVA Next Technologies at the Azure CCoE as a Cloud Architect.

Studying a Master's Degree in Big Data and Visual Analytics, learning Statistics and technologies such as Hadoop, Spark, MongoDB, Machine Learning and Neural Networks.

Interested in Cloud Computing, Data Engineering, Distributed Systems, Machine Learning and Open-Source.

Related Posts

Únete a nuestra Newsletter

Lidera la Conversación en la Nube