Ricerca nel sito web

Come effettuare il provisioning delle VM su KVM con Terraform


Se sei un fan di Terraform e KVM, sono sicuro che stavi cercando un modo per eseguire il provisioning di macchine virtuali su KVM in modo automatizzato con Terraform. In questo post del blog ti guiderò attraverso l'installazione del provider KVM Terraform e il suo utilizzo per gestire le istanze in esecuzione sull'hypervisor KVM.

Terraform è un'infrastruttura open source come strumento software di codice creato da HashiCorp. Ti consente di creare, modificare e migliorare l'infrastruttura in modo sicuro e prevedibile. Tutto il codice della tua infrastruttura può essere salvato in un repository Git e sottoposto a versione.

Un provider in Terraform è responsabile del ciclo di vita di una risorsa: creazione, lettura, aggiornamento, eliminazione. Hashicorp dispone di numerosi provider ufficialmente supportati disponibili per l'uso. Sfortunatamente, KVM non è nell'elenco.

Passaggio 1: installa l'hypervisor KVM

Il prerequisito principale per questa configurazione è l'hypervisor KVM. Installa KVM nel tuo sistema Linux facendo riferimento a un articolo pertinente dall'elenco seguente.

  • Come installare KVM su RHEL/CentOS 8
  • Come installare KVM su Fedora
  • Installa l'hypervisor KVM su Ubuntu
  • Come installare KVM su Debian
  • Installa KVM su CentOS 7/Ubuntu/Debian/SLES
  • Installa KVM su Arch Linux/Manjaro

Il servizio KVM (libvird) dovrebbe essere in esecuzione e abilitato per l'avvio all'avvio.

sudo systemctl start libvirtd
sudo systemctl enable libvirtd

Abilita il modulo kernel vhost-net su Ubuntu/Debian.

sudo modprobe vhost_net
echo vhost_net | sudo tee -a /etc/modules

Se desideri generare modelli VM KVM, fai riferimento a:

  • Crea modelli di VM CentOS/Fedora/RHEL su KVM

Passaggio 2: installa Terraform

Dopo aver installato e avviato KVM, esegui l'installazione di Terraform.

  • Come installare Terraform su sistemi Linux

L'installazione di Terraform è molto più semplice. Devi solo scaricare un archivio binario, estrarre e posizionare il file binario in una directory nel tuo ` PATH.

Passaggio 3: installa il provider KVM Terraform

Il fornitore KVM Terraform fornirà l'infrastruttura con KVM di Linux utilizzando libvirt. È gestito da Duncan Mac-Vicar P con altri contributori.

Il provider è disponibile per l'installazione automatica da Terraform Registry. Nel tuo file main.tf, specifica semplicemente la versione che desideri utilizzare:

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

provider "libvirt" {
  # Configuration options
}

Installazione manuale di Fornitore KVM Terraform (Non necessario)

Ma se desideri installarlo manualmente, segui i passaggi forniti in questa sezione.

Installa gli strumenti wget, curl e decomprimi

# Ubuntu / Debian
sudo apt update
sudo apt install wget curl unzip vim

# RHEL Based systems
sudo yum -y install wget curl unzip vim

Inizializza la directory di lavoro Terraform.

$ cd ~
$ terraform init
Terraform initialized in an empty directory!

Crea una directory per archiviare i plugin Terraform.

cd ~/.terraform.d
mkdir plugins

Controlla la pagina delle versioni di Github per i download disponibili.

Installa il provider KVM Terraform su Linux

Sistema Linux a 64 bit:

curl -s https://api.github.com/repos/dmacvicar/terraform-provider-libvirt/releases/latest \
  | grep browser_download_url \
  | grep linux_amd64.zip \
  | cut -d '"' -f 4 \
  | wget -i -
curl -s https://api.github.com/repos/dmacvicar/terraform-provider-libvirt/releases/latest \
  | grep browser_download_url \
  | grep linux_386.zip \
  | cut -d '"' -f 4 \
  | wget -i -

Estrai il file scaricato:

# 64-bit Linux
unzip terraform-provider-libvirt_*_linux_amd64.zip
rm -f terraform-provider-libvirt_*_linux_amd64.zip

# 32-bit Linux
unzip terraform-provider-libvirt_*_linux_386.zip
rm -f terraform-provider-libvirt_*_linux_386.zip

Sposta il file binario terraform-provider-libvirt nella directory ~/.terraform.d/plugins .

mkdir -p ~/.terraform.d/plugins/
mv terraform-provider-libvirt_* ~/.terraform.d/plugins/terraform-provider-libvirt

Passaggio 4: utilizzo di Terraform per eseguire il provisioning delle VM su KVM

Una volta che hai il provider nella directory dei plugin. Crea la cartella dei tuoi progetti Terraform.

mkdir -p ~/projects/terraform
cd ~/projects/terraform

Per l'installazione automatica del provider KVM, definire come di seguito:

$ vim main.tf
terraform {
  required_providers {
    libvirt = {
      source = "dmacvicar/libvirt"
    }
  }
}

provider "libvirt" {
  ## Configuration options
  uri = "qemu:///system"
  #alias = "server2"
  #uri   = "qemu+ssh://[email /system"
}

Successivamente, esegui il comando terraform init per inizializzare l'ambiente:

$ terraform init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of dmacvicar/libvirt...
- Installing dmacvicar/libvirt v0.7.1...
- Installed dmacvicar/libvirt v0.7.1 (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.

Ora possiamo creare il file libvirt.tf per la distribuzione della tua VM su KVM.

vim libvirt.tf

Ecco i contenuti del file che utilizzeremo nel nostro esempio:

# Defining VM Volume
resource "libvirt_volume" "centos7-qcow2" {
  name = "centos7.qcow2"
  pool = "default" # List storage pools using virsh pool-list
  source = "https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud.qcow2"
  #source = "./CentOS-7-x86_64-GenericCloud.qcow2"
  format = "qcow2"
}

# Define KVM domain to create
resource "libvirt_domain" "centos7" {
  name   = "centos7"
  memory = "2048"
  vcpu   = 2

  network_interface {
    network_name = "default" # List networks with virsh net-list
  }

  disk {
    volume_id = "${libvirt_volume.centos7-qcow2.id}"
  }

  console {
    type = "pty"
    target_type = "serial"
    target_port = "0"
  }

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

# Output Server IP
output "ip" {
  value = "${libvirt_domain.centos7.network_interface.0.addresses.0}"
}

Genera e mostra il piano di esecuzione di Terraform

$ terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # libvirt_domain.centos7 will be created
  + resource "libvirt_domain" "centos7" {
      + arch        = (known after apply)
      + disk        = [
          + {
              + block_device = null
              + file         = null
              + scsi         = null
              + url          = null
              + volume_id    = (known after apply)
              + wwn          = null
            },
        ]
      + emulator    = (known after apply)
      + fw_cfg_name = "opt/com.coreos/config"
      + id          = (known after apply)
      + machine     = (known after apply)
      + memory      = 2048
      + name        = "centos7"
      + qemu_agent  = false
      + running     = true
      + vcpu        = 2

      + console {
          + source_host    = "127.0.0.1"
          + source_service = "0"
          + target_port    = "0"
          + target_type    = "serial"
          + type           = "pty"
        }

      + graphics {
          + autoport       = true
          + listen_address = "127.0.0.1"
          + listen_type    = "address"
          + type           = "spice"
        }

      + network_interface {
          + addresses    = (known after apply)
          + hostname     = (known after apply)
          + mac          = (known after apply)
          + network_id   = (known after apply)
          + network_name = "default"
        }
    }

  # libvirt_volume.centos7-qcow2 will be created
  + resource "libvirt_volume" "centos7-qcow2" {
      + format = "qcow2"
      + id     = (known after apply)
      + name   = "centos7.qcow2"
      + pool   = "default"
      + size   = (known after apply)
      + source = "https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud.qcow2"
    }

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

─────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Quindi crea la tua infrastruttura Terraform se lo stato desiderato è confermato corretto.

$ terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # libvirt_domain.centos7 will be created
  + resource "libvirt_domain" "centos7" {
      + arch        = (known after apply)
      + disk        = [
          + {
              + block_device = null
              + file         = null
              + scsi         = null
              + url          = null
              + volume_id    = (known after apply)
              + wwn          = null
            },
        ]
      + emulator    = (known after apply)
      + fw_cfg_name = "opt/com.coreos/config"
      + id          = (known after apply)
      + machine     = (known after apply)
      + memory      = 2048
      + name        = "centos7"
      + qemu_agent  = false
      + running     = true
      + vcpu        = 2

      + console {
          + source_host    = "127.0.0.1"
          + source_service = "0"
          + target_port    = "0"
          + target_type    = "serial"
          + type           = "pty"
        }

      + graphics {
          + autoport       = true
          + listen_address = "127.0.0.1"
          + listen_type    = "address"
          + type           = "spice"
        }

      + network_interface {
          + addresses    = (known after apply)
          + hostname     = (known after apply)
          + mac          = (known after apply)
          + network_id   = (known after apply)
          + network_name = "default"
        }
    }

  # libvirt_volume.centos7-qcow2 will be created
  + resource "libvirt_volume" "centos7-qcow2" {
      + format = "qcow2"
      + id     = (known after apply)
      + name   = "centos7.qcow2"
      + pool   = "default"
      + size   = (known after apply)
      + source = "https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud.qcow2"
    }

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

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

Premere "" per confermare l'esecuzione. Di seguito è riportato l'output dell'esecuzione di Terraform.

libvirt_volume.centos7-qcow2: Creating...
  format: "" => "qcow2"
  name:   "" => "db.qcow2"
  pool:   "" => "default"
  size:   "" => "<computed>"
  source: "" => "./CentOS-7-x86_64-GenericCloud.qcow2"
libvirt_volume.centos7-qcow2: Creation complete after 8s (ID: /var/lib/libvirt/images/db.qcow2)
libvirt_domain.centos7: Creating...
  arch:                             "" => "<computed>"
  console.#:                        "" => "1"
  console.0.target_port:            "" => "0"
  console.0.target_type:            "" => "serial"
  console.0.type:                   "" => "pty"
  disk.#:                           "" => "1"
  disk.0.scsi:                      "" => "false"
  disk.0.volume_id:                 "" => "/var/lib/libvirt/images/db.qcow2"
  emulator:                         "" => "<computed>"
  graphics.#:                       "" => "1"
  graphics.0.autoport:              "" => "true"
  graphics.0.listen_address:        "" => "127.0.0.1"
  graphics.0.listen_type:           "" => "address"
  graphics.0.type:                  "" => "spice"
  machine:                          "" => "<computed>"
  memory:                           "" => "1024"
  name:                             "" => "centos7"
  network_interface.#:              "" => "1"
  network_interface.0.addresses.#:  "" => "<computed>"
  network_interface.0.hostname:     "" => "<computed>"
  network_interface.0.mac:          "" => "<computed>"
  network_interface.0.network_id:   "" => "<computed>"
  network_interface.0.network_name: "" => "default"
  qemu_agent:                       "" => "false"
  running:                          "" => "true"
  vcpu:                             "" => "1"
libvirt_domain.centos7: Creation complete after 0s (ID: e5ee28b9-e1da-4945-9eb0-0cda95255937)

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

Conferma la creazione della VM con il comando virsh.

$ sudo virsh  list
 Id   Name       State
--------------------------
 7    centos7    running

Ottieni l'indirizzo IP dell'istanza.

$ sudo virsh net-dhcp-leases default 
 Expiry Time           MAC address         Protocol   IP address           Hostname   Client ID or DUID
------------------------------------------------------------------------------------------------------------------------------------------------
 2019-03-24 16:11:18   52:54:00:3e:15:9e   ipv4       192.168.122.61/24    -          -
 2019-03-24 15:30:18   52:54:00:8f:8c:86   ipv4       192.168.122.198/24   rhel8      ff:61:69:21:bd:00:02:00:00:ab:11:0e:9c:c6:63:ee:7d:c8:d1

L'IP della mia istanza è 192.168.122.61. Posso eseguire il ping dell'istanza.

$  ping -c 1 192.168.122.61 
PING 192.168.122.61 (192.168.122.61) 56(84) bytes of data.
64 bytes from 192.168.122.61: icmp_seq=1 ttl=64 time=0.517 ms

--- 192.168.122.61 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.517/0.517/0.517/0.000 ms

Per distruggere la tua infrastruttura, esegui:

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

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

Passaggio 5: utilizzo di cloud-init con il provider Terraform Libvirt

La risorsa dell'istanza che abbiamo utilizzato non aveva un'opzione per passare la password dell'utente. Pertanto, se utilizzi un modello cloud che non supporta l'autenticazione della password, non sarai in grado di accedere. Fortunatamente, possiamo utilizzare la risorsa libvirt_cloudinit_disk per passare i dati dell'utente all'istanza.

Crea il file di configurazione Cloud Init.

$ vim cloud_init.cfg
#cloud-config
# vim: syntax=yaml
#
# ***********************
# 	---- for more examples look at: ------
# ---> https://cloudinit.readthedocs.io/en/latest/topics/examples.html
# ******************************
#
# This is the configuration syntax that the write_files module
# will know how to understand. encoding can be given b64 or gzip or (gz+b64).
# The content will be decoded accordingly and then written to the path that is
# provided.
#
# Note: Content strings here are truncated for example purposes.
ssh_pwauth: True
chpasswd:
  list: |
     root: StrongRootPassword
  expire: False

users:
  - name: jmutai # Change me
    ssh_authorized_keys:
      - ssh-rsa AAAAXX #Chageme
    sudo: ['ALL=(ALL) NOPASSWD:ALL']
    shell: /bin/bash
    groups: wheel
  • Questo imposterà la password di root su StrongRootPassword
  • Aggiungi l'utente denominato jmutai con le chiavi SSH pubbliche specificate
  • L'utente verrà aggiunto al gruppo wheel e potrà eseguire comandi sudo senza password.

Modifica libvirt.tf per utilizzare il file di configurazione Cloud init.

# Defining VM Volume
resource "libvirt_volume" "centos7-qcow2" {
  name = "centos7.qcow2"
  pool = "default"
  source = "https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud.qcow2"
  #source = "./CentOS-7-x86_64-GenericCloud.qcow2"
  format = "qcow2"
}

# get user data info
data "template_file" "user_data" {
  template = "${file("${path.module}/cloud_init.cfg")}"
}

# Use CloudInit to add the instance
resource "libvirt_cloudinit_disk" "commoninit" {
  name = "commoninit.iso"
  pool = "default" # List storage pools using virsh pool-list
  user_data      = "${data.template_file.user_data.rendered}"
}

# Define KVM domain to create
resource "libvirt_domain" "centos7" {
  name   = "centos7"
  memory = "2048"
  vcpu   = 2

  network_interface {
    network_name = "default"
  }

  disk {
    volume_id = "${libvirt_volume.centos7-qcow2.id}"
  }

  cloudinit = "${libvirt_cloudinit_disk.commoninit.id}"

  console {
    type = "pty"
    target_type = "serial"
    target_port = "0"
  }

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

# Output Server IP
output "ip" {
  value = "${libvirt_domain.centos7.network_interface.0.addresses.0}"
}

Reinizializzare la directory di lavoro Terraform.

$ terraform init
Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/template...
- Reusing previous version of dmacvicar/libvirt from the dependency lock file
- Installing hashicorp/template v2.2.0...
- Installed hashicorp/template v2.2.0 (signed by HashiCorp)
- Using previously-installed dmacvicar/libvirt v0.7.1

Terraform has made some changes to the provider dependency selections recorded
in the .terraform.lock.hcl file. Review those changes and commit them to your
version control system if they represent changes you intended to make.

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.

Quindi crea la macchina virtuale e le sue risorse utilizzando il comando apply:

terraform plan
terraform apply

Risultato dell'esecuzione:

Prendere nota dell'IP del server stampato sullo schermo.

Oppure usa il comando virsh per ottenere l'indirizzo IP del server.

$ sudo virsh net-dhcp-leases default
 Expiry Time           MAC address         Protocol   IP address           Hostname   Client ID or DUID
---------------------------------------------------------------------------------------------------------
 2019-03-24 16:41:32   52:54:00:22:45:57   ipv4       192.168.122.219/24   -          -

Prova ad accedere all'istanza come utente root e password impostata.

Controlla se l'utente ssh creato può accedere con la chiave SSH ed eseguire sudo senza password.

$ ssh [email 
The authenticity of host '192.168.122.219 (192.168.122.219)' can't be established.
ECDSA key fingerprint is SHA256:G8ByhT4+FXBzh/MabB67rcS6JpTUn1TcrusXhiy8ke0.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.122.219' (ECDSA) to the list of known hosts.

[jmutai@localhost ~]$ sudo su -
Last login: Sun Mar 24 13:16:46 UTC 2019 on tty1
[root@localhost ~]# 

Consulta la documentazione del provider KVM Terraform per l'utilizzo delle risorse e fornisci esempi su come utilizzare il provider.

Fare riferimento alla documentazione di Cloud Init per il suo utilizzo.

  • Distribuisci istanze VM su Hetzner Cloud con Terraform
  • Installa Terraform su Windows 10/Windows Server 2019
  • Installa e configura Hashicorp Vault Server su Ubuntu/CentOS/Debian
  • Come eseguire Minikube su KVM
  • Cheatsheet dei comandi virsh per gestire le macchine virtuali guest KVM

Articoli correlati: