Utilisez Terraform pour vos projets Docker

Magazine
Marque
GNU/Linux Magazine
Numéro
240
Mois de parution
septembre 2020
Spécialité(s)


Résumé

Terraform est un outil populaire pour déployer de l’infrastructure en particulier à destination des Clouds publics. Cependant, il possède de nombreux providers pour dialoguer avec différents hyperviseurs, bases de données ou solutions d’infrastructures en Software Defined. Voyons dans cet article son utilisation avec Docker.


Body

Le concept de l’Infrastructure as Code a pour but d’automatiser le déploiement des systèmes et infrastructures par l’utilisation de méthodes qui se rapprochent du développement logiciel, comme l’utilisation de scripts ou de fichiers de configuration. Ainsi, le processus de déploiement se trouve plus rapide, sans risque d’erreur humaine, répétable et au final mieux maîtrisé.

Les outils de type Puppet ou Ansible sont capables dans une certaine mesure d’interagir avec une couche infrastructure, toutefois leur rôle est privilégié pour ce qui est de la configuration des systèmes. À l’inverse, Terraform ne se préoccupe pas des systèmes d’exploitation, mais agit comme un formidable orchestrateur vers différentes solutions, à partir du moment où elles disposent d’une API. Leur utilisation conjointe est donc tout à fait possible, si ce n’est recommandé.

Dans le cadre d’une utilisation avec Docker, Terraform ne se préoccupe pas de la gestion des images. Leur création avec un DockerFile et leur publication sur une registry restent d’actualité et ceci est géré en dehors de Terraform. Son rôle démarre au moment de l’instanciation des conteneurs depuis une image. HashiCorp, l’éditeur derrière Terraform dispose toutefois d’un outil pour la gestion des images dont la logique est similaire : Packer.

1. Instancier un premier conteneur

1.1 Prérequis

Si le runtime Docker n’est pas présent sur votre machine, l’installation est très simple. Pour une CentOS ou une RedHat, il faudra en plus ajouter le dépôt Docker-ce à votre système.

$ sudo apt install docker.io
$ sudo systemctl enable docker.service
$ sudo systemctl start docker.service
$ sudo usermod -aG docker $USER

Terraform est distribué sous la forme d’un unique binaire compilé en statique, par conséquent il suffit d’extraire le contenu du ZIP téléchargé sur le site de l’éditeur et de le placer dans un répertoire, de préférence présent dans votre ${PATH}.

$ wget https://releases.hashicorp.com/terraform/0.12.24/terraform_0.12.24_linux_amd64.zip
$ sudo unzip -d /usr/local/bin/ terraform_0.12.24_linux_amd64.zip
Archive: terraform_0.12.24_linux_amd64.zip
  inflating: /usr/local/bin/terraform

Voilà, c’est toute la simplicité et l’élégance de Terraform. Il n’y a rien de plus à déployer ou configurer pour rendre l’outil fonctionnel. On peut d’ores et déjà écrire nos premières lignes de code.

1.2 Un conteneur en quelques lignes

Nous allons dans un premier temps instancier un conteneur avec la dernière image Ubuntu via un fichier Terraform. En ligne de commande, ce sera l’équivalent de ces deux commandes :

$ docker pull ubuntu:latest
$ docker run -d --name terraform_container ubuntu:latest

Il n’y a pas d’impératifs forts pour l’organisation des fichiers Terraform. Cependant, la convention veut que le code principal se trouve dans un fichier nommé main.tf.

Tout démarre par la déclaration du provider, en l’occurrence Docker dans notre cas, avec la méthode de connexion. Dans le cas le plus simple, il s’agit d’utiliser la socket UNIX locale, il faut donc avoir les droits sur la socket. Dans le cadre d’une utilisation à distance, avec SSH par exemple, cela amène donc à la nécessité de déclarer les credentials à utiliser pour s’authentifier. Toujours dans notre fichier main.tf, on déclare ensuite les ressources que nous souhaitons instancier. Pour un conteneur nommé terraform_container depuis la dernière image Ubuntu, cela donne :

provider "docker" {
  host = "unix:///var/run/docker.sock"
}

resource "docker_image" "ubuntu" {
  name = "ubuntu:latest"
}

resource "docker_container" "terraform_container" {
  image = docker_image.ubuntu.latest
  name  = "terraform_container"
}

1.3 Cycle de vie

Le conteneur est décrit dans un exemple de code, mais n’est pas encore instancié tant que l’on n’a pas fait appel à Terraform. Nous allons dans un premier temps avoir besoin du provider Terraform pour Docker [1], le binaire que nous avons téléchargé n’en disposant pas. Terraform va automatiquement détecter le backend dont il a besoin depuis le code :

$ terraform init
 
Initializing the backend...
 
Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "docker" (terraform-providers/docker) 2.7.0...
 
[...]
 
* provider.docker: version = "~> 2.7"
 
Terraform has been successfully initialized!

Nous allons maintenant générer un « plan ». Le plan ne crée aucun changement sur l’infrastructure, mais va analyser les actions qui doivent être entreprises pour appliquer les changements souhaités sur l’infrastructure. Cela permet de visualiser les actions que Terraform souhaite entreprendre avant qu’elles ne soient appliquées, en particulier lorsque ces changements sont sensibles. Optionnellement, le plan peut être sauvegardé dans un fichier.

$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage.
 
docker_image.ubuntu: Refreshing state...
[id=sha256:4e5021d210f65ebe915670c7089120120bc0a303b90208592851708c1b8c04bdubuntu:latest]
 
------------------------------------------------------------------------
 
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:
 
   # docker_container.terraform_container will be created
   + resource "docker_container" "terraform_container" {
     + attach           = false
     + bridge           = (known after apply)
     + command          = (known after apply)
     + container_logs   = (known after apply)
     + entrypoint       = (known after apply)
     + env              = (known after apply)
     + exit_code        = (known after apply)
     + gateway          = (known after apply)
     + hostname         = (known after apply)
     + id               = (known after apply)
     + image            = "sha256:4e5021d210f65ebe915670c7089120120bc0a303b90208592851708c1b8c04bd"
     + ip_address       = (known after apply)
     + ip_prefix_length = (known after apply)
     + ipc_mode         = (known after apply)
     + log_driver       = "json-file"
     + logs             = false
     + must_run         = true
     + name             = "terraform_container"
     + network_data     = (known after apply)
     + read_only        = false
     + restart          = "no"
     + rm               = false
     + shm_size         = (known after apply)
     + start            = true
 
     + labels {
         + label = (known after apply)
         + value = (known after apply)
       }
   }
 
Plan: 1 to add, 0 to change, 0 to destroy.[...]

Si la revue du plan est conforme aux actions souhaitées, il peut être appliqué :

$ terraform apply
docker_image.ubuntu: Refreshing state...
[id=sha256:4e5021d210f65ebe915670c7089120120bc0a303b90208592851708c1b8c04bdubuntu:latest]
 
[...]
 
Plan: 1 to add, 0 to change, 0 to destroy.
 
Do you want to perform these actions?
[...]
 
docker_container.terraform_container: Creating...
docker_container.terraform_container: Creation complete after 0s [id=2e6dc1f72de2ba701bbebb355d23515ff3380ff698d4dc39150bc8b8c4686ba3]
 
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

Vérifions que notre conteneur est bien instancié :

$ docker ps -a
CONTAINER ID    IMAGE          COMMAND       CREATED          STATUS                     PORTS   NAMES
133caea16805    4e5021d210f6   "/bin/bash"   12 minutes ago   Exited (0) 5 seconds ago           terraform_container

C’est parfait, Terraform a fait le job. À l’issue de son exécution, un fichier tfstate a été généré afin de pouvoir conserver un lien entre ce qui a été généré et le code de l’infrastructure. Cela permet également de gérer l’ensemble du cycle de vie du provisionning de l’infrastructure, ce que nous allons faire en décommissionnant notre conteneur :

$ terraform destroy
docker_image.ubuntu: Refreshing state... [id=sha256:4e5021d210f65ebe915670c7089120120bc0a303b90208592851708c1b8c04bdubuntu:latest]
docker_container.terraform_container: Refreshing state...
[id=133caea16805a38a3997f7a2fad438e1030a6c3a09ec732db76e706dc22ec217]
 
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
- destroy
 
Terraform will perform the following actions:
# docker_image.ubuntu will be destroyed
- resource "docker_image" "ubuntu" {
     - id     = "sha256:4e5021d210f65ebe915670c7089120120bc0a303b90208592851708c1b8c04bdubuntu:latest" -> null      
     - latest = "sha256:4e5021d210f65ebe915670c7089120120bc0a303b90208592851708c1b8c04bd" -> null
     - name   = "ubuntu:latest" -> null
   }
 
Plan: 0 to add, 0 to change, 1 to destroy.[...]

Que ce soit utilisé avec Docker, VMWare ou AWS [2], cette logique d’utilisation reste dans tous les cas valable.

2. Terraform dans le monde réel

L'exemple précédent n’était pas pertinent dans le périmètre d’un unique conteneur, mais permettait de s’approprier Terraform. Dès qu’il est question d’avoir plusieurs conteneurs qui doivent communiquer, d’exposer une application sur le réseau ou de permettre à un conteneur de disposer d’un stockage persistant, le besoin est tout autre.     

L’outil de prédilection avec Docker est plutôt Docker Compose, mais d’une part l’utilisation du YAML rebute certaines personnes et d’autre part, son utilisation est limitée à ce système de conteneurisation. Terraform propose une autre syntaxe pour arriver à ses fins.

Pour que le lecteur puisse facilement comparer, je suis parti de la stack Docker Wordpress sur le dockerhub [3] et je vous propose une version écrite en Terraform. Cette stack déploie un conteneur de base de données et deux volumes persistants. J’ai juste remplacé MySQL par MariaDB pour ne pas déployer un produit issu d’Oracle sur ma machine.

Voici donc ce que donne notre stack Wordpress réécrite en Terraform :

provider "docker" {

  host = "unix:///var/run/docker.sock"

}

 

resource "docker_network" "private_network" {

  name = "wp_net"

}

 

resource "docker_volume" "wp_vol_db" {

  name = "wp_vol_db"

}

 

resource "docker_volume" "wp_vol_html" {

  name = "wp_vol_html"

}

 

resource "docker_container" "db" {

  name  = "db"

  image = "mariadb"

  restart = "always"

  network_mode = "wp_net"

  mounts {

    type = "volume"

    target = "/var/lib/mysql"

    source = "wp_vol_db"

  }

  env = [

     "MYSQL_ROOT_PASSWORD=rootpassword",

     "MYSQL_DATABASE=wordpress",

     "MYSQL_USER=exampleuser",

     "MYSQL_PASSWORD=examplepass"

  ]

}

 

resource "docker_container" "wordpress" {

  name  = "wordpress"

  image = "wordpress:latest"

  restart = "always"

  network_mode = "wp_net"

  env = [

    "WORDPRESS_DB_HOST=db",

    "WORDPRESS_DB_USER=exampleuser",

    "WORDPRESS_DB_PASSWORD=examplepass",

    "WORDPRESS_DB_NAME=wordpress"

 

  ]

  ports {

    internal = "80"

    external = "8080"

  }

  mounts {

    type = "volume"

    target = "/var/www/html"

    source = "wp_vol_html"

  }

}

Un terraform apply plus loin, nous pouvons vérifier le déploiement de nos deux conteneurs et accéder à notre application sur le port 8080 :

$ docker ps
CONTAINER ID  IMAGE             COMMAND                 CREATED          STATUS         PORTS               NAMES
d52385968da1  mariadb           "docker-entrypoint.s…"  11 seconds ago   Up 10 seconds  3306/tcp            db
58b40a7d5d41  wordpress:latest  "docker-entrypoint.s…"  22 seconds ago   Up 21 seconds  0.0.0.0:8080->80/t  wordpress

Conclusion

La grande force de Terraform à mon sens est de ne pas jouer le rôle d’une couche d’abstraction aux différents providers. C’est souvent un facteur de bugs ou une limitation sur un dénominateur commun de fonctionnalités, qui s’en trouvent forcément réduites. En effet, les ressources manipulées sont directement celles de la solution administrée. On ne manipule pas une VM, mais une instance EC2 ou une machine virtuelle VM avec leurs spécificités et fonctionnalités, ne limitant donc pas l’éventail de possibilités à un sous-ensemble de fonctionnalités partagées.

Le grand intérêt de Terraform réside dans la capacité de pouvoir utiliser un même outil et un même flux de travail avec une grande variété de providers.

Références

[1] Provider Docker pour Terraform : https://www.terraform.io/docs/providers/docker/index.html

[2] J. MOROT, « Infrastructure As Code sous AWS avec Terraform », GNU/Linux Magazine n°216, juin 2018 : https://connect.ed-diamond.com/GNU-Linux-Magazine/GLMF-216/Infrastructure-As-Code-sous-AWS-avec-Terraform

[3] Image Wordpress sur DockerHub : https://hub.docker.com/_/wordpress/



Article rédigé par

Par le(s) même(s) auteur(s)

Déployer un service hautement disponible grâce à Redis Replication

Magazine
Marque
Linux Pratique
Numéro
140
Mois de parution
novembre 2023
Spécialité(s)
Résumé

Redis est un système de stockage de données en mémoire de type NoSQL orienté hautes performances. Il est souvent utilisé comme base de données, système de cache ou de messages. Nous verrons dans cet article comment déployer un service hautement disponible grâce à Redis Replication.

Galera, la solution pour des bases de données hautement disponibles

Magazine
Marque
Linux Pratique
Numéro
137
Mois de parution
mai 2023
Spécialité(s)
Résumé

Lorsqu’une application ne peut tolérer d’indisponibilités ou quand il devient nécessaire de fournir de la redondance au service de bases de données, il convient de mettre en place une architecture limitant les risques d’interruption de service. De même, le besoin de performance peut se faire sentir avec la croissance du nombre d’utilisateurs. Galera est une extension pour MariaDB aidant à résoudre ces deux situations.

Gérez votre stockage avec Stratis

Magazine
Marque
Linux Pratique
Numéro
128
Mois de parution
novembre 2021
Spécialité(s)
Résumé

Quand on parle de gestion du stockage local à un administrateur système Linux, il est généralement question de LVM et de filesystem XFS ou ext4. À un niveau plus évolué, on trouvera ZFS qui n'est pas libre ou bien Btrfs qui tarde à tenir ses promesses. Stratis a pour vocation de proposer quelque chose de neuf avec l’existant !

Les derniers articles Premiums

Les derniers articles Premium

La place de l’Intelligence Artificielle dans les entreprises

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

L’intelligence artificielle est en train de redéfinir le paysage professionnel. De l’automatisation des tâches répétitives à la cybersécurité, en passant par l’analyse des données, l’IA s’immisce dans tous les aspects de l’entreprise moderne. Toutefois, cette révolution technologique soulève des questions éthiques et sociétales, notamment sur l’avenir des emplois. Cet article se penche sur l’évolution de l’IA, ses applications variées, et les enjeux qu’elle engendre dans le monde du travail.

Petit guide d’outils open source pour le télétravail

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Ah le Covid ! Si en cette période de nombreux cas resurgissent, ce n’est rien comparé aux vagues que nous avons connues en 2020 et 2021. Ce fléau a contraint une large partie de la population à faire ce que tout le monde connaît sous le nom de télétravail. Nous avons dû changer nos habitudes et avons dû apprendre à utiliser de nombreux outils collaboratifs, de visioconférence, etc., dont tout le monde n’était pas habitué. Dans cet article, nous passons en revue quelques outils open source utiles pour le travail à la maison. En effet, pour les adeptes du costume en haut et du pyjama en bas, la communauté open source s’est démenée pour proposer des alternatives aux outils propriétaires et payants.

Sécurisez vos applications web : comment Symfony vous protège des menaces courantes

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Les frameworks tels que Symfony ont bouleversé le développement web en apportant une structure solide et des outils performants. Malgré ces qualités, nous pouvons découvrir d’innombrables vulnérabilités. Cet article met le doigt sur les failles de sécurité les plus fréquentes qui affectent même les environnements les plus robustes. De l’injection de requêtes à distance à l’exécution de scripts malveillants, découvrez comment ces failles peuvent mettre en péril vos applications et, surtout, comment vous en prémunir.

Bash des temps modernes

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Les scripts Shell, et Bash spécifiquement, demeurent un standard, de facto, de notre industrie. Ils forment un composant primordial de toute distribution Linux, mais c’est aussi un outil de prédilection pour implémenter de nombreuses tâches d’automatisation, en particulier dans le « Cloud », par eux-mêmes ou conjointement à des solutions telles que Ansible. Pour toutes ces raisons et bien d’autres encore, savoir les concevoir de manière robuste et idempotente est crucial.

Les listes de lecture

9 article(s) - ajoutée le 01/07/2020
Vous désirez apprendre le langage Python, mais ne savez pas trop par où commencer ? Cette liste de lecture vous permettra de faire vos premiers pas en découvrant l'écosystème de Python et en écrivant de petits scripts.
11 article(s) - ajoutée le 01/07/2020
La base de tout programme effectuant une tâche un tant soit peu complexe est un algorithme, une méthode permettant de manipuler des données pour obtenir un résultat attendu. Dans cette liste, vous pourrez découvrir quelques spécimens d'algorithmes.
10 article(s) - ajoutée le 01/07/2020
À quoi bon se targuer de posséder des pétaoctets de données si l'on est incapable d'analyser ces dernières ? Cette liste vous aidera à "faire parler" vos données.
Voir les 125 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous