<iframe src="//www.googletagmanager.com/ns.html?id=GTM-MBJN56" height="0" width="0" style="display:none;visibility:hidden">
terraformer.jpg

Grâce à l'infonuagique, il est aujourd'hui possible d'accéder, créer et modifier toutes les ressources dont vous avez besoin en seulement quelques requêtes à une API et ainsi créer entièrement votre infrastructure. En utilisant un outil qui traduit une description textuelle de votre environnement en une séquence de requêtes, il devient très facile de traiter votre infrastructure comme du code : c'est ce que fait Terraform. Dans cet article, nous allons voir comment utiliser Terraform avec cloud.ca.

Créer une configuration de base

Commençons avec une configuration simple : un VPC (Virtual Private Cloud - nuage privé virtuel en français) contenant un réseau avec une machine virtuelle sur ce réseau. Un VPC est un espace dans le nuage dédié spécifiquement à votre projet. Les VPC n'ont pas accès les uns aux autres et sont une façon efficace de séparer de projets n'ayant aucun rapport entre eux. Un réseau est appelé tier dans la terminologie de CloudStack. On peut aussi appliquer des ACL (Access Control List - liste de contrôle d'accès) à un réseau pour limiter le traffic entrant et sortant à des adresses, protocoles ou port spécifiques. Essayons maintenant d'écrire notre première configuration. Pour cela, vous pouvez cloner le repertoire git : https://github.com/vilisseranen/terraform_cloudca ($ git clone https://github.com/vilisseranen/terraform_cloudca.git) et vous placer sur le tag "step1" ($ git checkout step1).

La première étape consiste à configurer le fournisseur. C'est la partie qui permet de dire à Terraform où et comment faire les appels à l'API et quelles clefs utiliser. Les informations pour configurer le fournisseur avec Terraform se trouvent ici : https://terraform.io/docs/providers/cloudstack/index.html. Ouvrez ou créez le fichier cloudstack.tf.

provider "cloudstack" {
    api_url = "${var.api_url}"
    api_key = "${var.api_key}"
    secret_key = "${var.secret_key}"
}

Terraform permet de séparer une configuration sur plusieurs fichiers pour les gérer plus facilement. Ces fichiers sont ensuite concaténés quand la configuration est appliquée. Vous pouvez voir que la valeur associée à chaque clef est de la forme ${var.name}. Terraform est capable d'associer des variables définies dans un fichier spécial (nommé terraform.tfvars) avec leur référence : ${var.name}. De cette façon, vous pouvez conserver toutes les informations sensibles ou spécifiques à un projet dans un fichier séparé qui ne sera pas placé sous gestion de source. Nous devons également déclarer ces variables afin de pouvoir les utiliser. Nous faisons cela dans le fichier variables.tf. Ouvrez ou créez ce fichier.

variable "api_key" {}
variable "secret_key" {}
variable "project" {}

variable "api_url" {
    default = "https://compute-east.cloud.ca/client/api"
}
variable "zone" {
    default = "QC-1"
}
variable "vpc_offering" {
    default = "Default VPC offering"
}
variable "disk_offering" {
    default = "20GB - 100 IOPS Min."
}
variable "compute_offering" {
    default = "2vCPU.2GB"
}
variable "network_offering" {
    default = "DefaultIsolatedNetworkOfferingForVpcNetworks"
}
variable "compute_template" {
    default = "CoreOS Stable"
}

Ce fichier déclare chaque variable qui sera utilisable dans la configuration. Vous pouvez voir que certaines de ces variables contiennent la clef "default" qui permet de définir une valeur par défaut qui en l'occurence fonctionne sur cloud.ca. Ces valeurs par défaut peuvent être remplacées par d'autres dans le fichier terraform.tfvars. Ouvrez ce fichier ou créez le.

# cloudstack provider
api_key = "YOUR_API_KEY"
secret_key = "YOUR_SECRET_KEY"

# project
project = "YOUR_PROJECT"

Vous pouvez voir que les valeurs pour api_key, secret_key et project sont définies ici. Ces informations peuvent être trouvées sur cloud.ca dans le coin en haut à droite quand vous êtes connectés. Cliquez sur "Clés D'API", sélectionnez l'API de service "Compute - East" et l'environnement que vous voulez utiliser.

Nous avons maintenant toutes les informations nécessaires pour se connecter à cloud.ca avec Terraform. Nous pouvons maintenant configurer des ressources. Ouvrez ou créez main.tf.

resource "cloudstack_vpc" "my_own_private_cloud" {
    name = "my_own_private_cloud"
    cidr = "10.10.0.0/22"
    vpc_offering = "${var.vpc_offering}"
    zone = "${var.zone}"
    project = "${var.project}"
}

resource "cloudstack_network_acl" "acl_web" {
    name = "acl_web"
    vpc = "${cloudstack_vpc.my_own_private_cloud.id}"
}

resource "cloudstack_network_acl_rule" "acl_web_tier" {
  aclid = "${cloudstack_network_acl.acl_web.id}"

  rule {
    action = "allow"
    source_cidr = "0.0.0.0/0"
    protocol = "all"
    traffic_type = "egress"
  }
  rule {
    action = "allow"
    source_cidr = "0.0.0.0/0"
    protocol = "all"
    traffic_type = "ingress"
  }
}

Ce fichier défini un VPC et une ACL qui sera appliquée à un réseau. Dans web_tier.tf, on crée le réseau (qui utilise l'ACL définie dans le fichier précédent), une machine virtuelle appellée wordpress01 et une adresse IP avec une règle de redirection de port.

resource "cloudstack_network" "web_tier" {
    name = "web_tier"
    cidr = "10.10.1.0/24"
    network_offering = "${var.network_offering}"
    vpc = "${cloudstack_vpc.my_own_private_cloud.id}"
    zone = "${var.zone}"
    aclid = "${cloudstack_network_acl.acl_web.id}"
    project = "${var.project}"
}

resource "cloudstack_instance" "wordpress01" {
    name = "wordpress01"
    service_offering= "${var.compute_offering}"
    network = "${cloudstack_network.web_tier.id}"
    ipaddress = "10.10.1.10"
    template = "${var.compute_template}"
    zone = "${var.zone}"
    project = "${var.project}"
    user_data = "${file(\"cloudinit_wordpress\")}"
}

resource "cloudstack_ipaddress" "wordpress01" {
    vpc = "${cloudstack_vpc.my_own_private_cloud.id}"
    project = "${var.project}"
}

resource "cloudstack_port_forward" "wordpress01" {
  ipaddress = "${cloudstack_ipaddress.wordpress01.id}"

  forward {
    protocol = "tcp"
    private_port = 22
    public_port = 22
    virtual_machine = "${cloudstack_instance.wordpress01.id}"
  }
}

output "wordpress01" {
    value = "${cloudstack_ipaddress.wordpress01.ipaddress}"
}

Avec la redirection du port 22 et l'ACL configurée pour autoriser toutes les connections entrantes, nous avons simplement besoin de fournir une clef SSH à la machine virtuelle pour permettre de s'y connecter à distance. Cette partie est prise en charge dans le fichier cloudinit_wordpress qui est fourni à la machine virtuelle en tant que donnée utilisateur et permet de configurer celle-ci au démarrage. cloudinit_wordpress contient une instruction qui permet d'ajouter la clef spécifiée à l'utilisateur core (l'utilisateur par défaut de notre template CoreOS). La clef SSH publique copiée correspond au fichier core.pub qui est disponible dans le répertoire git mais vous pouvez également génerer vous même une nouvelle paire de clefs.

#cloud-config

ssh_authorized_keys:
    - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDPn/jt5vTyRNpvpg/Eze+HOuOBXh1ygpqfadpUgcHfNMLslU+Tvr4WFvx/mCmMxJ6HyFCyGZ8gSsbKgRYAi8x/DD1/VI7Zj/c1b9oYhwL/zucQwCDubiKxtS1JEqOcF19tYeuJtYl/HcsdtnQ3cdu4wtTM6ZNowvM27G7PKvX1o0D/M1jfIh1Cibc8oxmoDl0Il+MUm9s6JtxDLnhI7SDwXtBPWe6zqORw9BCCDIHhaIzI6qITpdf4hhc3fhMN6bWh/ExuFbNRMNK4e3SZMN9sSgEGPvYvbUb2/Z09bTk7R9HCEz9u637p7aB6UHlJyOJWOZT7pXpLOjWi89vGUOn/ vilisseranen@pegasus

Notre première configuration est maintenant prête. Terraform founit plusieurs outils pour travailler avec ses configurations. Dans le dossier contenant votre configuration vous pouvez faire :

  • $ terraform plan - qui va afficher les changemets qui seront apportés à votre environnement
  • $ terraform refresh - qui va recharger l'état de votre environnement géré par Terraform par rapport à ce qui existe vraiment dans cloud.ca
  • $ terraform apply - qui va appliquer la configuration et réellement créer des ressources
  • $ terraform destroy - qui va détruire les ressources crées par Terraform (mais seulement celles-ci)

Faites un $terraform plan pourvoir ce qui va être crée dans votre environnement. Si vous aimez ce que vous voyez, créez réellement les ressources avec un $terraform apply. Une fois que la configuration est déployée, vous pouvez vous connecter à la machine avec un ssh -i core@PUBLIC_IP avec la clef privée du répertoire git (disponible ici : https://raw.githubusercontent.com/vilisseranen/terraform_cloudca/master/core).

Vous pouvez aussi examiner les relations de dépendances entre les différents éléments de votre configuration en faisant un $ terraform graph | dot -Tpng > graph.png. Ouvrez l'image graph.png, vous verrez que Terraform est assez intelligent pour déterminer un ordre correct dans lequel les ressources doivent être créées.

L'environnement que vous venez de déployer ressemble à ce qui est présenté sur l'image ci-dessous. Le VPC est constitué d'un routeur virtuel et d'un réseau (web_tier) avec CIDR 10.10.1.0/24. Une machine virtuelle est attachée à ce réseau et possède l'IP 10.10.1.10. L'IP publique (non représentée sur le schéma) part du routeur et redirige le port 22 sur la machine virtuelle.

blog_post_-_step1.png

Application multi-tier

Pourquoi ne pas essayer quelque chose d'un peu plus complet ? Nous allons ajouter un nouveau tier avec une machine virtuelle. Comme vous l'avez sûrement deviné, la première machine virtuelle que nous avons crée auparavant va héberger un serveur web Wordpress alors que la seconde machine va héberger la base de données (DB).

Avec git, placez-vous sur le tag "step2" (git checkout step2).

Si vous ouvez le fichier main.tf, vous verrez que nous définissons une nouvelle ACL permettant encore une fois tout le traffic entrant ainsi que sortant. Il aurait été possible de réutiliser l'ACL que nous avons crée précédemment, mais nous allons les différencier dans la suite de ce tutoriel c'est pourquoi nous avons crée une nouvelle ressource de type ACL.

resource "cloudstack_vpc" "my_own_private_cloud" {
    name = "my_own_private_cloud"
    cidr = "10.10.0.0/22"
    vpc_offering = "${var.vpc_offering}"
    zone = "${var.zone}"
    project = "${var.project}"
}

resource "cloudstack_network_acl" "acl_web" {
    name = "acl_web"
    vpc = "${cloudstack_vpc.my_own_private_cloud.id}"
}

resource "cloudstack_network_acl_rule" "acl_web_tier" {
  aclid = "${cloudstack_network_acl.acl_web.id}"

  rule {
    action = "allow"
    source_cidr = "0.0.0.0/0"
    protocol = "all"
    traffic_type = "egress"
  }
  rule {
    action = "allow"
    source_cidr = "0.0.0.0/0"
    protocol = "all"
    traffic_type = "ingress"
  }
}

resource "cloudstack_network_acl" "acl_db" {
    name = "acl_db"
    vpc = "${cloudstack_vpc.my_own_private_cloud.id}"
}

resource "cloudstack_network_acl_rule" "acl_db_tier" {
  aclid = "${cloudstack_network_acl.acl_db.id}"


  rule {
    action = "allow"
    source_cidr = "0.0.0.0/0"
    protocol = "all"
    traffic_type = "egress"
  }
  rule {
    action = "allow"
    source_cidr = "0.0.0.0/0"
    protocol = "all"
    traffic_type = "ingress"
  }
}

Ouvrez ou créez le fichier : db_tier.tf. Ce fichier déclare un nouveau tier (réseau 10.10.2.0/24) et une machine virtuelle.

resource "cloudstack_network" "db_tier" {
    name = "db_tier"
    cidr = "10.10.2.0/24"
    network_offering = "${var.network_offering}"
    vpc = "${cloudstack_vpc.my_own_private_cloud.id}"
    zone = "${var.zone}"
    aclid = "${cloudstack_network_acl.acl_db.id}"
    project = "${var.project}"
}

resource "cloudstack_instance" "mysql01" {
    name = "mysql01"
    service_offering= "${var.compute_offering}"
    network = "${cloudstack_network.db_tier.id}"
    ipaddress = "10.10.2.10"
    template = "${var.compute_template}"
    zone = "${var.zone}"
    project = "${var.project}"
    user_data = "${file(\"cloudinit_db\")}"
}

resource "cloudstack_ipaddress" "mysql01" {
    vpc = "${cloudstack_vpc.my_own_private_cloud.id}"
    project = "${var.project}"
}

resource "cloudstack_port_forward" "mysql01" {
  ipaddress = "${cloudstack_ipaddress.mysql01.id}"

  forward {
    protocol = "tcp"
    private_port = 22
    public_port = 22
    virtual_machine = "${cloudstack_instance.mysql01.id}"
  }
}

output "mysql01" {
    value = "${cloudstack_ipaddress.mysql01.ipaddress}"
}

Cette machine a son propre fichier cloudinit (cloudinit_db). Pour l'instant, cette configuration ajoute uniquement la clef SSH (et encore une fois, il aurait été possible de réutiliser le même fichier cloudinit que pour la VM wordpress01 mais nous avons choisi de créer un nouveau fichier pour la même raison que nous avons créer une nouvelle ACL).

#cloud-config

ssh_authorized_keys:
    - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDPn/jt5vTyRNpvpg/Eze+HOuOBXh1ygpqfadpUgcHfNMLslU+Tvr4WFvx/mCmMxJ6HyFCyGZ8gSsbKgRYAi8x/DD1/VI7Zj/c1b9oYhwL/zucQwCDubiKxtS1JEqOcF19tYeuJtYl/HcsdtnQ3cdu4wtTM6ZNowvM27G7PKvX1o0D/M1jfIh1Cibc8oxmoDl0Il+MUm9s6JtxDLnhI7SDwXtBPWe6zqORw9BCCDIHhaIzI6qITpdf4hhc3fhMN6bWh/ExuFbNRMNK4e3SZMN9sSgEGPvYvbUb2/Z09bTk7R9HCEz9u637p7aB6UHlJyOJWOZT7pXpLOjWi89vGUOn/ vilisseranen@pegasus

blog_post_-_step2.png

Déploiement de l'application

La dernière étape de ce guide consiste à déployer l'application sur les machines virtuelles. Nous allons :

  • Déployer Wordpress dans un conteneur Docker dans le tier web
  • Déployer MySQL dans un conteneur Docker dans le tier DB
  • Changer les IP publiques et les redirections de ports
  • Mettre en place des ACL adéquates

Avec git, placez-vous sur le tag "step3" (git checkout step3).

Commençons par le container Wordpress. La seule chose à modifier est la configuration du fichier cloudinit. Ouvrez le fichier cloudinit_wordpress.

#cloud-config

coreos:
  units:
    - name: "docker-wordpress.service"
      command: "start"
      content: |
        [Unit]
          Description=Wordpress
          After=docker.service
          Requires=docker.service

          [Service]
          TimeoutStartSec=0
          ExecStartPre=-/usr/bin/docker kill wordpress
          ExecStartPre=-/usr/bin/docker rm wordpress
          ExecStartPre=/usr/bin/docker pull wordpress
          ExecStart=/usr/bin/docker run --name wordpress01 -p 80:80 -p 443:443 -e WORDPRESS_DB_HOST=10.10.2.10 -e WORDPRESS_DB_USER=wordpress -e WORDPRESS_DB_PASSWORD=myWordpressPassword wordpress

Nous avons retiré la clef SSH (on ne pourra donc plus se connecter sur la machine) et la configuration pour déployer Wordpress a été ajouté (vous pouvez trouver les informations sur comment faire ça sur https://coreos.com/os/docs/latest/cloud-config.html et valider le fichier YAML sur https://coreos.com/validate/). Nous exposons les ports 80 et 443 du container afin de pouvoir y accéder. Nous faisons la même chose avec le fichier cloudinit_db sauf que nous déployons un conteneur MySQL et exposons le port 3306.

#cloud-config

coreos:
  units:
    - name: "docker-mysql.service"
      command: "start"
      content: |
        [Unit]
          Description=MySQL
          After=docker.service
          Requires=docker.service

          [Service]
          TimeoutStartSec=0
          ExecStartPre=-/usr/bin/docker kill mysql
          ExecStartPre=-/usr/bin/docker rm mysql
          ExecStartPre=/usr/bin/docker pull mysql
          ExecStart=/usr/bin/docker run --name mysql -p 3306:3306 -e MYSQL_USER=wordpress -e MYSQL_PASSWORD=myWordpressPassword -e MYSQL_DATABASE=wordpress -e MYSQL_ROOT_PASSWORD=aStrongRootPassword mysql

Dans le fichier web_tier.tf, on modifie la redirection pour retirer le port 22 et ajouter les ports 80 et 443 vers notre machine Wordpress.

resource "cloudstack_network" "web_tier" {
    name = "web_tier"
    cidr = "10.10.1.0/24"
    network_offering = "${var.network_offering}"
    vpc = "${cloudstack_vpc.my_own_private_cloud.id}"
    zone = "${var.zone}"
    aclid = "${cloudstack_network_acl.acl_web.id}"
    project = "${var.project}"
}

resource "cloudstack_instance" "wordpress01" {
    name = "wordpress01"
    service_offering= "${var.compute_offering}"
    network = "${cloudstack_network.web_tier.id}"
    ipaddress = "10.10.1.10"
    template = "${var.compute_template}"
    zone = "${var.zone}"
    project = "${var.project}"
    user_data = "${file(\"cloudinit_wordpress\")}"
    depends_on = [ "cloudstack_instance.mysql01" ]
}

resource "cloudstack_ipaddress" "wordpress01" {
    vpc = "${cloudstack_vpc.my_own_private_cloud.id}"
    project = "${var.project}"
}

resource "cloudstack_port_forward" "wordpress01" {
  ipaddress = "${cloudstack_ipaddress.wordpress01.id}"

  forward {
    protocol = "tcp"
    private_port = 80
    public_port = 80
    virtual_machine = "${cloudstack_instance.wordpress01.id}"
  }
  forward {
    protocol = "tcp"
    private_port = 443
    public_port = 443
    virtual_machine = "${cloudstack_instance.wordpress01.id}"
  }
}

output "wordpress01" {
    value = "${cloudstack_ipaddress.wordpress01.ipaddress}"
}

Dans db_tier.tf, on retire complètement l'IP publique car cette machine ne devrait pas être accessible de l'extérieur du réseau.

resource "cloudstack_network" "db_tier" {
    name = "db_tier"
    cidr = "10.10.2.0/24"
    network_offering = "${var.network_offering}"
    vpc = "${cloudstack_vpc.my_own_private_cloud.id}"
    zone = "${var.zone}"
    aclid = "${cloudstack_network_acl.acl_db.id}"
    project = "${var.project}"
}

resource "cloudstack_instance" "mysql01" {
    name = "mysql01"
    service_offering= "${var.compute_offering}"
    network = "${cloudstack_network.db_tier.id}"
    ipaddress = "10.10.2.10"
    template = "${var.compute_template}"
    zone = "${var.zone}"
    project = "${var.project}"
    user_data = "${file(\"cloudinit_db\")}"
}

Ouvrez le fichier main.tf pour voir les changements apportés au ACL.

resource "cloudstack_vpc" "my_own_private_cloud" {
    name = "my_own_private_cloud"
    cidr = "10.10.0.0/22"
    vpc_offering = "${var.vpc_offering}"
    zone = "${var.zone}"
    project = "${var.project}"
}

resource "cloudstack_network_acl" "acl_web" {
    name = "acl_web"
    vpc = "${cloudstack_vpc.my_own_private_cloud.id}"
}

resource "cloudstack_network_acl_rule" "acl_web_tier" {
  aclid = "${cloudstack_network_acl.acl_web.id}"

  rule {
    action = "allow"
    source_cidr = "0.0.0.0/0"
    protocol = "tcp"
    ports = [ "53", "80", "123", "443" ]
    traffic_type = "egress"
  }
  rule {
    action = "allow"
    source_cidr = "0.0.0.0/0"
    protocol = "udp"
    ports = [ "53", "123" ]
    traffic_type = "egress"
  }
  rule {
    action = "allow"
    source_cidr = "${cloudstack_instance.mysql01.ipaddress}/32"
    protocol = "tcp"
    ports = [ "3306" ]
    traffic_type = "egress"
  }
  rule {
    action = "allow"
    source_cidr = "0.0.0.0/0"
    protocol = "icmp"
    traffic_type = "egress"
  }
  rule {
    action = "allow"
    source_cidr = "0.0.0.0/0"
    protocol = "tcp"
    ports = [ "80", "443" ]
    traffic_type = "ingress"
  }
  rule {
    action = "allow"
    source_cidr = "0.0.0.0/0"
    protocol = "icmp"
    traffic_type = "ingress"
  }
}

resource "cloudstack_network_acl" "acl_db" {
    name = "acl_db"
    vpc = "${cloudstack_vpc.my_own_private_cloud.id}"
}

resource "cloudstack_network_acl_rule" "acl_db_tier" {
  aclid = "${cloudstack_network_acl.acl_db.id}"

  rule {
    action = "allow"
    source_cidr = "0.0.0.0/0"
    protocol = "tcp"
    ports = [ "53", "80", "123", "443" ]
    traffic_type = "egress"
  }
  rule {
    action = "allow"
    source_cidr = "0.0.0.0/0"
    protocol = "udp"
    ports = [ "53", "123" ]
    traffic_type = "egress"
  }
  rule {
    action = "allow"
    source_cidr = "${cloudstack_instance.wordpress01.ipaddress}/32"
    protocol = "tcp"
    ports = [ "3306" ]
    traffic_type = "ingress"
  }
  rule {
    action = "allow"
    source_cidr = "0.0.0.0/0"
    protocol = "icmp"
    traffic_type = "egress"
  }
  rule {
    action = "allow"
    source_cidr = "0.0.0.0/0"
    protocol = "icmp"
    traffic_type = "ingress"
  }
}

Lors des étapes précédentes, nous avions autorisé toutes les connections entrantes et sortantes ce qui n'est pas très sécurisé. Nous allons maintenant restreindre les connections sortantes à DNS, NTP et ICMP et les connections entrantes à ICMP, ceci sur chaque tier. De plus, nous ajoutons les règles au tier web pour autoriser les connections entrantes HTTP (port 80) et HTTPS (port 443) ainsi que la connection sortantes 3306 restreintes à l'IP de la machine qui contient la base de données. Nous ajoutons une règle au tier db pour autoriser les connections MySQL depuis la machine Wordpress vers la machine MySQL.

Note:Si vous avez suivi ce guide étape par étape et que vous appliquez maintenant cette configuration, il se peut que vous rencontriez une erreur. Ceci est du au fait que Terraform doit détruire puis recréer la machine virtuelle pour modifier les données utilisateur (les fichiers cloudinit). Cependant, les machines virtuelles ne sont pas détruites instantanément quand on utilise l'API de CloudStack sur cloud.ca. À la place, les machines dont on demande la destruction sont mise pendant quelques minutes dans un état détruit duquel elles peuvent être de nouveau allumées si jamais la destruction était accidentelle. Si Terraform vous prévient qu'il n'est pas en mesure d'instantier une machine car une machine avec le même nom ou la même IP existe déjà sur le réseau, connectez-vous avec votre compte sur cloud.ca et purgez les machines qui posent problème.

blog_post_-_step3

Quand la configuration est déployée avec terraform apply, connectez-vous avec votre navigateur sur l'IP que vous donne Terraform. Vous pouvez commencer à travailler sur votre Wordpress !

L'environnement est maintenant complètement déployé et utilisable. Évidemment, il est toujours possible de l'améliorer. Voici quelques idées :

  • Exposer d'autres ports utiles comme SMTP
  • Utiliser les possibilités qu'offre cloud.ca en termes de répartition de charge (load-balancing) pour permettre d'utiliser plusieurs instances de Wordpress en simultané
  • Déployer un cluster MySQL

 

À propos de l'auteur

clement.jpg

Clément à récemment obtenu un diplôme de maîtrise en ingénierie logicielle à l'école de technologie supérieure de Montréal. Son projet de fin d'étude était orienté sur les contraintes de haute disponibilité et de tolérance aux fautes dans OpenStack. Chez CloudOps, il fait partie de l'équipe qui s'occupe de cloud.ca et ses responsabilités comprennent la gestion de l'infrastructure existante ainsi que l'implantation de nouveaux services. Pour rejoindre Clément, écrivez-lui: ccontini@cloudops.com