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.

Syndication

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

Nuage de tags

kubernetes docker timeseries influxdb warp10 grafana traefik elasticsearch kafka postgres python ansible aws sécurité terraform mysql redis telegraf git ovh tick chronograf cloud dashboard docker-compose hashicorp timescaledb cassandra helm podman ptsm swarm test vector flux iot kapacitor rancher timescale cérénit influxdata log machine-learning monitoring postgresql raspberrypi s3 spark sql vscode arm bilan comptabilité confluent devops gitlab gitlab-ci iac java ksql microservice nomad perspective prometheus serverless service-mesh angularjs api bigdatahebdo cli cncf consul container discovery dns flows gcp gke graphql influxace ingress javascript npm opensource operator rook scaleway ssh stream vault warpscript windows architecture cert-manager containerd csp documentation elastic forecast geospatial golang hpkp json kafka-streams kibana kubedb lambda lean licence maesh mariadb microsoft mqtt nginx orientdb quasardb redhat registry rest rethinkdb reverse-proxy rgpd warpstudio wireguard agile anomalie apm arima azure bash big-data ceph certificat challenge cluster co2 continous-delivery continous-integration cookie datatask dataviz dbt deployment diff django edge esp32 facebook fec fluxlang gdpr google-analytics grav hsts http/3 https hypriot ia influxdays istio jq k3s lets-encrypt linux load-balancer longhorn meetup metabase mobile molecule mongodb nosql nvidia openebs openhab openssh ovhcloud pandas parquet percona performance php pip pipeline questdb reaper replication rootless rpi rsyslog runc résilience scale secrets société solr sre systemd tempo timezone tinygo tls virtualenv vitess vue.js wagtail warpfleet yarn accessibilité acme adoptopenjdk agpl akka alerte alertes alibaba amazon-emr amqp anonymisation anthos apache-pulsar ara arduino arrow artefact asgi automation automatisation automl awstats banque bastion beam beat bi bme680 bootstrap bounded-context branche brigade browser buildah buildkit calico cd cdc cdk centos certificats cgroups chart check checklist chrome ci cilium cio circuitpython clever-cloud clickhouse cloud-init cloud-native cloud-storage cloudflare clusterip cnab cni cockroachdb code codeurs-en-seine commit confluence conftest consul-connect context continous-deployment conventional-commit coreos cors covid19 cqrs crash cri cron crontab csi csrf css cto curl d3.js daemonset data data-engineer data-pipelining data.gouv.fr databricks datacenter date date-scientist ddd debezium debian delta deprek8 desktop devoxx dig distributed-systems dive docker-app docker-hub docker-registry dockerfile dockershim documentdb dog dokcer données-personnelles draft dredd drop-in dsi duckdb duration déploiement ebs ec2 elassandra electron elk engineering entreprise etcd euclidia event-sourcing faas falco falcor feature-policy fedora feed filebeat firebase firefox fish flash flask fleet flink flovea fluentd font foundation framework frenchtech frontend fsync fugue fullstack git-filter-repo github gitignore gitpod glacier glowroot goaccess google google-cloud-next gpg gpu grep grid géospatial hacker hadoop haproxy harbor hdfs header holt-winters html html5 http httpx hue iaac ibm iiot immutable incident index indluxdata influxcloud infrastructure-as-code ingénierie inspec jenkins jless jquery jvm jwt k3d k6 k8s k9s kaniko katz kubeadm kubecon kubectl label laravel leap-second lens letsencrypt libssh linky linter liste-de-diffusion lmap loadbalancer logstash logstatsh loi loki lstm mailing-list management matomo maturité mesh mesos message metallb micro-service minio mot-de-passe multi-cloud médecine métrique n8n nebula network newsletter nodejs nodeport notebook notifications nrtsearch null numérique object-storage observability observabilité opa opendata openmetrics openshit openstack openweb opnsense over-engineering packaging partiql password persistent-volume-claim pico pipenv pivot pod portainer portworx prediction prescience privacy-shield production promql prophet prévision psp ptyhon publicité pubsub pulsar push pyenv pérénnité qualité quay queue quic ram rambleed raml react readme recaptcha recherche redistimeseries reindex reinvent reliability remote-execution repository responsive retention-policy revocation revue-de-code rexec rhel rkt robotframework rolespec root rpo rto rust rwd réseau résultat safe-harbor sarima scanner schema scp search select semiconducteur serverless-architecture service service-account service-worker setuptools sftp sha1 shard shard-duration shard-group sharding shell shipyard sidecar singer socket souveraineté-numérique spectre spinnaker sqlite sri ssh-agent ssl stabilité stash statistique stm32 storage sudo superset suse sympa sysdig syslog-ng sérénité task tavern template terracost terrascan test-unitaire thingspeak tidb tiers time timecale timer timestream training transformation travail trésorerie tsfel tsfr tsl ubuntu unikernel unit ux velero vendredi victoria-metrics vie-privée virtualbox virtualisation vm vnc volume voxxeddays vpc vpn wasm workflow yaml yield yq yubikey zip