Background Image
TECHNOLOGIE

L'infrastructure en tant que code réel : Une introduction au modèle de programmation Construct

Le modèle de programmation Construct
Headshot - Will Wermager
Will Wermager
Consultant principal

April 25, 2023 | 10 Lecture minute

L'infrastructure en tant que code est une partie non négociable de la construction de toute sorte de plateforme maintenable. La première chose qui vient à l'esprit lorsqu'on entend Infrastructure as Code (IaC) est probablement des outils comme Terraform de Hashicrorp et AWS CloudFormation avec une infrastructure définie explicitement en JSON, YAML, ou un langage spécifique à l'outil comme HCL de Terraform. Bien qu'il s'agisse de méthodes viables pour définir notre infrastructure, elles présentent quelques lacunes notables. En particulier, ce sont tous des langages déclaratifs, et pour cette raison, nous devons définir explicitement toute notre infrastructure et les pièces qui la relient. Avec ces langages, il y a souvent des modèles de chaudières et il n'y a que peu ou pas d'abstraction à ce niveau, ce qui entraîne des définitions d'infrastructure difficiles à manier pour les systèmes complexes ou de grande taille.

En 2019, le kit de développement du cloud AWS (AWS CDK) et le modèle de programmation de la construction (CPM) ont fait leur apparition. Après avoir lutté en interne contre le manque de modularité et avoir eu besoin d'abstraire les détails des modèles d'architecture communs, Amazon a créé l'AWS CDK en nous donnant un moyen de définir l'infrastructure en tant que code à l'aide d'un langage de programmation impératif. Bien qu'il s'agisse au départ d'un projet Java en interne, il a été mis en open-source en tant que projet Typescript. La prise en charge d'autres langages est assurée par un autre outil libre, JSII, qui crée des liaisons dans ces langages pour leur permettre d'interagir avec les classes JavaScript. La liste des langages compatibles au moment de la rédaction de ce document comprend Typescript, JS, Python, Java, C# et Go (preview). Depuis sa publication en code source libre, il existe de nombreuses implémentations du CPM dérivées de la solution d'Amazon (AWS CDK). Au moment d'écrire ces lignes, ces implémentations incluent CDKtf (Terraform), CDK8s (Kubernetes), et Projen un outil pour gérer les fichiers de configuration de projet comme package.json, tsconfig.json, etc. Ce dernier montre que l'utilisation du CPM ne doit pas être isolée à la seule infrastructure, car c'est une méthodologie viable pour tout ce qui est configuré via un langage déclaratif.

Bien que les exemples de ce billet soient écrits pour AWS CDK, il convient de noter que les avantages de l'utilisation du CPM ne sont pas exclusifs à l'offre d'Amazon. Quelle que soit l'implémentation que vous choisissez, vous disposez de toute la puissance du langage de programmation de votre choix pour construire votre infrastructure, qu'elle soit simple ou complexe. Si une fonctionnalité est spécifique à AWS CDK, je l'indiquerai directement.

Avant de continuer, il y a quelques termes clés que nous devons connaître et leur relation les uns avec les autres : app, construct et stack (tableau pour les CDK8).

Construction : Il s'agit de l'élément de base de toute application CDK. Un construct correspond à une ou plusieurs ressources synthétisées, qui peuvent être un VPC, un pod Kubernetes, ou même toutes les ressources requises pour un cluster de base de données et un hôte bastion. Nous discuterons de l'abstraction des constructions dans la prochaine section de ce billet.

Pile (graphique pour les CDK8) : Une pile est le véhicule de déploiement d'une application CDK, et il peut y en avoir plusieurs dans une application. Les piles peuvent être considérées comme un regroupement logique d'éléments d'infrastructure apparentés, représentés par des constructions. Chaque Stack/Chart une fois synthétisé va produire un fichier de configuration déclaratif qui correspond au CDK de choix : un template CloudFormation pour AWS CDK, un fichier de configuration Terraform pour CDKtf, un Kubernetes Manifest pour CDK8s, etc.

App : Un CDK App est un arbre de constructions : le nœud racine de cet arbre est la construction App, d'où il peut se ramifier vers une ou plusieurs constructions stack (ou chart), les stacks ont alors une ou plusieurs constructions qui elles-mêmes peuvent englober une ou plusieurs constructions.

Le diagramme ci-dessous donne une vue d'ensemble de la structure d'une application CDK.

Infrastructure as Actual Code: An Introduction to the Construct Programming Model Blog - Graphic #1

Ce billet est accompagné d'un projet d'exemple qui utilise le CDK d'AWS. J'ai créé ce projet principalement pour montrer à quel point il est simple d'établir des normes pour les déploiements d'infrastructure en étendant les constructions existantes ainsi qu'en créant et en utilisant des constructions abstraites qui définissent des modèles d'architecture ou des processus entiers. Le diagramme ci-dessous donne une vue d'ensemble de l'architecture déployée par le projet. Notez que comme j'ai opté pour un déploiement de cluster RDS multi-AZ, ce composant de l'architecture n'est pas éligible au Free Tier. Par conséquent, si vous choisissez de déployer cette infrastructure, assurez-vous de nettoyer après coup pour éviter des frais excessifs. Le projet est disponible ici : https://github.com/wwermager/cdk-with-custom-constructs  

 Infrastructure as Actual Code: An Introduction to the Construct Programming Model blog - Graphic #2

Abstraction de l'infrastructure

Les constructions offrent trois niveaux d'abstraction : le niveau 1 (L1), le niveau 2 (L2) et le niveau 3 (L3). Ce dernier est le plus abstrait. Les constructions L1 sont une correspondance 1:1 avec leur ressource sous-jacente dans le langage déclaratif de base. C'est dans les constructions de niveau 2 que le CPM commence vraiment à briller. Au niveau L2, nous n'avons plus à nous préoccuper de l'excès de documentation qui découle de l'utilisation de la ressource au niveau le plus bas. En outre, nous avons accès à des méthodes d'aide et à des configurations par défaut qui nous facilitent considérablement la vie. À titre d'exemple, une méthode d'aide immensément utile parmi de nombreuses méthodes d'utilisation de AWS CDK Constructs est grant qui nous permet de créer les politiques IAM nécessaires pour accéder aux ressources avec une seule ligne de code.

// From lib/api-stack.ts 
props.dbInfra.dbCluster.secret?.grantRead(isolatedApiFunction); 

Cette ligne est synthétisée dans la ressource CloudFormation ci-dessous. Cela montre à quel point notre infrastructure en tant que code peut être plus succincte et plus lisible en utilisant une implémentation CPM.

"getlambdafunctionServiceRoleDefaultPolicyC1D2F054": { 
 "Type": "AWS::IAM::Policy", 
 "Properties": { 
  "PolicyDocument": { 
   "Statement": [ 
    { 
     "Action": [ 
      "secretsmanager:DescribeSecret", 
      "secretsmanager:GetSecretValue" 
     ], 
     "Effect": "Allow", 
     "Resource": { 
      "Fn::ImportValue": "DatabaseStack:ExportsOutputRefrdswithbastionhostrdsdatabaseclusterSecretAttachment7F0016B283030634" 
     } 
    } 
   ], 
   "Version": "2012-10-17" 
  }, 
  "PolicyName": "getlambdafunctionServiceRoleDefaultPolicyC1D2F054", 
  "Roles": [ 
   { 
    "Ref": "getlambdafunctionServiceRole4009E415" 
   } 
  ] 
 }, 
 "Metadata": { 
  "aws:cdk:path": "ApiStack/get-lambda-function/ServiceRole/DefaultPolicy/Resource" 
 } 
} 

Enfin, il y a les constructions L3, aussi communément appelées "patterns". Ce sont les constructions les plus abstraites et elles sont utilisées pour les modèles d'architecture communs ou certaines fonctionnalités complexes. Dans l'exemple d'application cité plus haut, l'élément AuroraMysqlWithBastionHost et MysqlSetup sont des exemples de constructions L3. La première, un modèle d'architecture commun, déploie la base de données, l'infrastructure réseau et un hôte bastion. La seconde déploie une fonction lambda qui instancie la base de données avec une table et quelques données lors de notre premier déploiement de la pile de base de données, ce qui nous permet de disposer d'une fonctionnalité complexe. Ces constructions peuvent ensuite être déployées dans une pile, configurées par les propriétés exposées par leur interface. Les quelque 20 lignes de code ci-dessous synthétisent un modèle CloudFormation de plus de 2000 lignes de JSON formaté.

// From lib/api-stack.ts 
this.dbInfra = new AuroraMysqlWithBastionHost( 
  this, 
  "rds-with-bastion-host", 
  { 
    bastionHostInitScriptPath: path.join( 
      __dirname, 
      props.config.bastionHostInitScriptPath 
    ), 
    bastionhostKeyPairName: props.config.bastionhostKeyPairName, 
    dbAdminUser: props.config.dbAdminUser, 
    defaultDbName: props.config.defaultDbName, 
    dbSecretName: props.config.dbSecretName, 
    dbPort: props.config.dbPort, 
  } 
); 
 
new MysqlSetup(this, "mysql-setup", { 
  dbInfra: this.dbInfra, 
  lambdaDirectory: path.join(__dirname, props.config.lambdaApisDirectory), 
  defaultHandler: `utils/${props.config.defaultHandler}`, 
  dbTableName: props.config.dbTableName, 
}); 

Partager des modèles et établir des normes

Dans le modèle AuroraMysqlWithBastionHost mentionné précédemment, je tire parti d'une construction L3 publiquement disponible, que l'on trouve ici pour créer automatiquement une paire de clés pour se connecter à l'hôte bastion EC2. Je souligne ce point car il montre à quel point il est facile de partager/consommer des constructions personnalisées, un avantage clé du CPM. En ajoutant simplement la bibliothèque aux dépendances de notre projet (package.json dans ce cas) nous sommes en mesure de l'utiliser dans toutes les constructions et piles que nous créons. Il convient de noter que les constructions écrites en Typescript offrent un peu plus de flexibilité dans la façon dont elles peuvent être partagées. Comme avec JSII, des liaisons linguistiques peuvent être créées pour n'importe quelle langue prise en charge, rien ne vous empêche d'écrire et de partager des constructions spécifiquement pour la langue de votre choix.

Cette facilité de partage et de consommation des constructions est un énorme argument de vente dans l'utilisation d'une implémentation CPM. Avec l'héritage de notre côté, il devient trivial d'étendre une construction existante et de définir ou d'exiger certaines propriétés. Dans le projet d'exemple, c'est ce que j'ai fait à la fois pour l'élément Vpc et Fonction et Function Voici un exemple d'encapsulation de la construction de base Vpc de base avec une configuration de sous-réseau explicite.

// From lib/common/network/database-vpc.ts 
export class DatabaseVpc extends ec2.Vpc { 
  constructor(scope: Construct, id: string, props?: VpcProps) { 
    super(scope, id, { 
      ...props, 
      subnetConfiguration: [ 
        { 
          cidrMask: 28, 
          name: "public-subnet", 
          subnetType: ec2.SubnetType.PUBLIC, 
        }, 
        { 
          cidrMask: 28, 
          name: "private-with-egress", 
          subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS, 
        }, 
        { 
          cidrMask: 28, 
          name: "rds-private-subnet", 
          subnetType: ec2.SubnetType.PRIVATE_ISOLATED, 
        }, 
      ], 
    }); 
  } 
} 

Voici un exemple d'extension de la construction de base Fonction de base pour qu'elle exige que la fonction soit placée dans un VPC qui est spécifiquement du type DatabaseVpc. 

// From lib/common/lambda/isolated-function.ts 
export interface IsolatedFunctionProps extends lambda.FunctionProps { 
  readonly vpc: DatabaseVpc; 
} 
 
export class IsolatedFunction extends lambda.Function { 
  constructor(scope: Construct, id: string, props: IsolatedFunctionProps) { 
    super(scope, id, { 
      ...props, 
      vpc: props.vpc, 
    }); 
  } 
} 

La possibilité d'outrepasser les valeurs par défaut de cette manière fait du CDK un outil puissant dans votre boîte à outils de gouvernance et de normalisation de l'infrastructure pour les exigences spécifiques à l'entreprise. Pour donner un exemple concret, dans le cadre d'un précédent projet auquel j'ai participé, une équipe d'infrastructure s'est consacrée à la définition et à l'extension des constructions de manière à ce qu'elles répondent aux exigences de sécurité de l'entreprise et aux meilleures pratiques internes. Elle a ensuite regroupé ces constructions et les a mises à la disposition de toutes les équipes internes via des référentiels internes. Les équipes chargées des applications étaient alors libres de construire l'infrastructure dont elles avaient besoin pour leurs applications à l'aide de ces concepts. Ce n'est que lorsqu'une équipe choisissait de ne pas utiliser ces modèles qu'elle devait obtenir des approbations ou des exceptions spécifiques à un cas d'utilisation de la part des équipes chargées de la sécurité de l'information. Ces pratiques entourant le CDK nous ont permis de gagner un temps considérable que nous aurions pu passer en réunions pour faire approuver l'infrastructure, attendre qu'elle soit déployée par une autre équipe ou apprendre un langage ésotérique de l'IaC.

Quel est le problème ?

Bien que je sois un peu partial quant à l'utilisation d'une implémentation CPM lorsqu'elle est disponible en raison de mon expérience de projet antérieure, il y a certainement des choses dont il faut être conscient lors de l'utilisation d'une telle implémentation. Bien que le CPM permette aux développeurs et aux équipes d'infrastructure d'aller plus vite dans la construction de systèmes complexes, les abstractions et la flexibilité qui nous donnent cette vitesse ne remplacent pas une solide connaissance fondamentale des ressources sous-jacentes que nous construisons et des meilleures pratiques qui les entourent. Une équipe d'application ayant peu ou pas d'expérience en matière de cloud ne devrait pas déployer une infrastructure de cloud sans une certaine supervision. L'utilisation de structures créées en interne pour les besoins de l'entreprise peut contribuer à atténuer ce problème.

Même avec une connaissance approfondie de la plateforme de votre choix, le niveau extrême d'abstraction possible, en particulier lors de l'utilisation de constructions L3 (patterns), peut être une source d'inquiétude. Cette inquiétude peut être atténuée par le fait qu'en fin de compte, nous déployons toujours le même modèle CloudFomration, la même configuration Terraform ou le même manifeste Kubernetes que nous aurions dû construire de toute façon. Par exemple, dans l'exemple de projet AWS CDK que j'ai fourni, si vous le construisez, vous pouvez trouver les modèles CloudFormation générés dans le fichier cdk.out dans le répertoire cdk.out. Encore mieux que de regarder la sortie synthétisée serait d'écrire des tests unitaires complets. AWS CDK, CDKtf et CDK8s fournissent tous un ensemble d'utilitaires pour les tests. Nous pouvons utiliser ces outils pour nous assurer que seules les ressources que nous attendons sont créées et que les configurations que nous avons données sont appliquées. Vous trouverez ci-dessous un exemple de test vérifiant que le VPC que la pile de base de données déploie dans l'exemple d'application possède 3 sous-réseaux (voir la construction du Vpc personnalisé ci-dessus) pour chaque zone de disponibilité dans notre région de déploiement.

// From test/database-stack.ts 
const ec2Client = new EC2Client({ region: process.env.AWS_REGION }); 
const app = new cdk.App(); 
const stack = new DatabaseStack.DatabaseStack(app, "database-stack", { 
  env, 
  config, 
}); 
const template = Template.fromStack(stack); 
// TEST CASE 
it("should deploy 3 subnets for each availability zone in our region.", async () => { 
  const azs = await ec2Client.send(new DescribeAvailabilityZonesCommand({})); 
  template.resourceCountIs( 
    "AWS::EC2::Subnet", 
    azs.AvailabilityZones.length * 3 
  ); 
}); 

Enfin, bien qu'il ne s'agisse pas tant d'une préoccupation que d'un défi potentiel à l'usage, ces projets sont dans différents états de complétude. Lorsqu'une construction est ajoutée ou mise à jour dans AWS CDK, la construction L1 est immédiatement disponible en raison de la correspondance 1:1. Cependant, les constructions L2 et L3 peuvent avoir tendance à prendre du retard lorsque les ressources sous-jacentes reçoivent des mises à jour. D'un autre côté, CDKtf n'est pas encore disponible de manière générale, il y a donc un certain risque inhérent à choisir de l'utiliser car il est possible que des changements d'API soient apportés à chaque nouvelle version. Un autre problème avec CDKtf est que la plupart des constructions pour les fournisseurs disponibles semblent être L1 et qu'il n'y a donc pas beaucoup de méthodes d'aide comme la méthode grant d'AWS CDK présentée plus haut. Terraform n'a rien à se reprocher sur ce point, car fournir une méthode unique qui fonctionne avec toutes les API des fournisseurs pertinents est une tâche beaucoup plus complexe. Notez que si vous utilisez le fournisseur AWS via CDKtf, vous pouvez utiliser l'adaptateur Terraform adaptateur AWS de Terraform qui permet d'utiliser les constructions CDK d'AWS directement dans CDKtf, mais pour tout autre fournisseur, vous devrez créer explicitement des autorisations. Il convient de noter que cet adaptateur AWS n'est lui aussi qu'en avant-première technique.

Réflexions finales

Le CPM et ses implémentations sont un excellent moyen d'accélérer le processus permettant de passer d'un diagramme d'architecture à une solution robuste. La possibilité de personnaliser et de créer facilement des constructions nous permet de créer des ressources qui répondent à des besoins commerciaux spécifiques ou même de fournir des modèles d'architecture entiers qui peuvent être facilement distribués et réutilisés publiquement ou au sein de notre organisation. La création de ces constructions, de notre infrastructure, dans un langage de programmation à part entière nous donne beaucoup plus de pouvoir que la nature déclarative de la plupart des solutions IaC. En outre, nous pouvons nous assurer que nous écrivons des tests unitaires robustes pour nos piles afin de nous prémunir contre les changements accidentels et de nous assurer que nous déployons exactement ce que nous prévoyons de déployer. Bien que certaines des implémentations CPM soient encore en cours de développement, je vous encourage vivement à essayer l'une d'entre elles dans le cadre de votre prochain projet, s'il s'y prête. Personnellement, j'ai hâte de commencer à travailler avec l'offre CDK de Terraform, malgré sa relative jeunesse. À moins qu'un projet ne soit entièrement basé sur AWS, il est presque certain que Terraform sera le choix de prédilection pour l'IaC. Ressources utiles

  • CDK AWS 

  • CDKtf - Terraform

  • CDK8s - Kubernetes

  • Projen - Génération de projets modèles à l'aide de CPM

  • JSII - Générer des liens linguistiques

Technologie
Ingénierie des plates-formes

Vous avez besoin d'aide pour créer votre prochaine application Go ?

Dernières réflexions

Explorez nos articles de blog et laissez-vous inspirer par les leaders d'opinion de nos entreprises.
Tomorrow Technology Today Blog Thumbnail
DONNÉES

Comment savoir si votre plateforme de données est réellement moderne ?

Voyons ce qui définit une plateforme de données moderne et comment en exploiter tout le potentiel pour votre entreprise.