Instancier un cluster de machines sur Scaleway avec Terraform


18/04/2017 terraform scaleway infrastructure as code iac

Objectifs

Pour pouvoir tester différentes solutions, il peut être utile de pouvoir créer rapidement une infrastructure chez un fournisseur tiers. Pour cela nous allons nous appuyer sur la solution Terraform, dont l’objectif est de pouvoir gérer une insfrastructure via du code qui permet de décrire cette infrastructure et les règles associées (dans la tendance Infrastructure as Code).

Cela se fera chez Scaleway mais cela peut se faire chez d’autres fournisseurs tant qu’un provider Terraform existe.

Nous allons nous appuyer sur l’intégration Terraform/Scaleway pour créer un cluster de 3 machines.

Pré-requis

  • Un compte chez Scaleway
  • Avoir créé un token (via Account > Credentials)
  • Avoir terraform sur son PC ; version 0.9.2 dans mon cas.

Créer un ficheir cluster-3.tf dans un dossier vide. Il est important qu’il soit vide car Terraform prend en compte tous les fichiers .tf qu’il rencontre dans le répertoire courant.

Il faut commencer par déclarer notre provider ; je fournis les infos de key & token, ainsi que le datacenter où je souhaite lancer mon cluster :

provider "scaleway" {
  organization = "<access key>"
  token        = "<token>"
  region       = "par1"
}

Je déclare ensuite des data, que l’on peut assimiler à des variables à ce stade :

  • le bootscript est une option de démarrage, notamment au niveau du noyau, d’un serveur chez Scaleway,
  • l’image est l’OS voulu, ici une version Ubuntu Xenial en 64 bits.
data "scaleway_bootscript" "latest" {
  architecture = "x86_64"
  name_filter  = "stable"
}

data "scaleway_image" "ubuntu" {
  architecture = "x86_64"
  name         = "Ubuntu Xenial"
}

Ensuite, je définis une politique de sécurité en 2 étapes :

  • Je déclare la politique de sécurité,
  • J’y ajoute les différentes règles : ici on autorise par défaut ssh, http et https.
resource "scaleway_security_group" "cluster_default" {
  name        = "cluster_default"
  description = "Allow SSH, HTTP, HTTPS traffic"
}

resource "scaleway_security_group_rule" "ssh_accept" {
  security_group = "${scaleway_security_group.cluster_default.id}"
  action         = "accept"
  direction      = "inbound"
  ip_range       = "0.0.0.0/0"
  protocol       = "TCP"
  port           = 22
}

resource "scaleway_security_group_rule" "http_accept" {
  security_group = "${scaleway_security_group.cluster_default.id}"
  action         = "accept"
  direction      = "inbound"
  ip_range       = "0.0.0.0/0"
  protocol       = "TCP"
  port           = 80
}

resource "scaleway_security_group_rule" "https_accept" {
  security_group = "${scaleway_security_group.cluster_default.id}"
  action         = "accept"
  direction      = "inbound"
  ip_range       = "0.0.0.0/0"
  protocol       = "TCP"
  port           = 443
}

Je pourrais donc me connecter en ssh, http et https sur l’ensemble de mon cluster une fois qu’il sera initialisé.

Je déclare ensuite une IP et je l’associe au serveur qui l’utilisera :

resource "scaleway_ip" "cluster-master_ip" {
  server = "${scaleway_server.cluster-master.id}"
}

Enfin, je déclare mon serveur qui va reprendre l’ensemble des informations précédentes, ainsi que quelques informations spécifique comme le type d’instance (type) ou encore le volume additionnel (volume) attaché au serveur. Il y a des directives scaleway_volume ou scaleway_volume_attachment mais l’API Scaleway ne permet pas de les utiliser pour ce type de serveur.

resource "scaleway_server" "cluster-master" {
  name           = "cluster-master"
  image          = "${data.scaleway_image.ubuntu.id}"
  type           = "VC1M"
  bootscript     = "${data.scaleway_bootscript.latest.id}"
  security_group = "${scaleway_security_group.cluster_default.id}"
  
  volume {
    size_in_gb = 50
    type       = "l_ssd"
  }
}

Sur le même modèle, nous allons instancier les 2 autres noeuds de notre cluster :

resource "scaleway_ip" "cluster-node1_ip" {
  server = "${scaleway_server.cluster-node1.id}"
}

resource "scaleway_server" "cluster-node1" {
  name           = "cluster-node1"
  image          = "${data.scaleway_image.ubuntu.id}"
  type           = "VC1M"
  bootscript     = "${data.scaleway_bootscript.latest.id}"
  security_group = "${scaleway_security_group.cluster_default.id}"
  
  volume {
    size_in_gb = 50
    type       = "l_ssd"
  }
}

resource "scaleway_ip" "cluster-node2_ip" {
  server = "${scaleway_server.cluster-node2.id}"
}

resource "scaleway_server" "cluster-node2" {
  name           = "cluster-node2"
  image          = "${data.scaleway_image.ubuntu.id}"
  type           = "VC1M"
  bootscript     = "${data.scaleway_bootscript.latest.id}"
  security_group = "${scaleway_security_group.cluster_default.id}"
  
  volume {
    size_in_gb = 50
    type       = "l_ssd"
  }
}

Vous pouvez déjà valider qu’il n’y a pas d’erreur de syntaxe avec

↪ terraform fmt
cluster-3.tf

Si pas d’erreur, alors il ne retourne que le nom du fichier ou rien si commande déjà exécutée.

Ensuite vous pouvez regarder ce qu’il va se passer avec terraform plan. Cela vous sort un diff de ce qui va être réalisé (ou changé s’il y a mise à jour du plan)

Dans notre cas :

↪ terraform plan

[...]

+ scaleway_ip.cluster-master_ip
    ip:     "<computed>"
    server: "${scaleway_server.cluster-master.id}"

+ scaleway_ip.cluster-node1_ip
    ip:     "<computed>"
    server: "${scaleway_server.cluster-node1.id}"

+ scaleway_ip.cluster-node2_ip
    ip:     "<computed>"
    server: "${scaleway_server.cluster-node2.id}"

+ scaleway_security_group.cluster_default
    description: "Allow SSH, HTTP, HTTPS traffic"
    name:        "cluster_default"

+ scaleway_security_group_rule.http_accept
    action:         "accept"
    direction:      "inbound"
    ip_range:       "0.0.0.0/0"
    port:           "80"
    protocol:       "TCP"
    security_group: "${scaleway_security_group.cluster_default.id}"

+ scaleway_security_group_rule.https_accept
    action:         "accept"
    direction:      "inbound"
    ip_range:       "0.0.0.0/0"
    port:           "443"
    protocol:       "TCP"
    security_group: "${scaleway_security_group.cluster_default.id}"

+ scaleway_security_group_rule.ssh_accept
    action:         "accept"
    direction:      "inbound"
    ip_range:       "0.0.0.0/0"
    port:           "22"
    protocol:       "TCP"
    security_group: "${scaleway_security_group.cluster_default.id}"

+ scaleway_server.cluster-master
    bootscript:          "8fd15f37-c176-49a4-9e1d-10eb912942ea"
    enable_ipv6:         "false"
    image:               "89457135-d446-41ba-a8df-d53e5bb54710"
    name:                "cluster"
    private_ip:          "<computed>"
    public_ip:           "<computed>"
    public_ipv6:         "<computed>"
    security_group:      "${scaleway_security_group.cluster_default.id}"
    state:               "<computed>"
    state_detail:        "<computed>"
    type:                "VC1M"
    volume.#:            "1"
    volume.0.size_in_gb: "50"
    volume.0.type:       "l_ssd"
    volume.0.volume_id:  "<computed>"

+ scaleway_server.cluster-node1
    bootscript:          "8fd15f37-c176-49a4-9e1d-10eb912942ea"
    enable_ipv6:         "false"
    image:               "89457135-d446-41ba-a8df-d53e5bb54710"
    name:                "cluster-node1"
    private_ip:          "<computed>"
    public_ip:           "<computed>"
    public_ipv6:         "<computed>"
    security_group:      "${scaleway_security_group.cluster_default.id}"
    state:               "<computed>"
    state_detail:        "<computed>"
    type:                "VC1M"
    volume.#:            "1"
    volume.0.size_in_gb: "50"
    volume.0.type:       "l_ssd"
    volume.0.volume_id:  "<computed>"

+ scaleway_server.cluster-node2
    bootscript:          "8fd15f37-c176-49a4-9e1d-10eb912942ea"
    enable_ipv6:         "false"
    image:               "89457135-d446-41ba-a8df-d53e5bb54710"
    name:                "cluster-node2"
    private_ip:          "<computed>"
    public_ip:           "<computed>"
    public_ipv6:         "<computed>"
    security_group:      "${scaleway_security_group.cluster_default.id}"
    state:               "<computed>"
    state_detail:        "<computed>"
    type:                "VC1M"
    volume.#:            "1"
    volume.0.size_in_gb: "50"
    volume.0.type:       "l_ssd"
    volume.0.volume_id:  "<computed>"

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

Pour provisionner l’infrastructure, il ne vous reste plus qu’à faire terraform apply.

↪ terraform apply
data.scaleway_bootscript.latest: Refreshing state...
data.scaleway_image.ubuntu: Refreshing state...
scaleway_security_group.cluster_default: Creating...
  description: "" => "Allow SSH, HTTP, HTTPS traffic"
  name:        "" => "cluster_default"
scaleway_security_group.cluster_default: Creation complete (ID: 06b93dc3-...2beffd62)
scaleway_security_group_rule.ssh_accept: Creating...
  action:         "" => "accept"
  direction:      "" => "inbound"
  ip_range:       "" => "0.0.0.0/0"
  port:           "" => "22"
  protocol:       "" => "TCP"
  security_group: "" => "06b93dc3-9a8b-4022-80bf-e9172beffd62"
scaleway_server.cluster-master: Creating...

[...]

Apply complete! Resources: 10 added, 0 changed, 0 destroyed.

Vous pouvez consulter l’ensemble des informations de votre infrastructure avec la commande terraform show.

Il ne vous reste plus qu’à vous connecter sur vos serveurs, faire les actions dont vous avez besoin.

Supposons que nous ayons besoin d’un 4ème noeud, il suffirait de rajouter dans le fichier cluster-3.tf :

resource "scaleway_ip" "cluster-node3_ip" {
  server = "${scaleway_server.cluster-node3.id}"
}

resource "scaleway_server" "cluster-node3" {
  name           = "cluster-node3"
  image          = "${data.scaleway_image.ubuntu.id}"
  type           = "VC1M"
  bootscript     = "${data.scaleway_bootscript.latest.id}"
  security_group = "${scaleway_security_group.cluster_default.id}"

  volume {
    size_in_gb = 50
    type       = "l_ssd"
  }
}

On peut valider notre changement avec terraform plan :

↪ terraform plan

[...]

+ scaleway_ip.cluster-node3_ip
    ip:     "<computed>"
    server: "${scaleway_server.cluster-node3.id}"

+ scaleway_server.cluster-node3
    bootscript:          "8fd15f37-c176-49a4-9e1d-10eb912942ea"
    enable_ipv6:         "false"
    image:               "89457135-d446-41ba-a8df-d53e5bb54710"
    name:                "cluster-node3"
    private_ip:          "<computed>"
    public_ip:           "<computed>"
    public_ipv6:         "<computed>"
    security_group:      "06b93dc3-9a8b-4022-80bf-e9172beffd62"
    state:               "<computed>"
    state_detail:        "<computed>"
    type:                "VC1M"
    volume.#:            "1"
    volume.0.size_in_gb: "50"
    volume.0.type:       "l_ssd"
    volume.0.volume_id:  "<computed>"


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

Puis de mettre à jour le cluster avec terraform apply

↪ terraform apply

[...]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

Enfin, comme toutes les choses ont une fin, vous pouvez détruire l’ensemble de l’infrastructure créé via terraform destroy.

↪ terraform destroy

[...]

Destroy complete! Resources: 14 destroyed.

J’espère que cette introduction à Terraform vous donnera des idées ; les éditeurs de code modernes fournissent souvent des plugins pour la syntaxe et les snippets terraform. Scaleway reste assez limité en tant que provider terraform. Si vous regardez le provider AWS, vous pouvez interagir avec énormément de services AWS (pour ne pas dire tous) : VPC, EC2, ELB, RDS, etc.

Le Blog

Nous partageons ici notre veille et nos réflexions

Article(s) lié(s)

Nuage de tags

docker kubernetes traefik influxdb ansible elasticsearch kafka postgres grafana timeseries python aws redis sécurité mysql tick cloud ovh terraform warp10 cassandra helm swarm telegraf test timescaledb chronograf docker-compose rancher résilience architecture confluent ksql log machine-learning microservice prometheus ptsm serverless spark angularjs api cert-manager cncf container devops flux git gitlab graphql hashicorp iac java javascript monitoring opensource operator optimisation raspberrypi service-mesh stream windows arm bilan csp cérénit dns documentation elastic gcp hpkp ingress jenkins kafka-streams kapacitor kibana kubedb lambda lean licence maesh maintenance microsoft mobile nginx npm orientdb perspective redhat rest rethinkdb reverse-proxy s3 sauvegarde scaleway sql ssh agile apm automatisation azure bash big-data certificat ci/cd cli cluster containerd continous-delivery continous-integration cookie dashboard deployment diff fluxlang framework gdpr grav hsts http/3 https hypriot hébergement influxace influxdata influxdays istio json k3s lets-encrypt linux load-balancer meetup molecule mongodb percona performance php pip pipeline postgresql reaper registry replication rook rpi rsyslog scale secrets société solr sre systemd tls vault virtualenv vscode vue.js wagtail yarn accessibilité acme akka alerte alibaba amazon-emr anonymisation anthos apache-pulsar ara audit bastion beam beat bigdatahebdo bounded-context branche brigade browser buildkit cahier-des-charges cassandra-reaper cd cdc centralisation-de-logs ceph certificats chart checklist chrome ci cloud-init cloud-native cloud-storage clusterip cnab cockroachdb code codeurs-en-seine confluence conftest consul continous-deployment coreos cors covid19 cqrs crash cron crontab csrf css curl d3.js daemonset data-pipelining data.gouv.fr datacenter dataviz date ddd debezium debian delta deprek8 desktop devoxx distributed-systems dive docker-app docker-hub docker-registry docker-swarm documentdb dokcer données-personnelles draft drop-in déploiement développement-du-site e-commerce ebs ec2 edge elassandra electron elk engineering entreprise ergonomie etcd event-sourcing faas facebook faisabilité falcor feature-policy fedora feed filebeat firebase firefox fish flash flask fleet flink flows fluentd forecast formation foundation frontend fsync fullstack github gitlab-ci gke glacier glowroot google google-cloud-next gpu grid géospatial hacker hadoop haproxy harbor hdfs header html html5 http hue ia iaac ibm immutable incident index influxcloud infrastructure-as-code ingénierie inspec jq jquery jwt k3d k8s k9s kotlin kubeadm kubecon kubectl laravel letsencrypt linky liste-de-diffusion loadbalancer logstash logstatsh loi longhorn mailing-list management mariadb message metallb micro-service mot-de-passe multi-cloud médecine métrique newsletter nodeport nomad nosql null object-storage observabilité opa opendata openebs openmetrics openshit openssh openstack openweb over-engineering packaging pandas partiql password persistent-volume-claim pipenv pod portainer prediction prescience publicité pubsub push pyenv quasardb quay questdb queue quic ram rambleed raml react recaptcha recherche redistimeseries reindex reinvent reliability responsive revocation revue-de-code rgpd rkt rolespec root rpo rto runc rwd safe-harbor scalabilité scanner schema sdk search select serverless-architecture service-account service-worker sha1 sharding shell shipyard sidecar souveraineté-numérique spinnaker spécifications sri ssh-agent ssl stash statistique superset suse sympa syslog-ng test-unitaire tidb tiers timer timezone training travail tsl ubuntu unikernel unit ux vendredi victoria-metrics vie-privée virtualbox virtualisation vitess vm vnc volume voxxeddays vpc warpscript web yubikey

Syndication

Restez informé(s) de notre actualité en vous abonnant au flux du blog (Atom)