Note importante : Vous devez avoir lu et effectué les manipulations de la Partie 1 de Terraform avant de commencer cette section.

Etape 1 - Notre première instance.

Nous allons créer un fichier nommé instances.tf dans notre dossier application, qui va contenir la description de toutes nos instances. Pour commencer, nous allons faire petit : une instance, seule, qui ne se connecte à aucun réseau.

# Première instance
resource "openstack_compute_instance_v2" "hello_world" {
  name            = "bonjour"
  image_name      = "Centos 7"
  flavor_name     = "b2-7"
}

Ici, nous allons créer une instance (définie par le openstack_compute_instance_v2), basée sur l'image (image_name) Centos 7 (préalablement créée dans OpenStack), et ayant comme gabarit (flavor_name) b2-7. Le gabarit est ici issu des grilles d'OVH, ce qui correspond chez eux à 2 vCPU, 7Go de RAM et 50Go de disque. Cette instance aura pour nom (name) "bonjour", dans OpenStack. Vous vous demandez peut-être ce qu'est ce hello_world, et à juste titre : Et bien, il s'agit du nom de notre ressource Terraform. Pour le moment, cela vous semble idiot, et à juste titre. Mais nous verrons un peu plus tard que ce sera fortement utile. Il est important de noter que le nom de la ressource Terraform ET le nom Openstack (name) doivent être UNIQUES. Sinon, Terraform jettera une erreur.

Quoi qu'il en soit, il est temps d'essayer de déployer : Tapez la commande terraform applydans le terminal précédemment ouvert. Terraform devrait alors mouliner, et au bout de quelques secondes, vous indiquer ceci :


An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # openstack_compute_instance_v2.hello_world will be created
  + resource "openstack_compute_instance_v2" "hello_world" {
      + access_ip_v4        = (known after apply)
      + access_ip_v6        = (known after apply)
      + all_metadata        = (known after apply)
      + availability_zone   = (known after apply)
      + flavor_id           = (known after apply)
      + flavor_name         = "b2-7"
      + force_delete        = false
      + id                  = (known after apply)
      + image_id            = (known after apply)
      + image_name          = "Centos 7"
      + name                = "bonjour"
      + power_state         = "active"
      + region              = (known after apply)
      + security_groups     = (known after apply)
      + stop_before_destroy = false

      + network {
          + access_network = (known after apply)
          + fixed_ip_v4    = (known after apply)
          + fixed_ip_v6    = (known after apply)
          + floating_ip    = (known after apply)
          + mac            = (known after apply)
          + name           = (known after apply)
          + port           = (known after apply)
          + uuid           = (known after apply)
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.
  Enter a value:

Comme vous le voyez, Terraform est très verbeux, et vous donne un maximum d'informations. Pour valider, tapez yes, puis la touche entrée. Terraform va alors réellement effectuer les actions.

openstack_compute_instance_v2.hello_world: Creating...
openstack_compute_instance_v2.hello_world: Still creating... [10s elapsed]
openstack_compute_instance_v2.hello_world: Creation complete after 18s [id=1055c180-5f36-468b-9dc5-e0250578a60a]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Et voilà, notre instance est créée; elle n'est reliée à aucun réseau et ne sert à rien... Mais elle est là. Vous devriez d'ailleurs pouvoir vous y connecter via l'onglet "Console" d'Horizon : Compute -> Overview -> bonjour -> console.

Comme nous payons à l'heure, ne laissons pas une instance tourner inutilement. Reprenons courageusement notre terminal, et tapons terraform destroy, puis yes. Voilà, notre instance est partie au paradis des machines virtuelles. Vous pouvez le constater une fois de plus sur Horizon.

Note : Si devoir valider manuellement au déploiement ou à la destruction vous embête, vous pouvez utiliser l'argument --auto-approve. Faites attention cependant, c'est un jouet assez dangereux...

Voilà, vous venez de faire vos premiers pas dans une "Infrastructure as Code". Félicitations !

Etape 2 - J'en voudrais 2, en fait.

Nous avons précédemment créé une instance vide, basée sur CentOS 7. Maintenant, j'aimerais bien ne pas en avoir une, mais deux. Car à deux, c'est toujours mieux ! Naturellement, vous auriez envie de modifier le fichier instances.tf comme ceci :

# Première instance
resource "openstack_compute_instance_v2" "numero1" {
  name            = "first"
  image_name      = "Centos 7"
  flavor_name     = "b2-7"
}

# Seconde instance
resource "openstack_compute_instance_v2" "numero2" {
  name            = "deuz"
  image_name      = "Centos 7"
  flavor_name     = "b2-7"
}

Bien évidemment, si vous lancez un terraform apply, cela va fonctionner, vous allez bien avoir deux instances créées, nommées "first" et "deuz". Cependant, c'est un sévère gâchis de place, vous dupliquez du code. Cela semble normal, comme nous avons dit précédemment que chaque nom d'instance doit être unique... Cependant, je vous rappelle que Terraform supporte la variabilisation!

Ainsi, il a été possible de créer une fonctionnalitée fortement utile : count. En définissant ce count, il est possible d'indiquer à Terraform de créer plusieurs fois une instance, un réseau, ou autre. Pour régler le soucis du nom, il suffit alors de variabiliser en utilisant la valeur de count. Voici le résultat :

# Joli groupe
resource "openstack_compute_instance_v2" "hello_world" {
  name            = "hello${count.index + 1}"
  image_name      = "Centos 7"
  flavor_name     = "b2-7"

  count = "2"
}

Et voilà, vous venez de créer deux instances, hello1 et hello2, sur OpenStack. Vous noterez que le nom de la ressource Terraform (hello_world), n'est pas variabilisé : En effet, Terraform va considérer ces deux instances comme étant un groupe, et effectuera donc les modifications sur les 2 machines créées. Cela permet d'assurer l'homogénéïté de notre infrastructure, contrairement à la version où nous dupliquons du code.

Etape 3 - Si on en faisait quelque chose ?

L'idée de créer une machine à vide est drôle, mais assez inutile. Nous allons donc y effectuer des opérations lors de la création. Pour cela, nous allons utiliser une option de Terraform, mais également une fonctionnalité native de beaucoup de système Linux : cloud-init. Commençons par modifier notre fichier instances.tf:

# Première instance
resource "openstack_compute_instance_v2" "hello_world" {
  name            = "bonjour"
  image_name      = "Centos 7"
  flavor_name     = "b2-7"
  user_data = "${file("conf/data.yml")}"
}

Ici, nous voulons créer une instance, qui à un user_data prédéfinis. Cette variable permet, une fois l'instance crée, d'effectuer une série d'opérations sur la machine. Créons le dossier conf, et le fichier data.yml :

#cloud-config
yum_repos:
  nginx: 
    baseurl: "http://nginx.org/packages/mainline/centos/7/$basearch/"
    name: "nginx repo"
    enabled: true
    gpgcheck: false
packages:
- nginx
runcmd:
- ["systemctl", "enable", "nginx"]
- ["systemctl", "start", "nginx"]
- ["chown", "-R", "centos:root", "/usr/share/nginx/html/"]
write_files:
- path: /usr/share/nginx/html/hello.html
  content: |
    coucou${id}
users:
- name: lambda
  groups: sudo
  sudo: ALL=(ALL) ALL
  shell: /bin/bash
  lock-passwd: false
  passwd: $1$GwZT4duo$guq9frwju6IMrz5rBCxIH0

Ici, nous disons à la machine d'installer le paquet Nginx, de l'activer au démarrage de la machine et de le démarrer, de définir centos du groupe root comme propriétaire de /usr/share/nginx/html, et de créer un fichier /usr/share/nginx/html/hello.html, ayant pour contenu "coucou", et de créer l'utilisateur "lambda" avec le mot de passe "monmotdepasse".

Un coup de terraform apply, et vous pourrez voir depuis la console que la machine a été créée. Vous pouvez vous y connecter avec le compte "lambda", et passer en root avec "sudo su". Vous verrez alors que le paquet nginx est bien installé et actif (pour s'en assurer : service nginx status), etc. Cependant, votre machine n'est pas accessible depuis Internet. Nous résoudrons ce problème plus tard, dans un prochain article. En attendant, il nous reste à parler de la persistence des données...

Etape 4 - Persister des données

Nous avons vu comment créer des instances, seules ou en batterie, et comment les configurer en "position initiale". Maintenant, il nous faut parler quelque peu de la persistence des données. Avant tout, définissons le concept de persistence comme étant le principe simple qu'une donnée est toujours accessible, indépendemment des modifications apportées à l'infrastructure. En d'autres termes, lorsqu'on vient à supprimer une instances, ces données doivent rester accessibles, pour être réutilisées plus tard.

Bien évidemment, ce concept de persistence va à l'encontre du principe même des instances : Comment pouvons-nous garder des données alors que nous détruisons une instance pour en créer une nouvelle ? Les utilisateurs réguliers de Docker ont déjà la réponse : les volumes.

Il est important de NE PAS créer les volumes via Terraform. En effet, vous détruiriez les volumes lors du terraform destroy, ce qui fait perdre un peu l'intérêt de la chose.

Allons dans l'interface d'Horizon, puis dans Volumes, et créons en un de 1Go, vide. Cliquez ensuite sur son nom, pour récupérer son id. Ajoutons ensuite le volume à une machine. Editons le fichier instances.tf :

# Première instance
resource "openstack_compute_instance_v2" "hello_world" {
  name            = "bonjour"
  image_name      = "Centos 7"
  flavor_name     = "b2-7"
  user_data = "${file("conf/data.yml")}"
}

resource "openstack_compute_volume_attach_v2" "hello_world" {
  instance_id = "${openstack_compute_instance_v2.hello_world.id}"
  volume_id = "a7dfaaaa-aaaaaa-aaaaaaaa"
  device = "/dev/sdb"
}

Ici, nous définissons que nous voulons relier le volume ayant pour id a7dfaaaa-aaaaaa-aaaaaaaa à l'instance "hello_world" créée plus tôt, dans le point de montage /dev/sdb. Ensuite, il sera possible de le monter avec la classique commande mount (mount /dev/sdb /mnt/coucou) (note : la première fois, il faut également formater le volume : mkfs.ext4 /dev/sdb). Une fois le disque monté, on peut y ajouter et supprimer des données, et ces données seront persistées dans le volume, même si on détruit l'instance !

Nous avons vu dans cet article comment créer une instance, lui donner une configuration initiale, et y relier des volumes. Maintenant, il est temps de les connecter à un réseau. C'est ce que nous verrons dans le prochain article!

Article précédent Article suivant