Terraform + Cloud-Init a través de extra_config y DataSourceVMware

Terraform + Cloud-Init a través de extra_config y DataSourceVMware

Pregunta:

¿Alguien ha utilizado con éxito terraform + extra_config + imágenes en la nube de Ubuntu para interactuar con Cloud Init, proporcionando metadatos/datos de usuario? Espero que esto interactúe con DataSourceVMware, pero no puedo estar seguro en este momento.

Lo que he estado haciendo:

Estoy usando Terraform para implementar imágenes en la nube de Ubuntu en VMware vSphere 7. Ha sido bastante fácil usar las propiedades de vApp:

... below code snipped from resource "vsphere_virtual_machine" "vm" { }

vapp {
    properties = {
        hostname = var.vm_Name_Lower
        instance-id = var.vm_Name_Lower
        user-data = base64encode(file("${path.module}/userdata.yml"))
    }
}

Pero cualquier intento de utilizar extra_config ha fracasado. Me gustaría poder proporcionar guestinfo.metadata y guestinfo.userdata. Pero actualmente, mis pruebas con metadatos (que se ven a continuación) parecieron fallar porque el nombre de host no está configurado en la VM:

data "cloudinit_config" "metadata" {
    gzip = true
    base64_encode = true
    part {
        content_type = "text/cloud-config"
        content = <<-EOF
            local-hostname: testvm
            instance-id: testvm
        EOF
    }
}

... below code snipped from resource "vsphere_virtual_machine" "vm" { }

extra_config = {
    "guestinfo.metadata" = data.cloudinit_config.metadata.rendered
    "guestinfo.metadata.encoding" = "gzip+base64"
}

Puedo ver la entrada del registro de vSphere para demostrar que se envió extra_config:

config.extraConfig("guestinfo.metadata"): (key = "guestinfo.metadata", value = "H4sIAAAAAAAA/2SOTUvGMBCE74H8h/De11dPQsSDHz14qIK ... snipped

Referencias:

Detalles de la versión:

Client system (on which terraform is run): Ubuntu 20.04.3 LTS
ESXi: 7.0.2 / Build: 18538813
vCenter Server: 7.0.2 / Build: 18455184
Cloud Image: https://cloud-images.ubuntu.com/impish/current/impish-server-cloudimg-amd64.ova
Terraform v1.0.7
on linux_amd64
provider registry.terraform.io/hashicorp/template v2.2.0
provider registry.terraform.io/hashicorp/vsphere v1.24.3

Respuesta1

El problema es que cloud-init tiene, de forma predeterminada, el proveedor de fuente de datos OVF invocado antes de la nueva fuente de datos VMware (a partir de cloud-init 21.3). Terraform proporciona datos que le gustan al proveedor de fuentes de datos OVF y, por lo tanto, procesa la información. Eso explica por qué los "datos de usuario" de vApp Properties aceptan el archivo cloud-config.

La solución es eliminar el proveedor de fuente de datos OVF de cloud-init:

  1. [Navegador web[ Descargar OVA: https://cloud-images.ubuntu.com/impish/current/impish-server-cloudimg-amd64.ova
  2. [VC UI] Implemente desde OVF, acepte los valores predeterminados (excepto el aprovisionamiento de disco, use Thin Provisioning).
  3. [VC UI] Editar configuración/Opciones de VM/Opciones de arranque/Retraso de arranque = 2000 ms.
  4. [IU de VC] Abra la consola de VM.
  5. [Consola de VM] Encienda la VM.
  6. [Consola VM] Mantenga presionada la tecla Mayús en la pantalla del BIOS (para forzar a GRUB a mostrar el menú).
  7. [Consola VM] Seleccione Opciones avanzadas para Ubuntu.
  8. [Consola VM] Seleccione la última versión del kernel con "(modo de recuperación)" al final.
  9. [Consola VM] Seleccione "raíz / Colocar en el indicador de shell raíz"
  10. [Consola VM] Presione Enter para mantenimiento
  11. [Consola VM] # dpkg-reconfigure cloud-init
  12. [Consola VM] Deseleccione todo excepto VMware y Ninguno
  13. [Consola VM] # limpieza de inicio de nube
  14. [Consola VM] # apagado -h ahora
  15. [VC UI] Editar configuración/Opciones de VM/Opciones de arranque/Retraso de arranque = 0 ms.
  16. [VC UI] Convertir a plantilla

Respuesta2

Haciendo lo mismo con los OVA en la nube de Ubuntu y encontré una pequeña solución para esto. No es ideal, ya que implica un reinicio, lo que significa que necesita un local-execaprovisionador para detectar cuándo finaliza la instancia, pero funciona.

Supongamos que tiene dos archivos yaml que está utilizando para datos de usuario: el primero que está pasando con las propiedades de vApp denominadas vapp-userdata.yamly luego el segundo que está pasando para la fuente de datos de VMware denominadaguest-userdata.yaml

Algo como esto en Terraform

  vapp {
    properties = {
      user-data = base64encode(data.template_file.vapp_userdata[count.index].rendered)
    }
  }

  extra_config = {
    "guestinfo.metadata"          = base64encode(data.template_file.guest-metadata[count.index].rendered)
    "guestinfo.metadata.encoding" = "base64"
    "guestinfo.userdata"          = base64encode(data.template_file.userdata.rendered)
    "guestinfo.userdata.encoding" = "base64"
  }

Tiene vapp-userdata.yamluna write_fileacción que sobrescribe las definiciones de fuentes de datos actuales y las elimina OVFcuando ya ha llegado a ese punto.

write_files:
  - path: /etc/cloud/cloud.cfg.d/90_dpkg.cfg
    owner: root:root
    permissions: "0644"
    content: |
      datasource_list: [ VMware, None ]

Luego, en la parte inferior, termine con un reinicio.

power_state:
  timeout: 600
  mode: reboot

Cuando la máquina virtual se reinicie después de ese último comando, leerá la VMwarefuente de datos como se define en la nueva configuración de inicio de nube y procesará su información guest-userdata.yamly, metadata.yamlsi la ha definido, también.

En cuanto a detectar cuándo está terminado, todavía estoy tratando de encontrar la mejor manera de hacerlo. La manera más fácil es comenzar nc -l 12345al final guest-userdata.yamly tener un aprovisionador local que continúe cuando pueda conectarse tcp/12345, pero eso lo deja con un oyente de netcat abierto tcp/12345que no es ideal.

Si encuentras una mejor manera, responde :)

Editar

Definitivamente habrá una mejor manera de hacer esto, pero...

En el fondo deguest-userdata.yaml

runcmd:
  - mkdir -p /mnt/sharedfolder
  - sysctl -w vm.overcommit_memory=1
  - sysctl -w kernel.panic=10
  - sysctl -w kernel.panic_on_oops=1
  - curl https://releases.rancher.com/install-docker/${docker_version}.sh | sh
  - usermod -aG docker ubuntu
  - nc -l 1234 & ncpid=$!  #start nc and get PID
  - sleep 20
  - kill $ncpid #kill PID once Terraform has had time to connect

Luego como aprovisionador al final de su .tfarchivo

  provisioner "local-exec" {
    # Wait for cloud-init userdata cmds
    # Netcat: z (scan port only), w1 (wait 1 second)
    command = "count=0; until $(nc -zw1 ${self.default_ip_address} 1234); do sleep 1; count=`expr $count + 1`; done"
  }

información relacionada