Loading search data...

Prendre en main Hashicorp Terraform avec le provider libvirt

Je vous propose ici de découvrir comment utiliser Terraform avec libvirt. Je vous conseille de monter cet environnement de dev qui contient tout ce qu’il faut. Ce billet fait suite à l'introduction à Terraform ou tout est expliqué sur ce produit d’Infrastructure As Code.

Installer le plugin libvirt

Si vous avez pris mon environnement de dev rien à installer, ca fonctionne de suite. Sinon il faudra installer libvirt et mkisofs (on va utiliser cloud-init).

La c’est un peu plus compliqué. En effet libvirt n’est pas fourni par hashicorp mais par un développeur répondant au nom de Duncan Mac-Vicar P.

Dans votre fichier main.tf il suffit d’indiquer ce provider (le code source est ) :

terraform {
  required_providers {
    libvirt = {
      source = "dmacvicar/libvirt"
    }
  }
}

provider "libvirt" {
  # Configuration options
}

On lance la commande terraform init et normalement il va installer ce plugin.

terraform init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of dmacvicar/libvirt...
- Installing dmacvicar/libvirt v0.6.11...
- Installed dmacvicar/libvirt v0.6.11 (self-signed, key ID 96B1FE1A8D4E1EAB)

Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/cli/plugins/signing.html

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

Création de la première machine

On va voir un exemple permettant de créer une machine ubuntu en utilisant cloud-init pour sa configuration.

Faites attention à ce que votre réseau kvm default existe avec le même réseau 192.168.122.1, sinon adaptez le contenu en conséquence :

mkdir test-terraform
cd test-terraform

Les fichiers de configuration

Éditer le fichier main.tf avec ce contenu :

terraform {
  required_providers {
    libvirt = {
      source = "dmacvicar/libvirt"
    }
  }
}

// instance the provider
provider "libvirt" {
  uri = "qemu:///system"
}

// variables that can be overriden
variable "hostname" { default = "test" }
variable "domain" { default = "example.com" }
variable "ip_type" { default = "dhcp" } # dhcp is other valid type
variable "memoryMB" { default = 1024*1 }
variable "cpu" { default = 1 }

// fetch the latest ubuntu release image from their mirrors
resource "libvirt_volume" "os_image" {
  name = "${var.hostname}-os_image"
  pool = "default"
  source = "bionic-server-cloudimg-amd64.img"
  format = "qcow2"
}

// Use CloudInit ISO to add ssh-key to the instance
resource "libvirt_cloudinit_disk" "commoninit" {
          name = "${var.hostname}-commoninit.iso"
          pool = "default"
          user_data      = data.template_cloudinit_config.config.rendered
          network_config = data.template_file.network_config.rendered
}

data "template_file" "user_data" {
  template = file("${path.module}/cloud_init.cfg")
  vars = {
    hostname = var.hostname
    fqdn = "${var.hostname}.${var.domain}"
    public_key = file("~/.ssh/id_rsa.pub")
  }
}

data "template_cloudinit_config" "config" {
  gzip = false
  base64_encode = false
  part {
    filename = "init.cfg"
    content_type = "text/cloud-config"
    content = "${data.template_file.user_data.rendered}"
  }
}

data "template_file" "network_config" {
  template = file("${path.module}/network_config_${var.ip_type}.cfg")
}


// Create the machine
resource "libvirt_domain" "domain-ubuntu" {
  # domain name in libvirt, not hostname
  name = "${var.hostname}"
  memory = var.memoryMB
  vcpu = var.cpu

  disk {
       volume_id = libvirt_volume.os_image.id
  }
  network_interface {
       network_name = "default"
  }

  cloudinit = libvirt_cloudinit_disk.commoninit.id

  # IMPORTANT
  # Ubuntu can hang is a isa-serial is not present at boot time.
  # If you find your CPU 100% and never is available this is why
  console {
    type        = "pty"
    target_port = "0"
    target_type = "serial"
  }

  graphics {
    type = "spice"
    listen_type = "address"
    autoport = "true"
  }
}

terraform {
  required_version = ">= 0.12"
}

output "ips" {
  #value = libvirt_domain.domain-ubuntu
  #value = libvirt_domain.domain-ubuntu.*.network_interface
  # show IP, run 'terraform refresh' if not populated
  value = libvirt_domain.domain-ubuntu.*.network_interface.0.addresses
}

Créer les deux fichiers cloud_init.cfg et network_config_dhcp.cfg

hostname: ${hostname}
fqdn: ${fqdn}
manage_etc_hosts: true
users:
  - name: ubuntu
    sudo: ALL=(ALL) NOPASSWD:ALL
    groups: users, admin
    home: /home/ubuntu
    shell: /bin/bash
    lock_passwd: false
    ssh-authorized-keys:
      - ${public_key}
ssh_pwauth: true
disable_root: false
chpasswd:
  list: |
    ubuntu:linux
  expire: False
packages:
    - qemu-guest-agent
    - python3
bootcmd:
    - [ sh, -c, 'echo $(date) | sudo tee -a /root/bootcmd.log' ]
runcmd:
    - [ sh, -c, 'echo $(date) | sudo tee -a /root/runcmd.log' ]
final_message: "The system is finall up, after $UPTIME seconds"
power_state:
  delay: "+30"
  mode: reboot
  message: Bye Bye
  timeout: 30
  condition: True

le second :

version: 2
ethernets:
  ens3:
    dhcp4: true

Préparation de l’image

Avant de se lancer je vous propose de récupérer l’image et de changer le mot de passe du user root. Cela sera bien pratique pour debugger et surtout d’éviter de télécharger l’image à chaque lancement de la commande terraform apply.

wget https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img
sudo dnf -y install libguestfs-tools
virt-customize -a bionic-server-cloudimg-amd64.img --root-password password:stephan

Provisionning

C’est bon on peut y aller, lancer les commandes suivantes :

terraform init
terraform plan
terraform apply

Répondez yes à la commande apply.

Controlons si la vm a bien été provisionné :

sudo virsh list --all
 Id   Name                                        State
------------------------------------------------------------
 2    staticip   running

Récupérons son adresse IP :

terraform refresh && terraform output ips
libvirt_cloudinit_disk.commoninit: Refreshing state... [id=/var/lib/libvirt/images/staticip-commoninit.iso;bb2ef864-37cf-444a-a8a0-efe6b036dfbb]
libvirt_volume.os_image: Refreshing state... [id=/var/lib/libvirt/images/staticip-os_image]
libvirt_domain.domain-ubuntu: Refreshing state... [id=ac83d6c0-acb0-4465-9f83-01324deae8bd]

Outputs:

ips = [
  tolist([
    "192.168.122.209",
  ]),
]
sudo virsh net-dhcp-leases default
 Expiry Time           MAC address         Protocol   IP address           Hostname   Client ID or DUID
------------------------------------------------------------------------------------------------------------------------------------------------
 2021-11-04 09:20:06   52:54:00:22:05:c4   ipv4       192.168.122.20/24    ubuntu     ff:b5:5e:67:ff:00:02:00:00:ab:11:b2:76:cf:2b:0f:6d:5a:b1

Oui elle bien up ! Maintenant il suffit de lancer se connecter à la vm

ssh ubuntu@192.168.122.209

On va controler le resultat de la commande cloud_init :

cat /var/log/cloud-init-output.log
Cloud-init v. 20.4.1-0ubuntu1~18.04.1 running 'init-local' at Thu, 04 Feb 2021 10:30:15 +0000. Up 7.43 seconds.
Cloud-init v. 20.4.1-0ubuntu1~18.04.1 running 'init' at Thu, 04 Feb 2021 10:30:17 +0000. Up 9.54 seconds.
ci-info: ++++++++++++++++++++++++++++++++++++++Net device info++++++++++++++++++++++++++++++++++++++
....
Processing triggers for ureadahead (0.100.0-21) ...
Processing triggers for systemd (237-3ubuntu10.44) ...
Thu Feb 4 10:30:34 UTC 2021
Cloud-init v. 20.4.1-0ubuntu1~18.04.1 running 'modules:final' at Thu, 04 Feb 2021 10:30:22 +0000. Up 14.06 seconds.
The system is finall up, after 25.96 seconds

Génial !!!

Voila maintenant détruisez la :

Décomissionnement

terraform destroy

Debug

Attention le cloud-init ne se lance que lors du premier resfresh et donc il faudra faire un destroy puis aplly si vous devez le rejouer.

Pour debugger votre cloud-init il suffit d’activer cockpit :

sudo dnf -y install cockpit
sudo systemctl enable --now cockpit.socket
sudo firewall-cmd --add-service=cockpit --permanent
sudo firewall-cmd --reload

Ensuite allez sur votre navigateur, pour accéder à cockpit qui sera disponible via l’url : https://(serverip or hostname):9090/

terraform libvirt cockpit

Il vous suffira ensuite de vous connecter à la machine via la console série avec le compte root. Ensuite les logs de cloud-init se trouve dans le répertoire /var/log

A bientot !

Mots clés :

devops tutorials infra as code terraform

Si vous avez apprécié cet article de blog, vous pouvez m'encourager à produire plus de contenu en m'offrant un café sur Ko-Fi. Vous pouvez aussi passer votre prochaine commande sur amazon, sans que cela ne nous coûte plus cher, via ce lien. Je vous remercie de votre soutien