Infra as code : créer un blog multirégion avec Terraform sur Public Cloud – Part 2

Temps de lecture estimé : 7 minute(s)

Bienvenue dans le deuxième volet de notre série d’articles consacrés à l’Infrastructure as code avec Terraform. Après avoir posé les bases de notre projet, il est désormais temps de configurer une première instance et de voir comment y déployer votre blog !

Dans un monde où le devops règne en maître, il est légitime de vouloir patcher son infrastructure de la même façon dont on corrige son code, avec des possibilités similaires en termes d’automatisation, de reproductibilité et de collaboration. Ces vœux sont aujourd’hui exaucés grâce à l’Infrastructure as code, qui permet de gérer ses ressources de la même manière qu’on travaille sur le code de son application.

Cette série d’articles cherche à illustrer les bénéfices de cette approche. Plutôt que de nous perdre en considérations théoriques, nous avons préféré travailler sur un scénario concret, dont les différentes étapes sont l’occasion de passer en revue les grands principes de l’infra as code. Nous avons retenu Terraform, qui répond particulièrement bien aux problématiques d’abstraction de l’infrastructure en environnement hybride. Les snippets de ces articles sont extraits du guide détaillé, qui est à votre disposition sur Github.

Notre premier article abordait les bases du fonctionnement de Terraform avec les notions clés de provider, resource et variable et la création de notre premier environnement de déploiement. Commençons avec le démarrage d’une instance et la configuration de notre site. Nous verrons ensuite comment sécuriser l’ensemble et mettre en place un premier niveau de répartition de charge entre deux régions.

Comme les considérations liées à l’utilisation d’un système de gestion de contenu (Content Management System, ou CMS) ne sont pas notre sujet du jour, nous allons créer notre blog à l’aide de Hugo, un excellent générateur de sites statiques dont l’installation s’effectue très facilement.

Préparation de l’instance

Pour définir votre instance, ouvrez le fichier « main.tf », où il est nécessaire de commencer par patcher le provider (OpenStack dans notre exemple) en spécifiant la version utilisée (1.5) pour éviter de voir votre infrastructure malmenée par une mise à jour majeure de l’outil Terraform. Nous vous invitons à retenir cette astuce, qui peut vous éviter bien des ennuis. Il est également nécessaire de passer la région en variable pour préparer l’utilisation de plusieurs régions OpenStack.

provider "openstack" {

version     = "= 1.5"

region      = "${var.region_a}"

alias = "region_a"

}
provider

Passons maintenant à l’instance elle-même. Plusieurs éléments sont nécessaires à son lancement. Terraform se charge de résoudre les dépendances et définit le plan d’action correspondant.

resource "openstack_compute_instance_v2" "nodes_a" {
  count       = "${var.count}"
  name        = "${var.name}_a_${count.index}"
  image_name  = "Ubuntu 18.04"
  flavor_name = "${var.flavor_name}"
  key_pair    = "${openstack_compute_keypair_v2.keypair_a.name}"
  user_data   = "${data.template_file.userdata.rendered}"

  network {
    access_network = true
    port           = "${openstack_networking_port_v2.public_a.*.id[count.index]}"
  }

  provider = "openstack.region_a"
}
instance A

Si vous observez de plus près le code utilisé pour notre exemple, vous remarquerez que Terraform autorise l’interpolation de variables : c’est pour cette raison que l’on voit apparaître le paramètre « var.count » au niveau de notre propriété « count » sur notre instance.

Disponible en JSON ou en HCL (HashiCorp Configuration Language), la syntaxe d’interpolation nous permet ici de renseigner le nombre d’instances à démarrer. Terraform itérerera autant de fois que la valeur renseignée dans la variable count. Elle peut servir à d’autres choses, comme la mise en place d’opérations logiques : vérifier qu’une variable remplit une condition avant d’ordonner la création d’une ressource par exemple.

Configurer le système après le démarrage

La configuration de la machine suit la même logique : les données utilisateur (user_data) sont inscrites dans un template qui définit les scripts à lancer ou les fichiers à charger à la création de l’instance. Ces templates sont compilés par Terraform en suivant l’arbre de dépendance des ressources. C’est par exemple à ce niveau que vous pourrez paramétrer Apache pour accueillir un fichier de virtual host que vous aurez préparé en amont.

data "template_file" "userdata" {
  template = <<CLOUDCONFIG
#cloud-config
write_files:
  [...]
  - path: /tmp/setup/myblog.conf
    permissions: '0644'
    content: |
      ${indent(6, data.template_file.myblog_conf.rendered)}
  [...]
runcmd:
   - /tmp/setup/run.sh
CLOUDCONFIG
}
data "template_file" "myblog_conf" {
  template = "${file("${path.module}/myblog.conf.tpl")}"

  vars {
    server_name = "${var.name}.${var.zone}"
  }
}
user_data

Il est aussi possible de déterminer des paramètres de provision automatique pour chaque ressource afin de gagner encore en efficacité ou simplement en cas de quantité de données trop importante. Nous demandons par exemple la copie du contenu de notre blog sur chaque instance initiée via scp.

Nous sommes maintenant en mesure de créer notre première instance. Néanmoins, pour maximiser la sécurité de celle-ci, il est préférable de mettre en place un certificat TLS et une première version de redondance.

Générer un certificat TLS

Évidemment, il ne s’agit pas de déployer manuellement un certificat Let’s Encrypt sur votre serveur Apache, mais plutôt de prévoir dans Terraform la génération et l’intégration automatiques du certificat au moment de la création de l’instance.

Cette fonctionnalité n’existant pas nativement dans Terraform, il vous suffit d’installer le plugin ACME Provider. Terraform va créer ensuite le compte et le certificat au moyen des ressources dédiées, avant de demander la validation.

resource "acme_certificate" "certificate" {
  account_key_pem = "${acme_registration.reg.account_key_pem}"
  common_name     = "${var.name}.${var.zone}"
  dns_challenge {
    provider = "ovh"
  }
}
certificate

Maintenant que vous disposez d’un certificat TLS, pourquoi ne pas renforcer votre infrastructure avec une deuxième instance hébergée sur une autre région ? Dans le cadre de cet article, nous allons nous contenter d’une répartition de charge simple à l’aide du Round Robin DNS, mais nous verrons plus tard que Terraform permet d’aller nettement plus loin en matière de haute disponibilité.

Configurer le Round Robin DNS

Pour déployer notre instance sur une nouvelle région, il suffit de déclarer le provider OpenStack correspondant (région B) au niveau du fichier « main.tf », puis de répéter les éléments de configuration de cette nouvelle instance (port réseau B, instance B…). Il ne reste plus qu’à créer l’articulation entre les deux au moyen du provider OVH, qui recense toutes les ressources nécessaires à l’exploitation de nos API. La ressource « ovh_domain_zone_record » nous permet de spécifier notre zone DNS et les enregistrements associés.

resource "ovh_domain_zone_record" "subdomain_records_a" {
  count     = "${var.count}"
  zone      = "${data.ovh_domain_zone.rootzone.name}"
  subdomain = "${var.name}"
  fieldtype = "A"
  target    = "${data.template_file.ipv4_addr_a.*.rendered[count.index]}"
}
DNS record

Au terme de ces quelques étapes, vous disposez d’un blog opérationnel, avec une première version de la répartition de charge entre deux instances situées dans des régions différentes. Ces quelques manipulations nous ont permis de mieux comprendre le fonctionnement d’une instance sur Terraform et la façon dont on utilise ses différentes propriétés. Il est possible d’aller nettement plus loin : c’est ce que nous verrons dans le prochain article de notre série !

 

Pour connaître le détail complet des recettes Terraform liées à cet article, consultez nos articles Github :